七大排序(1.插入排序2.希尔排序3.选择排序法4.堆排序5.冒泡排序法6.快速排序法7.归并排序)

目录

1.插入排序

2.希尔排序

3.选择排序法

4.堆排序

5.冒泡排序法

6.快速排序法

7.归并排序


1.插入排序

        设我们有个array[n]数组需要排序,当插入第i(i >=1)个元素的时候,前面i - 1个元素已经排好序,此时,我们用第i个元素与前面的i - 1的元素进行比较,找到要插入的位置,而原来位置上的元素向后移动就可以了。

        比如:当i == 1时,array[i] = 3,我们要将array[i] 和 array[i -1]比较,发现array[i] 比

array[i - 1]小

        那么我们就将array[i]插入到array[i - 1]前,而array[i - 1]往后移一位就可以了。

        

        又比如:当i == 2时,array[i] = 5,我们要将array[i] 和 array[i -1]、 array[i -2] ... array[0]比较,发现array[i] 比array[0]小

        那么我们就将array[i]插入到array[0]前,而array[0]、array[1]、array[2]...array[i - 1]往后移一位就可以了。

 

上代码:

public static void insertSort (int[] array) {
        for (int i = 1; i < array.length; i++) {
            // 因为前1个元素已经有序,所以我们从第2个元素(array[1])开始与第1个元素(array[0])进行比较
            int tmp = array[i];//将需要进行插入的第i个元素进行备份。
            int j = i  -1;
            for (j = i - 1; j >= 0; j--) {//从第i - 1个元素开始,逐步向前比较
                if (array[j] > tmp) {
                    array[j + 1] = array[j];//比要插入的元素大的就往后移动
                } else {
                    //如果不小于了就停止比较,此时j指向的是比要插入的元素小的元素。
                    // 或者在i == 1时,且array[0]大于要插入的元素array[1]时,j会等于 -1;
                    break;
                }
            }
            array[j + 1] = tmp;//找到要插入的位置
        }
    }

 插入排序的特性:

1.元素集合越接近有序,插入排序算法的时间效率越高。元素越接近有序,那么在插入过程中,其余元素向后移位的次数就越少。

2.时间复杂度:O(N^{2})

3.空间复杂度:O(1)。

4,稳定性:稳定

2.希尔排序

        希尔排序可以看成是优化之后的插入排序。利用插入排序元素集合越接近有序,插入排序算法的时间效率越高的特性来进行优化。

        希尔排序法又称缩小增量法。希尔排序的基本思想是:选定一个整数gap,把待排序数组array分成gap个组,将所有距离为gap的元素分在同一组内,并对每一组进行插入排序。然后将gap不断缩小,重复上述的分组和插入排序的工作,当gap == 1时,即所有元素都在同一个组中,再次排好序。

        假设我们有10个元素的数组,我们取gap = 5,这个数组分成5组,对它们分别进行插入排序:

取gap = 2,这个数组分成2组,对它们分别进行插入排序:

最后取gap = 1,这个数组变成1组,对它进行插入排序,就完成啦~~

 上代码:

public static void shellSort(int[] array) {
        int gap = array.length;
        while (gap > 1) {
            gap = (gap / 3) + 1;//+1的目的时为例避免出现gap = 0的情况发生,
                                // 比如当gap = 2的时候,直接除以3就会等于0
            for (int i = gap; i < array.length; i++) {
                //从第gap个元素开始
                int tmp = array[i];
                int j = i - gap;//要和插入的元素距离为gap的元素进行比较,及与同一组的元素进行比较
                for (j = i - gap; j >= 0; j -= gap) {
                    if (array[j] > tmp) {
                        array[j + gap] = array[j];
                    } else {
                        break;
                    }
                }
                array[j + gap] = tmp;
            }
        }
    }

        不难看出,当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

希尔排序的特性:

1.时间复杂度:希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些书中给出的希尔排序的时间复杂度都不固定,我们暂且按照O(N^{1.25})O(1.6 * N^{1.25})来计算。

2.空间复杂度:O(1)

3,稳定性:不稳定

3.选择排序法

        基本思想就是在array[i]到array[n - 1]个元素选择一个最小的元素与下标为i的元素进行交换。

比如i = 0的时候

         从array[0]到array[9]中选一个最小的值,和array[0]交换位置,

        当i = 1时,那就在array[1]到array[9]中选一个最小值,和array[1]交换位置

 

 以此类推~~

上代码:

    public static void swap(int[] array, int x, int y) {
        int tmp = array[x];
        array[x] = array[y];
        array[y] = tmp;
    }

    public static void selectSort (int[] array) {
        for (int i = 0; i < array.length; i++) {
            int j = i + 1;
            //在i + 1及往后的元素寻找比array[i]小的元素,
            // 如果没有,则array[i]就是i到n之间最小的元素
            int mini = i;//记录当前最小的元素的下标
            for (j = i + 1; j < array.length; j++) {
                if (array[j] < array[mini]) {
                    mini = j;
                }
            }
            //此时mini为最小元素的下标,让它与第i个元素互换位置
            swap(array, mini, i);
        }
    }

快速排序的特性:

1.时间复杂度:O(N^{2}) 

2.空间复杂度:O(1)

3.稳定性:不稳定

4.堆排序

堆排序在之前我写的堆的文章中有详细介绍,大家可以康康:

(7条消息) 堆 --- 永不过时的数据结构_如画亦枫的博客-CSDN博客

上代码:

    public static void swap(int[] array, int x, int y) {
        int tmp = array[x];
        array[x] = array[y];
        array[y] = tmp;
    }

    public static void heapSort (int[] array) {
        for (int j = (array.length - 1 - 1) / 2; j >= 0; j--) {
            shiftDown(array, j, array.length);
        }//建堆

        for (int i = array.length - 1; i > 0; i--) {
            shiftDown(array, 0, i);
            swap(array, 0, i);
        }
    }

    public static void shiftDown (int[] array, int parent, int end) {
        int child = parent * 2 + 1;
        while (child < end) {
            if (child + 1 < end && array[child] < array[child + 1]) {
                child++;
            }

            if (array[parent] < array[child]) {
                swap(array, parent, child);
                parent = child;
                child = parent * 2 + 1;
            }
            else {
                break;
            }
        }
    }

堆排序的特性:

1.时间复杂度:O(Nlog_{2}N)

2.空间复杂度:O(1)

3.稳定性:不稳定

5.冒泡排序法

冒泡排序法原理:

① 比较相邻的元素。如果第一个比第二个大,就交换他们两个。

② 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。

③ 针对所有的元素重复以上的步骤,除了最后一个。

④ 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

上代码:

    public static void swap(int[] array, int x, int y) {
        int tmp = array[x];
        array[x] = array[y];
        array[y] = tmp;
    }

    public static void bubbleSort(int[] array) {
        for (int i = 0; i < array.length; i ++) {
            boolean flag = false;
            for (int j = 0; j < array.length - i - 1; j ++) {
                if (array[j] > array[j + 1]) {
                    swap(array, j, j + 1);
                    flag = true;
                }
            }
            
            if (flag == false) {
                break;//flag 还等于false就证明该数组在上述阶段没有进行交换操作,即已经有序,
                //就不用再进行比较了
            }
        }
    }

 冒泡排序法的特征:

1.时间复杂度:O(N^{2})

2.空间复杂度:O(1)

3.稳定性:稳定


6.快速排序法

        基本原理:任取待排序元素序列中的某元 素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有 元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

上代码(挖坑法):

    public static void quickSort(int[] array) {
        quick(array, 0, array.length - 1);
    }

    public static void quick(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        int tmp = array[left];//我们把下标为left的值作为基准值,并进行备份
        int begin = left;
        int end = right;
        while (begin < end) {
            while (begin < end && array[end] > tmp) {
                end--;//注意一定要先从右边找起,找到比基准值tmp小的数
            }
            array[begin] = array[end];//把这个比基准值小的数移到左边
            while (begin < end && array[begin] < tmp) {
                begin ++;//找到比基准值tmp大的数
            }
            array[end] = array[begin];//把这个比基准值小的数移到右边
        }
        array[end] = tmp;//最后再将基准值填回去

        quick(array, left, end - 1);//左边同理
        quick(array, end + 1, right);//右边同理
    }


    //acWing写法
    public static void quickSort(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        int l = left - 1;
        int r = right + 1;
        int tmp = array[left + right >> 1];
        while (l < r) {
            while (array[++ l] < tmp);
            while (array[-- r] > tmp);
            if (l < r) {
                swap(array, l, r);
            }
        }

        quickSort(array, left, r);
        quick(array, r + 1, right);
    }

快速排序的特性:

1.快速排序的两种优化方式:1. 三数取中法选key 2. 递归到小的子区间时,可以考虑使用插入排序。

2.时间复杂度: O(Nlog_{2}N)

3.空间复杂度:O(log_2{N})到O(N) 

4.稳定性:不稳定

7.归并排序

        归并排序采用了分治的思想:

         再来看看治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。

 上代码:

    public static void mergeSort(int[] array) {
        merge(array, 0, array.length - 1);
    }

    public static void merge(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        int mid = left + right >> 1;
        merge(array, left, mid);//分治
        merge(array, mid + 1, right);

        int[] tmp = new int[right - left + 1];
        int begin1 = left;
        int begin2 = mid + 1;
        int index = 0;
        while (begin1 <= mid && begin2 <= right) {
            if (begin1 <= mid && array[begin1] <= array[begin2]) {
                tmp[index ++] = array[begin1 ++];
            }

            if (begin2 <= right && array[begin1] > array[begin2]) {
                tmp[index ++] = array[begin2 ++];
            }
        }

        while (begin1 <= mid) {
            tmp[index ++] = array[begin1 ++];
        }

        while (begin2 <= right) {
            tmp[index ++] = array[begin2 ++];
        }

        for (int i = 0; i < tmp.length; i++) {
            array[i + left] = tmp[i];
        }
    }

归并排序特性:

1.时间复杂度:O(Nlog_{2}N)

2.空间复杂度:O(N)

3.稳定性:稳定

总结:

排序方法平均情况最好情况最坏情况辅助空间稳定性
冒泡排序法O(N^{2})O(n)O(N^{2})O(1)稳定
简单选择排序法O(N^{2})O(N^{2})O(N^{2})O(1)不稳定
直接插入排序法O(N^{2})O(n)O(N^{2})O(1)稳定
希尔排序O(N^{1.25})O(1.6 * N^{1.25})O(N^{1.25})O(N^{2})O(1)不稳定
堆排序O(Nlog_{2}N)O(Nlog_{2}N)O(Nlog_{2}N)O(1)不稳定
归并排序O(Nlog_{2}N)O(Nlog_{2}N)O(Nlog_{2}N)O(N)稳定
快速排序O(Nlog_{2}N)O(Nlog_{2}N)O(N^{2})O(log_2{N})到O(N)不稳定

 

 

 

 

 

 

 

                          

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值