原地排序算法

1、选择排序

  参考算法:每一轮循环都选择出当前范围最大值,然后从右到左开始占位存储

    第一轮循环

      1)从数组0~N中选择出最大,与索引位0的数进行交换。

    第二轮循环

      1)从数组中1~N选择出最大,与索引位1的数进行交换。

      2)……

    第N-1轮循环

      3)从数组N-1~N中选择出最大,与索引位N-1的数进行交换。   

  时间复杂度:

   1)交换操作0~N-1次;最好的情况:已经有序,O(0)。最差的情况每次比较都需要交换O(N-1)

     2)比较操作(N-1)+(N-2)+(N-3)+……+(N+1-N)次【等差数列】O(n^2)

    N*(N-1)/2

      3)不稳定性:个人感觉是指交换操作在值相等的情况下,是不需要操作的。以后有更深的理解后再看。如果当前值已经在排序后的位置上,那么也不需要交换

  示例:

    2 5 3 1 3 8 -> 索引0和6位置交换

        8 5 3 1 3 2 -> 不变

    8 5 3 1 3 2 -> 不变

    8 5 3 3 1 2 -> 索引3和4位置交换

    8 5 3 3 2 1 -> 索引4和5位置交换

  代码示例:

  

     // 从0~N选出一个最小的值放到0索引位置
        // 从1~N选出一个最小的值放到1索引位置
        // ……
        // 第一层循环,N-1次循环
        for (int i = 0; i < arr.length - 1; i++) {
            int min = arr[i];
            int idx = i;
            // 第二次循环N阶减次循环,可以理解为从左开始占位,每次占1位用于存储当前范围内的最小值
            for (int i1 = i + 1; i1 < arr.length; i1++) {
                if (arr[i1] < min) {
                    min = arr[i1];
                    idx = i1;
                }
            }
            if (idx != i) {
                arr[idx] = arr[i];
                arr[i] = min;
            }
        }

 

2、冒泡排序

  参考算法:每一轮循环都不断比较数组中的左右值,大的往右冒,一轮结束,最右侧为当前最大,特点是每次比较都可能包含交换

    第一轮循环

      1)索引位置0和索引位置1比较,左边大于右边,则交换

      2)索引位置1和索引位置2比较,左边大于右边,则交换

      3)……

      4)索引位置N-2和索引位置N-1比较,左边大于右边,则交换

    第二轮循环

      1)索引位置0和索引位置1比较,左边大于右边,则交换

      2)索引位置1和索引位置2比较,左边大于右边,则交换

      3)……

      4)索引位置N-3和索引位置N-2比较,左边大于右边,则交换

      ……

    第N-1轮循环

      1)索引位置0和索引位置1比较,左边大于右边,则交换 

  时间复杂度:

    1)交换操作0~(N-1)+(N-2)+(N-3)+……+1 ->  套用上面的结果 N*(N-1)/2

    2)比较操作(N-1)+(N-2)+(N-3)+……+1      ->  N*(N-1)/2

    3)不稳定性,也是交换上面,有些是不需要交换的

  示例:

    2 5 3 1 3 8

        2 5 3 1 3 8 -> 不变

    2 3 5 1 3 8 -> 索引1和2的位置交换

    2 3 1 5 3 8 -> 索引3和4位置交换

    2 3 1 3 5 8 -> 索引4和5位置交换

    2 3 1 3 5 8 -> 不变

    2 3 1 3 5 8 -> 不变

    2 1 3 3 5 8 -> 索引1和2的位置交换

    2 1 3 3 5 8 -> 不变

    2 1 3 3 5 8 -> 不变

    1 2 3 3 5 8 -> 索引0和1的位置交换

    1 2 3 3 5 8 -> 不变

    ……

    1 2 3 3 5 8 -> 不变

  代码示例:

        // N-1外层循环
        for (int i = 0; i < arr.length - 1; i++) {
            // N-1递减循环,最右侧不断占位
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    int tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                }
            }
        }    

 

3、插入排序(模拟扑克牌的排序原则,抽到一张牌之后找到合适的位置插入)

  参考算法:从第二个数值开始往左比较,如果右值小于左值,则交换。一直判断到最左侧。再开始下一轮循环。则左侧一部分始终保持有序。

    由于索引位置0必为有序,所以不处理

    第一轮循环

      1)从索引1位置开始与索引0位置比较,小于则交换

    第二轮循环

      1)从索引2位置与索引1位置比较,小于则交换

      2)从索引1位置开始与索引0位置比较,小于则交换

    第三轮循环

      1)从索引3位置与索引2位置比较,小于则交换

      2)从索引2位置开始与索引1位置比较,小于则交换

      3)从索引1位置开始与索引0位置比较,小于则交换

    ……

    第N-1轮循环

      1)从索引N-1位置与索引N-2位置比较,小于则交换

      2)从索引N-2位置与索引N-2位置比较,小于则交换

      ……

      N-1)从索引1位置与索引0位置比较,小于则交换

  时间复杂度:

    1)交换操作:0~(0+1+2+3+……+N-1) -> 0~(N*(N-1)/2)

    2)比较操作:N-1~(0+1+2+3+……+N-1) -> (N*(N-1)/2)。当左值始终比右值大的情况下,就不需要继续向左比较了,所以最好的情况下是每一轮循环值需要做一次比较的情况

    3)不稳定性:如果数值正好处于最终排序后的位置,则当前循环可以不必向左比较和交换。此特性在数组已经有部分排好序的情况下,将提升效率

  示例:

    2 5 3 1 3 8

    2 5 3 1 3 8 

    2 3 5 1 3 8

    2 3 1 5 3 8 

    2 1 3 5 3 8 

    1 2 3 5 3 8

    1 2 3 3 5 8

    1 2 3 3 5 8

  代码示例:

        // 从1开始,进行N-1次循环
        for (int i = 1; i < arr.length; i++) {
            // 从当前比较的值索引位开始,向左比较
            for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }    

 

posted @ 2022-02-24 11:47  gabin  阅读(688)  评论(0编辑  收藏  举报