【排序算法】插入排序、快速排序以及归并排序

一、插入排序

插入排序(Insertion sort)是一种简单直观且稳定的排序算法

Image-插入排序.png

算法思维

每次将一个待排序的记录,按其关键字大小插入到前面的已经排好的子表中的适当的位置。直到全部记录插入完成为止。

算法性能

类型

插入排序主要包括:直接插入排序,二分插入排序(又称折半插入排序),链表插入排序,希尔排序(又称缩小增量排序)。

  • 直接插入排序:

【解释】:直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。

直接插入排序的算法思路:
(1) 设置监视哨r[0],将待插入记录的值赋值给r[0];
(2) 设置开始查找的位置j;
(3) 在数组中进行搜索,搜索中将第j个记录后移,直至r[0].key≥r[j].key为止;
(4) 将r[0]插入r[j+1]的位置上。
  • 折半插入排序

【解释】:将直接插入排序中寻找A[i]的插入位置的方法改为采用折半比较,即可得到折半插入排序算法。

算法的基本过程(思路):

(1)计算 0 ~ i-1 的中间点,用 i 索引处的元素与中间值进行比较,如果 i 索引处的元素大,说明要插入的这个元素应该在中间值和刚加入i索引之间,反之,就是在刚开始的位置 到中间值的位置,这样很简单的完成了折半;

(2)在相应的半个范围里面找插入的位置时,不断的用(1)步骤缩小范围,不停的折半,范围依次缩小为 1/2 1/4 1/8 .......快速的确定出第 i 个元素要插在什么地方;

(3)确定位置之后,将整个序列后移,并将元素插入到相应位置。

  • 希尔排序法

希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。

各组内的排序通常采用直接插入法。
由于开始时s的取值较大,每组内记录数较少,所以排序比较快。随着不断增大,每组内的记录数逐步增多,但由于已经按排好序,因此排序速度也比较快。

【Java_Code】语言解法如下:

import java.util.Scanner;

public class InsertionSort{
    public static void main(String[] array){
        int[] array_one = {23,34,567,78,56,423,99,11};
        int[] array_two = insertionSort(array_one);
        int[] array_three = insertionSort_2(array_one);

        printNumbers(array_two);
        printNumbers(array_three);

        // 技能点提升:给自定义的数组【插入排序】;
        System.out.println("请输入您要排序的数组:" +'\n');
        Scanner sc = new Scanner(System.in);
        String inputString = sc.nextLine();
        String stringArray[] = inputString.split(",");
        // 数组类型转换;
        int num[] = new int[stringArray.length];
        for (int i = 0; i < stringArray.length; i++) {
              num[i] = Integer.parseInt(stringArray[i]);
        }
        int[] array_four = insertionSort_2(num);
        printNumbers(array_four);

    }
    private static void printNumbers(int[] input) {
        System.out.println("正在排序中..." +'\n');
        for (int i = 0; i < input.length; i++) {
            System.out.print(input[i] + ", ");
        }
        System.out.println("\n");
    }
    public static int[] insertionSort(int[] array){
        for (int i = 1;i < array.length; i++){
            for(int j = i; j > 0;j--){
                if(array[j] < array[j-1]){
                    int temp = array[j];
                    array[j] = array[j-1];
                    array[j-1] = temp;
                }
            }
        }
        return array;
    }
    // 【算法改进】: 因为insertionSort中每一次位置置换的时候需要定义一个temp,这这多浪费内存啊!
    //所以我的解决思路是,将所有要后移的元素全部都存在一个新开辟的内存空间中,然后再进行比较重排序。只定义了一个temp
    public static int[] insertionSort_2(int[] array){
        for(int i = 1;i < array.length;i++){
            int temp = array[i];
            int k;
            for (k = i;k > 0 && array[k-1] > temp;k--){
                array[k] = array[k-1];
            }
            array[k] = temp;
        }
        return array;
    }

}
Image-插入排序.png

【Python_Code】如下,寥寥几行就实现了。

import random
Range = 100
Length = 5
list = random.sample(range(Range),Length)    #在指定序列中随机获取指定长度片段
print('before sort:',list)
for i in range(1,Length):                   #默认第一个元素已经在有序序列中,从后面元素开始插入    
    for j in range(i,0,-1):                 #逆向遍历比较,交换位置实现插入        
        if list[j] < list[j-1]:            
            list[j],list[j-1] = list[j-1],list[j]
print('after sort:',list)

二、快速排序

算法思想:基于分治的思想,是冒泡排序的改进型。

首先在数组中选择一个基准点。基准点的选取可能会影响快速排序的效率。

算法的思维

然后分别从数组的两端扫描数组,设两个指示标志(low指向起始位置,high指向末尾),首先从后半部分开始,如果发现有元素比该基准点的值小,就交换low和high位置的值。

然后从前半部分开始扫秒,发现有元素大于基准点的值,就交换low和high位置的值,如此往复循环,直到low>=high,然后把基准点的值放到high这个位置。

Image-快速排序.png

算法性能

  • 理想的情况:算法的时间复杂度为 O(nlog2 n)

  • 最坏的情况:排序算法的时间复杂度为 O(n^2)

  • 快速排序的空间复杂度为O(log2n))

【注释】:它是基于关键字比较的内部排序算法中速度最快的,快速排序亦因此而得名。

public class QuickSort {

    private int array[]; //定义一个数组
    private int length;  // 定义一个私有属性

    public void sort(int[] inputArr) {
        if (inputArr == null || inputArr.length == 0) {
            return;
        }
        this.array = inputArr;
        length = inputArr.length;
        quickSort(0, length - 1);
    }
    // 快速排序算法
    private void quickSort(int lowerIndex, int higherIndex) {
        int i = lowerIndex;
        int j = higherIndex;
        
        int pivot = array[lowerIndex+(higherIndex-lowerIndex)/2];
        // 分为两部分
        while (i <= j) {
            while (array[i] < pivot) {
                i++;
            }
            while (array[j] > pivot) {
                j--;
            }
            if (i <= j) {
                exchangeNumbers(i, j);
                //move index to next position on both sides
                i++;
                j--;
            }
        }
        // call quickSort() method recursively
        if (lowerIndex < j)
            quickSort(lowerIndex, j);
        if (i < higherIndex)
            quickSort(i, higherIndex);
    }
    // 元素置换
    private void exchangeNumbers(int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    public static void main(String a[]){
        QuickSort sorter = new QuickSort();
        int[] input = {11,88,9,99,66,77,333,678,345,123,101,100,90,55};
        sorter.sort(input);
        for(int i:input){
            System.out.print(i);
            System.out.print(" ");
        }
    }
}
Image快速排序

三、归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

通过将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

GIF形象表示归时并

算法描述

  • 第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

  • 第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置

  • 第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

算法地位

速度仅次于快速排序,为稳定排序算法。

归并原理.png

【Java_Code】语言解法如下:

public class MergeSort {

    private int[] array;
    private int[] tempMergArr;
    private int length;

    public static void main(String a[]){

        int[] inputArr = {11,88,9,99,66,77,333,678,345,123,101,100,90,55};
        MergeSort mms = new MergeSort();
        mms.sort(inputArr);
        for(int i:inputArr){
            System.out.print(i);
            System.out.print(" ");
        }
    }

    public void sort(int inputArr[]) {
        this.array = inputArr;
        this.length = inputArr.length;
        this.tempMergArr = new int[length];
        doMergeSort(0, length - 1);
    }

    private void doMergeSort(int lowerIndex, int higherIndex) {

        if (lowerIndex < higherIndex) {
            int middle = lowerIndex + (higherIndex - lowerIndex) / 2;
            // Below step sorts the left side of the array
            doMergeSort(lowerIndex, middle);
            // Below step sorts the right side of the array
            doMergeSort(middle + 1, higherIndex);
            // Now merge both sides
            mergeParts(lowerIndex, middle, higherIndex);
        }
    }

    private void mergeParts(int lowerIndex, int middle, int higherIndex) {

        for (int i = lowerIndex; i <= higherIndex; i++) {
            tempMergArr[i] = array[i];
        }
        int i = lowerIndex;
        int j = middle + 1;
        int k = lowerIndex;
        while (i <= middle && j <= higherIndex) {
            if (tempMergArr[i] <= tempMergArr[j]) {
                array[k] = tempMergArr[i];
                i++;
            } else {
                array[k] = tempMergArr[j];
                j++;
            }
            k++;
        }
        while (i <= middle) {
            array[k] = tempMergArr[i];
            k++;
            i++;
        }
    }
}
归并排序

【Python_Code】

def MergeSort(lists):
    if len(lists) <= 1:
        return lists
    num = int( len(lists) / 2 )
    left = MergeSort(lists[:num])
    right = MergeSort(lists[num:])
    return Merge(left, right)
def Merge(left,right):
    r, l=0, 0
    result=[]
    while l<len(left) and r<len(right):
        if left[l] <= right[r]:
            result.append(left[l])
            l += 1
        else:
            result.append(right[r])
            r += 1
    result += list(left[l:])
    result += list(right[r:])
    return result
print MergeSort([1, 2, 3, 4, 5, 6, 7, 90, 21, 23, 45])
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容

  • 简单来说,时间复杂度指的是语句执行次数,空间复杂度指的是算法所占的存储空间 时间复杂度计算时间复杂度的方法: 用常...
    Teci阅读 1,091评论 0 1
  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    蚁前阅读 5,170评论 0 52
  • 本文首发于我的个人博客:尾尾部落 排序算法是最经典的算法知识。因为其实现代码短,应该广,在面试中经常会问到排序算法...
    繁著阅读 4,571评论 3 119
  • 概述 因为健忘,加上对各种排序算法理解不深刻,过段时间面对排序就蒙了。所以决定对我们常见的这几种排序算法进行统一总...
    清风之心阅读 689评论 0 1
  • 排序的基本概念 在计算机程序开发过程中,经常需要一组数据元素(或记录)按某个关键字进行排序,排序完成的序列可用于快...
    Jack921阅读 1,421评论 1 4