[java]-算法与数据结构-第七章-排序算法

七、排序算法

1. 排序算法介绍

排序也称排序算法(Sort Algorithm),排序是将一组数据,依指定的顺序进行排列的过程。

排序的分类

  1. 内部排序:指将需要处理的所有数据都加载到内部存储器中进行排序。
    2. 外部排序法:数据量过大,无法全部加载到内存中,需要借助外部存储(文件)进行排序。
    3. 常见的排序算法分类(见左图)

在这里插入图片描述

2. 算法的时间复杂度

1)计算复杂度

  1. 事后统计法

    这种方法可行,但是有两个问题:

    一是要想对设计的算法的运行性能进行评测,需要实际运行该程序;

    二是所得时间的统计量依赖于计算机的硬件、软件等环境因素,这种方式,要在同一台计算机的相同状态下运行,才能比较那个算法速度更快。

  2. 事前估算的方法

    通过分析某个算法的时间复杂度来判断哪个算法更优

2)时间频度

一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。

一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)

基本案例

a

忽略常数项

在这里插入图片描述

忽略低频次数

在这里插入图片描述

忽略系数

在这里插入图片描述

3)时间复杂度计算

  1. 一般情况下,算法中的基本操作语句的重复执行次数是问题规模n的某个函数,用n表示,

    若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n) / f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。

    记作T(n)=o1( f(n) ),称O( f(n))为算法的渐进时间复杂度,简称时间复杂度。
    1. T(n)不同,但时间复杂度可能相同。如: T(n)=n2+7n+6与T(n)=3n2-+2n+2它

    们的T(n)不同,但时间复杂度相同,都为O(n2)。
    1. 计算时间复杂度的方法:

    • 常数1代替运行时间中的所有加法常数 T(n)= n 2 n^2 n2 + 7n +6 —>T(n)= n 2 n^2 n2 + 7n +1
    • 修改后的运行次数函数中,只保留最高阶项 T(n)= n 2 n^2 n2+7n+1 —> T(n)=n 2 ^2 2
    • 去除最高阶项的系数 T(n)= n 2 n^2 n2 —> T(n)= n 2 n^2 n2 —> O( n 2 n^2 n2)

常见的时间复杂度

  1. 常数阶 O(1)
    2. 对数阶 O( l o g 2 log_2 log2 n n n)
    3. 线性阶 O(n)
    4. 线性对数阶O( n l o g 2 n nlog_2n nlog2n)
    5. 平方阶 O( n 2 n^2 n2)
    6. 立方阶 O( n 3 n^3 n3)
    7. k次方阶 O( n k n^k nk)
    8. 指数阶 O( 2 n 2^n 2n)

说明:
常见的算法时间复杂度由小到大依次为:o(1)<o(logzn)<o(n)<o( n l o g 2 n nlog_2n nlog2n)<o( n 2 n^2 n2)<o( n 3 n^3 n3)<o( n k n^k nk)<o( 2 n 2^n 2n),随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低
从图中可见,我们应该尽可能避免使用指数阶的算法
在这里插入图片描述

常数阶O(1)

无论代码执行了多少行,只要是没有循环等复杂结构,这个代码的时间复杂度就是 O(1)

int i = 1;
int j = 2;
++i;
++j;
int m = i + j;

上述代码在执行的时候,它消耗的时候并不随着某个变量的增长而增长,那幺无论这类代码有多长,即使有几万几十万行,都可以用O(1)来表示它的时同复杂度。

对数阶 O( l o g 2 n log_2n log2n)

int i = 1;
while(i<n){
    
    
  i=  i*2
}

说明:

在while循环里面,每次都将i乘以2,乘完之后,i距离n就越来越近了。假设循环x次之后,i就大于2了,此时这个循环就退出了,也就是说2的x次方等于n,那么x= l o g 2 n log_2n log2n
也就是说当循环 l o g 2 n log_2n log2n次以后,这个代码就结束了。
因此这个代码的时间复杂度为O( l o g 2 n log_2n log2n)。O( l o g 2 n log_2n log2n)的这个2时间上是根据代码变化的,i=i*3,则是O( l o g 3 n log_3n log3n).

线性阶O(n)

for(m = 1; m < n; m++){
    
    
  j = 1;
  j ++ ;
}

说明:

for循环里面的代码会执行 n 遍,因此它消耗的时间是随着n的变化而变化的,因此这类代码都可以用O(n)来表示它的时间复杂度

线性对数阶 O(nlogN)

for(m = 1; m < n; m++){
    
    
  i = 1;
  while (i<n){
    
    
    i = i * 2;
  }
}

说明:

线性对数阶o(nlogN)其实非常容易理解,将时间复杂度为o(logr的代码循环N遍的话,

那么它的时间复杂度就是n * o(logN),也就是了o(nlogN)

平方阶O( n 2 n^2 n2)

for(x = 1; i <= 0; x++){
    
    
  for(i = 1; i<= n; i++){
    
    
    j = i;
    j++;
  }
}

说明:平方阶o(n)就更容易理解了,如果把o(n)的代码再嵌套循环一遍,它的时间复杂度就是o(n2),这段代码其实就是嵌套了2层n循环,它的时间复杂度就是O(nn),
即o( ∗ n 2 ∗ *n^2* n2)如果将其中一层循环的n改成m,那它的时间复杂度就变成了o(m
n)

4)平均时间复杂度和最坏时间复杂度

  1. 平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下,该算法的运行时间。

  2. 最坏情况下的时间复杂度称最坏时间复杂度。一般讨论的时间复杂度均是最坏情况下的时间复杂度。

    这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的界限,这就保证了算法的运行时间不会比最坏情况更长。

  3. 平均时间复杂度和最坏时间复杂度是否一致,和算法有关

    在这里插入图片描述

3. 冒泡排序

1)介绍

冒泡排序(Bubble Sorting)的基本思想是:

通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大
的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。

因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,
因此要在排序过程中设置一个标志flag判断元素是否进行过交换。从而减少不必要的比较。

2)图解

在这里插入图片描述

3)举例图解

在这里插入图片描述

4)代码实现

时间复杂度 O( n 2 n^2 n2)

        int num = 0;
        for (int i = 0; i < arr.length - 1; i++) {
    
    
            for (int j = 0; j < arr.length - 1 - i; j++) {
    
    
                if(arr[j] > arr[j +1]){
    
    
                    num = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = num;
                }
                System.out.println(Arrays.toString(arr));
            }
        }

优化1:加 flag

        int[] arr = {
    
    3, 9, -1, 10, 20};
        int num = 0;
        // 表示是否进行过交换
        boolean flag = false;

        for (int i = 0; i < arr.length - 1; i++) {
    
    
            for (int j = 0; j < arr.length - 1 - i; j++) {
    
    
                if (arr[j] > arr[j + 1]) {
    
    
                    num = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = num;
                    flag = true;
                }
            }
            if (!flag) {
    
    
                // 没有交换
                break;
            } else {
    
    
                flag = false;
            }
            System.out.println(Arrays.toString(arr));
        }

4. 选择排序

1)介绍

选择式排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置达到排序的目的。

2)思想

在这里插入图片描述

3)图解

a

4)举例图解

在这里插入图片描述

5)代码实现

        for (int i = 0; i < arr.length - 1; i++) {
    
    
            int minIndex = i;
            int min = arr[i];
            for (int j = i + 1; j < arr.length; j++) {
    
     // 从 i + 1 开始,前面的排序好了
                if(min > arr[j]){
    
    
                    min = arr[j]; // 重置 min
                    minIndex = j; // 重置 minIndex
                }
            }
            // 交换
            if(minIndex != i){
    
     // 发生了交换
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }

6. 希尔排序

1)简单插入排序存在的问题

在这里插入图片描述

2)介绍

希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。

3)思想

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;

随着增量逐渐减少,每组包含的关键询越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止

4)图解

在这里插入图片描述

5)代码实现

交换插入

    /*
     * @Description //TODO 希尔排序 交换插入
     * @Param [arr]
     * @return int[]
     **/
    public static int[] shellSortSwitch(int[] arr) {
    
    
        // 第一轮
        // 1. 初始增量


        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
    
    


            for (int i = gap; i < arr.length; i++) {
    
    
                // 2. 分组
                for (int j = i - gap; j >= 0; j -= gap) {
    
    
                    // 3. 交换元素
                    if (arr[j] > arr[j + gap]) {
    
    
                        int tem = arr[j];
                        arr[j] = arr[j + gap];
                        arr[j + gap] = tem;
                    }

                }
            }
        }
        return arr;

    }

移动插入
控制了一部分不必要的循环

    /*
     * @Description //TODO 希尔排序 移动插入
     * @Param [arr]
     * @return int[]
     **/
    public static int[] shellSortMove(int[] arr) {
    
    
        // 第一轮
        // 1. 初始增量
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
    
    
            // 1. 从 第 gap 个元素,逐个对其所在的组直接进行插入排序
            for (int i = gap; i < arr.length; i++) {
    
    
                int j = i;
                int temp = arr[j];
                if (arr[j] < arr[j - gap]) {
    
    
                    while (j - gap >= 0 && temp < arr[j - gap]) {
    
    
                        // 移动
                        arr[j] = arr[j - gap];
                        j -= gap;
                    }
                    // 退出循环,找到位置
                    arr[j] = temp;

                }
            }
        }
        return arr;

    }

7. 快速排序

1)介绍

快速排序(Quicksort〉是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,

其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

2)图解

在这里插入图片描述

3)代码实现

    /*
     * @Description //TODO 快速排序
     * @Param [arr, l, r]
     * @return void
    **/
    public static void quickSort(int[] arr, int l, int r) {
    
    
        int left = l;
        int right = r;
        // 中间值
        int pivot = arr[(left + right) / 2];
        int temp = 0;
        // 让 比 pivot 值小的放到 左边 , 大的放到 右边
        while (left < right) {
    
     // 剩一个不循环
            // 左边一直找,
            while (arr[left] < pivot) {
    
     // 最左边值 小于 中间值
                left += 1;
            }
            // 右边一直找
            while (arr[right] > pivot) {
    
    
                right -= 1;
            }
            // 说明 左右两边的值 已经 按照 左边全部是 < pivot的,右边都是 >= pivot的
            if (left >= right) {
    
    
                break;
            }
            // 交换
            temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;

            // 如果交换完毕,发现 arr[left] == pivot值, right-- 前移一步
            if (arr[left] == pivot) {
    
    
                System.out.println(pivot);

                right -= 1;
            }
            // 如果交换完毕,发现 arr[right] == pivot值, left++ 前移一步
            if (arr[right] == pivot) {
    
    
                System.out.println(pivot);
                left += 1;
            }
        }
        // 如果 left == right 必须 left ++ ,right--,否则视为栈溢出
        if (left == right) {
    
    
            left += 1;
            right -= 1;
        }
        // 向左递归
        if (left < r) {
    
    
            quickSort(arr, l, right);
        }
        // 向右递归
        if (r > left) {
    
    
            quickSort(arr, left, r);
        }
    }

七、排序算法

1. 排序算法介绍

排序也称排序算法(Sort Algorithm),排序是将一组数据,依指定的顺序进行排列的过程。

排序的分类

1. 内部排序:指将需要处理的所有数据都加载到内部存储器中进行排序。
2. 外部排序法:数据量过大,无法全部加载到内存中,需要借助外部存储(文件)进行排序。
3. 常见的排序算法分类(见左图)

![](https://secure2.wostatic.cn/static/m5FnnHGvctATehhpXuzj8K/image.png)

2. 算法的时间复杂度

1)计算复杂度

  1. 事后统计法

    这种方法可行,但是有两个问题:

    一是要想对设计的算法的运行性能进行评测,需要实际运行该程序;

    二是所得时间的统计量依赖于计算机的硬件、软件等环境因素,这种方式,要在同一台计算机的相同状态下运行,才能比较那个算法速度更快。

  2. 事前估算的方法

    通过分析某个算法的时间复杂度来判断哪个算法更优

2)时间频度

一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。

一个算法中的语句执行次数称为语句频度或时间频度。记为`T(n)`。

#### 基本案例

  ![](https://secure2.wostatic.cn/static/7PGGCYmAdCC12DSJcbeB6k/image.png)

#### 忽略常数项

  ![](https://secure2.wostatic.cn/static/ckK19LLjA31pAuKSKRaA5K/image.png)

#### 忽略低频次数

  ![](https://secure2.wostatic.cn/static/22wexPbe5EAzeZSGKEBAcP/image.png)

#### 忽略系数

  ![](https://secure2.wostatic.cn/static/c3tc61gnLQoXR73zhwJAkx/image.png)

3)时间复杂度计算

1. 一般情况下,算法中的基本操作语句的重复执行次数是问题规模n的某个函数,用n表示,

    若有某个辅助函数f(n),使得当n趋近于无穷大时,`T(n) / f(n)`的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。

    记作T(n)=o1( f(n) ),称`O( f(n))`为算法的渐进时间复杂度,简称时间复杂度。
1. T(n)不同,但时间复杂度可能相同。如: T(n)=n2+7n+6与T(n)=3n2-+2n+2它

    们的T(n)不同,但时间复杂度相同,都为O(n2)。
1. 计算时间复杂度的方法:
    - 常数1代替运行时间中的所有加法常数 T(n)=$n^2$ + 7n +6 —>T(n)=$n^2$ + 7n +1
    - 修改后的运行次数函数中,只保留最高阶项 T(n)=$n^2$+7n+1 —> T(n)=n$^2$
    - 去除最高阶项的系数 T(n)=$n^2$ —> T(n)=$n^2$ —> O($n^2$)

#### 常见的时间复杂度

1. 常数阶 O(1)
2. 对数阶 O($log_2$$n$)
3. 线性阶 O(n)
4. 线性对数阶O($nlog_2n$)
5. 平方阶 O($n^2$)
6. 立方阶 O($n^3$)
7. k次方阶 O($n^k$)
8. 指数阶 O($2^n$)

说明:

常见的算法时间复杂度由小到大依次为:o(1)<o(logzn)<o(n)<o( n l o g 2 n nlog_2n nlog2n)<o( n 2 n^2 n2)<o( n 3 n^3 n3)<o( n k n^k nk)<o( 2 n 2^n 2n),随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低

从图中可见,我们应该尽可能避免使用指数阶的算法

![](https://secure2.wostatic.cn/static/x2Tz8Sbcd7z1noq9dSxxf3/image.png)



#### 常数阶O(1)

无论代码执行了多少行,只要是没有循环等复杂结构,这个代码的时间复杂度就是 O(1)

![](https://secure2.wostatic.cn/static/oXwP427b43ZbscyvXoyPMG/image.png)

上述代码在执行的时候,它消耗的时候并不随着某个变量的增长而增长,那幺无论这类代码有多长,即使有几万几十万行,都可以用O(1)来表示它的时同复杂度。



#### 对数阶 O($log_2n$)
int i = 1;
while(i<n){
  i=  i*2
}
说明:

在while循环里面,每次都将i乘以2,乘完之后,i距离n就越来越近了。假设循环x次之后,i就大于2了,此时这个循环就退出了,也就是说2的x次方等于n,那么x=$log_2n$

也就是说当循环$log_2n$次以后,这个代码就结束了。

因此这个代码的时间复杂度为O($log_2n$)。O($log_2n$)的这个2时间上是根据代码变化的,i=i*3,则是O($log_3n$).



#### 线性阶O(n)
for(m = 1; m < n; m++){
  j = 1;
  j ++ ;
}
说明:

for循环里面的代码会执行 n 遍,因此它消耗的时间是随着n的变化而变化的,因此这类代码都可以用O(n)来表示它的时间复杂度



#### 线性对数阶 O(nlogN)
for(m = 1; m < n; m++){
  i = 1;
  while (i<n){
    i = i * 2;
  }
}
说明:

线性对数阶o(nlogN)其实非常容易理解,将时间复杂度为o(logr的代码循环N遍的话,

那么它的时间复杂度就是n * o(logN),也就是了o(nlogN)



#### 平方阶O($n^2$)
for(x = 1; i <= 0; x++){
  for(i = 1; i<= n; i++){
    j = i;
    j++;
  }
}
说明:平方阶o(n)就更容易理解了,如果把o(n)的代码再嵌套循环一遍,它的时间复杂度就是o(n2),这段代码其实就是嵌套了2层n循环,它的时间复杂度就是O(n**n),

即o( ∗ n 2 ∗ *n^2* n2)如果将其中一层循环的n改成m,那它的时间复杂度就变成了o(m**n)

4)平均时间复杂度和最坏时间复杂度

  1. 平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下,该算法的运行时间。

  2. 最坏情况下的时间复杂度称最坏时间复杂度。一般讨论的时间复杂度均是最坏情况下的时间复杂度。

    这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的界限,这就保证了算法的运行时间不会比最坏情况更长。

  3. 平均时间复杂度和最坏时间复杂度是否一致,和算法有关

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BUkpHFjh-1650541283247)(https://secure2.wostatic.cn/static/sJCfgnmv48x4zEhGPY5BSU/image.png)]

3. 冒泡排序

1)介绍

冒泡排序(Bubble Sorting)的基本思想是:

通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大

的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。

因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,

因此要在排序过程中设置一个标志flag判断元素是否进行过交换。从而减少不必要的比较。

2)图解

![](https://secure2.wostatic.cn/static/qncpH1ns24zFEQ34sKxRjv/image.png)

3)举例图解

![](https://secure2.wostatic.cn/static/tyZ99XUeCoJW15s9SrEvqn/image.png)

4)代码实现

时间复杂度 O($n^2$)
        int num = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if(arr[j] > arr[j +1]){
                    num = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = num;
                }
                System.out.println(Arrays.toString(arr));
            }
        }
优化1:加 flag
        int[] arr = {3, 9, -1, 10, 20};
        int num = 0;
        // 表示是否进行过交换
        boolean flag = false;

        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    num = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = num;
                    flag = true;
                }
            }
            if (!flag) {
                // 没有交换
                break;
            } else {
                flag = false;
            }
            System.out.println(Arrays.toString(arr));
        }

4. 选择排序

1)介绍

选择式排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置达到排序的目的。

2)思想

![](https://secure2.wostatic.cn/static/gqxHvY7MwJT7Nwp5xYprmC/image.png)

3)图解

![](https://secure2.wostatic.cn/static/cyJbpHsLxYuL8gShaLGaq8/image.png)

4)举例图解

![](https://secure2.wostatic.cn/static/4iCKeLhmUog9VVzNFtMPNx/image.png)

5)代码实现

        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;
            int min = arr[i];
            for (int j = i + 1; j < arr.length; j++) { // 从 i + 1 开始,前面的排序好了
                if(min > arr[j]){
                    min = arr[j]; // 重置 min
                    minIndex = j; // 重置 minIndex
                }
            }
            // 交换
            if(minIndex != i){ // 发生了交换
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }

5. 插入排序

1)介绍

插入式排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的。

2)思想

插入排序(Insertion Sorting)的基本思想是:

把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,

排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

3)图解

![](https://secure2.wostatic.cn/static/kAKKDoNs48KdYwquMuh4wQ/image.png)

4)代码实现

        int insertVal = 0;
        int insertIndex = 0;
  // 第一论 {9,3 -1, 10, 20} => {3,9 -1, 10, 20}
        for (int i = 1; i < arr.length; i++) {

            // 定义待插入数
            insertVal = arr[i];
            insertIndex = i - 1;  // 即 arr[1] 前一个数的下标

            // 给insertVal找到一个插入的位置
            // 1. insertIndex1 >=0 保证在给 insertVal 找插入位置,不越界
            // 2. insertVal < arr[insertIndex] 待插入的数,没找到插入位置
            // 3. 就需要将 insertIndex 后移
            while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
                arr[insertIndex + 1] = arr[insertIndex];
                insertIndex--;
            }
            if(insertIndex + 1 != i){
                break;
            }
            arr[insertIndex + 1] = insertVal;
        }

6. 希尔排序

1)简单插入排序存在的问题

![](https://secure2.wostatic.cn/static/vnxfzp5h3Bn3Zr1kga7Hwf/image.png)

2)介绍

希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。

3)思想

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;

随着增量逐渐减少,每组包含的关键询越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止

4)图解

![](https://secure2.wostatic.cn/static/88kVUrS3c46GFJq5WUJbcg/image.png)

![](https://secure2.wostatic.cn/static/k6pPg38kGBBpLXUAtENb2E/image.png)

5)代码实现

> 交换插入
    /*
     * @Description //TODO 希尔排序 交换插入
     * @Param [arr]
     * @return int[]
     **/
    public static int[] shellSortSwitch(int[] arr) {
        // 第一轮
        // 1. 初始增量


        for (int gap = arr.length / 2; gap > 0; gap /= 2) {


            for (int i = gap; i < arr.length; i++) {
                // 2. 分组
                for (int j = i - gap; j >= 0; j -= gap) {
                    // 3. 交换元素
                    if (arr[j] > arr[j + gap]) {
                        int tem = arr[j];
                        arr[j] = arr[j + gap];
                        arr[j + gap] = tem;
                    }

                }
            }
        }
        return arr;

    }
> 移动插入

控制了一部分不必要的循环

    /*
     * @Description //TODO 希尔排序 移动插入
     * @Param [arr]
     * @return int[]
     **/
    public static int[] shellSortMove(int[] arr) {
        // 第一轮
        // 1. 初始增量
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            // 1. 从 第 gap 个元素,逐个对其所在的组直接进行插入排序
            for (int i = gap; i < arr.length; i++) {
                int j = i;
                int temp = arr[j];
                if (arr[j] < arr[j - gap]) {
                    while (j - gap >= 0 && temp < arr[j - gap]) {
                        // 移动
                        arr[j] = arr[j - gap];
                        j -= gap;
                    }
                    // 退出循环,找到位置
                    arr[j] = temp;

                }
            }
        }
        return arr;

    }

7. 快速排序

1)介绍

快速排序(Quicksort〉是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,

其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

2)图解

![](https://secure2.wostatic.cn/static/gHLzQwQhapKEyNSpjStmU9/image.png)

3)代码实现

    /*
     * @Description //TODO 快速排序
     * @Param [arr, l, r]
     * @return void
    **/
    public static void quickSort(int[] arr, int l, int r) {
        int left = l;
        int right = r;
        // 中间值
        int pivot = arr[(left + right) / 2];
        int temp = 0;
        // 让 比 pivot 值小的放到 左边 , 大的放到 右边
        while (left < right) { // 剩一个不循环
            // 左边一直找,
            while (arr[left] < pivot) { // 最左边值 小于 中间值
                left += 1;
            }
            // 右边一直找
            while (arr[right] > pivot) {
                right -= 1;
            }
            // 说明 左右两边的值 已经 按照 左边全部是 < pivot的,右边都是 >= pivot的
            if (left >= right) {
                break;
            }
            // 交换
            temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;

            // 如果交换完毕,发现 arr[left] == pivot值, right-- 前移一步
            if (arr[left] == pivot) {
                System.out.println(pivot);

                right -= 1;
            }
            // 如果交换完毕,发现 arr[right] == pivot值, left++ 前移一步
            if (arr[right] == pivot) {
                System.out.println(pivot);
                left += 1;
            }
        }
        // 如果 left == right 必须 left ++ ,right--,否则视为栈溢出
        if (left == right) {
            left += 1;
            right -= 1;
        }
        // 向左递归
        if (left < r) {
            quickSort(arr, l, right);
        }
        // 向右递归
        if (r > left) {
            quickSort(arr, left, r);
        }
    }

8. 归并排序

1)介绍

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer〉策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

2)思想

在这里插入图片描述

3)代码

  • 代码
    /*
     * @Description //TODO 归并排序
     * @Param [arr]
     * @return void
     **/
    public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
    
    

        int i = left; // 初始化 I, 左边有序序列的初始索引
        int j = mid + 1; // 初始化 j , 右边有序序列的初始索引
        int t = 0; // 临时数组的初始索引

        // 1. 先把左右两边的数据按照规则填充到 temp 数组
        // 直到左右两边的有序序列,有一边处理完毕为止
        while (i <= mid && j <= right) {
    
     // 不越界
            // left <= right  添加到 temp
            if (arr[i] <= arr[j]) {
    
    
                temp[t] = arr[i];
                t++;
                i++;
            } else {
    
    
                temp[t] = arr[j];
                t++;
                j++;
            }
        }

        // 2. 把有剩余数据的一遍的数据依次全部填充到 temp
        while (i <= mid) {
    
     // 左边有剩
            temp[t] = arr[i];
            t++;
            i++;
        }
        while (j <= right) {
    
     // 右边有剩
            temp[t] = arr[j];
            t++;
            j++;
        }

        // 3. 将 temp 的元素拷贝到 arr
        t = 0;
        int tempLeft = left;
        while (tempLeft <=right) {
    
    
            arr[tempLeft] = temp[t];
            tempLeft++;
            t++;
        }
    }

    public static void mergeSort(int[] arr,int left,int right,int[] temp){
    
    
        if(left < right){
    
    
            int mid = (left + right) / 2;
            //向左递归进行分解
            mergeSort(arr,left,mid,temp);
            // 向右递归
            mergeSort(arr,mid+1,right,temp);
            // 合并
            merge(arr,left,mid,right,temp);
        }
    }

猜你喜欢

转载自blog.csdn.net/m0_56186460/article/details/124225190