算法复杂程度表

image.png

原文链接:

一份清晰又全面的排序算法攻略_程序员小灰的博客-CSDN博客

1)冒泡排序

1.1)冒泡排序思想:

冒泡排序无疑是最为出名的排序算法之一,从序列的一端开始往另一端冒泡(你可以从左往右冒泡,也可以从右往左冒泡,看心情),依次比较相邻的两个数的大小(到底是比大还是比小也看你心情)。

以 [ 8,2,5,9,7 ] 这组数字来做示例,上图来战:

从左往右依次冒泡,将小的往右移动
image.png
冒泡排序1
首先比较第一个数和第二个数的大小,我们发现 2 比 8 要小,那么保持原位,不做改动。位置还是 8,2,5,9,7 。
指针往右移动一格,接着比较:
image.png
冒泡排序2
比较第二个数和第三个数的大小,发现 2 比 5 要小,所以位置交换,交换后数组更新为:[ 8,5,2,9,7 ]。
指针再往右移动一格,继续比较:
image.png
冒泡排序3
比较第三个数和第四个数的大小,发现 2 比 9 要小,所以位置交换,交换后数组更新为:[ 8,5,9,2,7 ]
同样,指针再往右移动,继续比较:
image.png
冒泡排序4
比较第 4 个数和第 5 个数的大小,发现 2 比 7 要小,所以位置交换,交换后数组更新为:[ 8,5,9,7,2 ]
下一步,指针再往右移动,发现已经到底了,则本轮冒泡结束,处于最右边的 2 就是已经排好序的数字。
通过这一轮不断的对比交换,数组中最小的数字移动到了最右边。
接下来继续第二轮冒泡:
image.png
冒泡排序5
image.png
冒泡排序6
image.png
冒泡排序7
由于右边的 2 已经是排好序的数字,就不再参与比较,所以本轮冒泡结束,本轮冒泡最终冒到顶部的数字 5 也归于有序序列中,现在数组已经变化成了[ 8,9,7,5,2 ]。
image.png
冒泡排序8
让我们开始第三轮冒泡吧!
image.png
冒泡排序9
image.png
冒泡排序10
由于 8 比 7 大,所以位置不变,此时第三轮冒泡也已经结束,第三轮冒泡的最后结果是[ 9,8,7,5,2 ]
紧接着第四轮冒泡:
image.png
冒泡排序11
9 和 8 比,位置不变,即确定了 8 进入有序序列,那么最后只剩下一个数字 9 ,放在末尾,自此排序结束。

1.2) 时间复杂度:

冒泡的代码还是相当简单的,两层循环,外层控制冒泡轮数,里层依次比较和交换位置,江湖中人人尽皆知。
我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为O(n2)。

1.3)冒泡排序代码:

冒泡的代码还是相当简单的,两层循环,外层冒泡轮数,里层依次比较和交换位置,江湖中人人尽皆知。

  1. /**
  2. * 冒泡排序
  3. * 1,每一个元素和它右边的元素进行比较,
  4. * 如果比右边的元素大,则交换位置,然后该元素继续和右边的元素进行比较;
  5. * 如果比右边的元素小,则换成右边的该较大的元素继续往右比较;
  6. * 2,这样,每一轮冒泡之后--(元素的比较和位置交换),当前轮中最大的元素都一定在最右边的位置了;
  7. * @param arr
  8. */
  9. public static void maopao(int[] arr) {
  10. //1,遍历数组:
  11. //i是外循环控制轮数:
  12. for (int i = 0; i < arr.length; i++) {
  13. //j循环里边进行元素的比较和位置交换
  14. //因为是j和j+1进行比较,所以 j < arr.length-1
  15. //因为每一轮冒泡之后--(元素的比较和位置交换),最大、第二大、第三大...的元素都一定在最右边的位置了, 所以那些右边冒过泡的元素都不需要再次进行比较了,所以j < arr.length-1-i
  16. for (int j = 0; j < arr.length-1-i; j++) {
  17. //如果比后边的元素大,则交换位置,然后该元素继续和后边的元素进行比较;
  18. if (arr[j] > arr[j+1]) {
  19. int temp = arr[j];
  20. arr[j] = arr[j+1];
  21. arr[j+1] = temp;
  22. continue;
  23. }
  24. //如果比后边的元素小,则换成后边的该较大的元素继续往后比较;
  25. else{
  26. continue;
  27. }
  28. }
  29. }
  30. }

1.4)冒泡排序优化:


2)选择排序:

2.1)选择排序思想:

选择排序的思路是这样的:

首先,找到数组中最小的元素,拎出来,将它和数组的第一个元素交换位置
第二步,在剩下的元素中继续寻找最小的元素,拎出来,和数组的第二个元素交换位置,

如此循环,直到整个数组排序完成。

至于选大还是选小,这个都无所谓,你也可以每次选择最大的拎出来排,也可以每次选择最小的拎出来的排,只要你的排序的手段是这种方式,都叫选择排序。

图解:

我们还是以[ 8,2,5,9,7 ]这组数字做例子。

第一次选择,先找到数组中最小的数字 2 ,然后和第一个数字交换位置。(如果第一个数字就是最小值,那么自己和自己交换位置,也可以不做处理,就是一个 if 的事情)
image.png
选择排序1
第二次选择,由于数组第一个位置已经是有序的,所以只需要查找剩余位置,找到其中最小的数字5,然后和数组第二个位置的元素交换。
image.png
选择排序2
第三次选择,找到最小值 7 ,和第三个位置的元素交换位置。
image.png
选择排序3
第四次选择,找到最小值8,和第四个位置的元素交换位置。
image.png
选择排序4
最后一个到达了数组末尾,没有可对比的元素,结束选择。
如此整个数组就排序完成了。


2.2)选择排序:(自己理解)

(以从小到大为例)

总结:

一直选择“第i位置”为当前数组中最小的元素:
与后边的元素进行比较,小于则继续遍历,大于则进行元素的交换;

比如:
选择第一个位置,第一个位置的元素成为最小元素之后,
再选择第二个位置,第二个位置的元素成为除第一个位置所在元素之外最小的数之后,再选择第三个数…

步骤:

从i=0开始,让数组中的第i位置的元素,依次与它后面的那些元素们进行比较,

  1. 如果第一个位置的元素小于等于后面的元素,则继续往后遍历;
  2. 如果第一个位置的元素大于后面的元素,则进行位置的交换。
  3. 【交换之后,此时第一个位置所在元素成为较小元素,然后继续和后面的元素进行比较,重复步骤1和步骤2】

这样多轮内循环结束之后,数组中第一个位置就一定是最小的元素了;
之后,i++,让数组中的第i位置的元素,再依次与它后面的那些元素们进行比较,内循环之后,数组中第二个位置的元素就选出来了;
……
不理解的看看图应该就差不多了:
image.png

2.3)选择排序的时间复杂度

双层循环,时间复杂度和冒泡一模一样,都是O(n2)。

2.4)选择排序代码:

/**
* 选择排序:(外层控制 选择的位置,内层进行 该位置所在元素 与 其他的元素 进行比较与交换)
* 1,选出当前数组中最小的数在第一个位置;
* 2,除了第一个位置之外,选择当前数组中最小的数在第二个位置;
* ...
* 3,最终,完成排序;
*
* @param arr
*/
public static void xuanzepaixu(int[] arr) {
    //遍历数组:
    //i < arr.length-1; 最后一个元素不需要去进行选择排序了;
    for (int i = 0; i < arr.length-1; i++) {
          //选择第i位置为当前数组中最小的元素;
          for (int j = i + 1; j < arr.length; j++) {
                //如果arr[i]>arr[j],则进行位置交换,然后第i位置的元素继续和后边的元素进行比较
                if (arr[i] > arr[j]) {
                      int temp = arr[i];
                      arr[i] = arr[j];
                      arr[j] = temp;
                }
                //如果arr[i]<=arr[j],说明第i位置的元素是较小的,则继续往后进行比较;
                else{
                      continue;
                }
          }
    }
}

3)插入排序:

3.1)插入排序思想:

插入排序的思想和我们打扑克摸牌的时候一样,从牌堆里一张一张摸起来的牌都是乱序的,我们会把摸起来的牌插入到左手中合适的位置,让左手中的牌时刻保持一个有序的状态。
那如果我们不是从牌堆里摸牌,而是左手里面初始化就是一堆乱牌呢?
一样的道理,我们把牌往手的右边挪一挪,把手的左边空出一点位置来,然后在乱牌中抽一张出来,插入到左边,再抽一张出来,插入到左边,再抽一张,插入到左边,每次插入都插入到左边合适的位置,时刻保持左边的牌是有序的,直到右边的牌抽完,则排序完毕。
数组初始化:[ 8,2,5,9,7 ],我们把数组中的数据分成两个区域,已排序区域和未排序区域,初始化的时候所有的数据都处在未排序区域中,已排序区域是空。
image.png
插入排序1
第一轮,从未排序区域中随机拿出一个数字,既然是随机,那么我们就获取第一个,然后插入到已排序区域中,已排序区域是空,那么就不做比较,默认自身已经是有序的了。(当然了,第一轮在代码中是可以省略的,从下标为1的元素开始即可)
image.png
插入排序2
第二轮,继续从未排序区域中拿出一个数,插入到已排序区域中,这个时候要遍历已排序区域中的数字挨个做比较,
比大比小取决于你是想升序排还是想倒序排,这里排升序:
image.png
插入排序3
第三轮,排 5 :
image.png
插入排序4
第四轮,排 9 :
image.png
插入排序5
第五轮,排 7
image.png
插入排序6
排序结束。

3.2)插入排序时间复杂度:

从代码里我们可以看出,如果找到了合适的位置,就不会再进行比较了,就好比牌堆里抽出的一张牌本身就比我手里的牌都小,那么我只需要直接放在末尾就行了,不用一个一个去移动数据腾出位置插入到中间。
所以说,最好情况的时间复杂度是 O(n),最坏情况的时间复杂度是 O(排序算法总结 - 图24)
然而时间复杂度这个指标看的是最坏的情况,而不是最好的情况,所以插入排序的时间复杂度是 O(排序算法总结 - 图25)。

3.3)插入排序代码:

插入排序:(从小到大排列)

将数组分为两个部分,一个部分是排好序的,一个部分是未排序的,不断的将未排序的元素插入到排好序的那一部分中,最终完成排序;

  1. 第一位元素不用插入,从第二位开始-往左遍历和插入;
  2. 以i为边界,i以及i左边是已排好序的部分i右边的是未排序的部分
  3. 将待插入元素插入到已排好序的部分—从右往左遍历(方便大于待插入元素的元素的右移)

3.3.1,如果待插入元素小于当前遍历元素,则:当前遍历元素右移一位,待插入元素暂时插入到当前遍历元素的原位置,然后插入元素继续往左遍历;

3.3.2,如果待插入元素大于当前遍历元素,则此时正好当前遍历元素的右边紧邻位置就是待插入元素,则此轮插入完成

/**
* 插入排序:(从小到大排序)
* 1,第一位元素不用插入,从第二位开始-往左遍历和插入;
* 2,以i为边界,i以及i左边是已排好序的部分,i右边的是未排序的部分;
* 3,将待插入元素插入到已排好序的部分--从右往左遍历--(方便大于待插入元素的元素的右移)
*     3.1,如果待插入元素小于当前遍历元素,则当前遍历元素右移一位,待插入元素暂时插入到当前遍历元素的原位置,然后插入元素继续往左遍历;
*     3.2,如果待插入元素大于当前遍历元素,则此时正好当前遍历元素的右边紧邻位置就是待插入元素,则此轮插入完成;
*
* @param arr
*/
public static void charuPaiXu2(int[] arr) {
    //i < arr.length - 1是因为代码中有 arr[i + 1],且i为最后一个元素时就说明已经排好序了;
    for (int i = 0; i < arr.length - 1; i++) {
          //将待插入元素复制出来一份,防止被覆盖
          int daicharuEle = arr[i + 1];
          //从右往左遍历比较已排好序的部分:
          for (int j = i; j >= 0; j--) {
                //如果待插入元素小于当前元素:
                if (daicharuEle < arr[j]) {
                      //将当前元素右移,将待插入元素暂时插入到当前元素的原位置:
                      arr[j + 1] = arr[j];
                      arr[j] = daicharuEle;
                      continue;
                }
                //如果待插入元素大于当前元素,此时待插入元素正好是在当前元素的右边紧邻位置,本轮插入完成:
                else {
                      break;
                }
          }
    }
}

4)希尔排序:

4.1)希尔排序思想:

希尔排序算法是插入排序算法的一种,又叫作缩小增量排序算法,是插入排序算法的一种更高效的改进版本,也是非稳定排序算法。

希尔排序算法将数组按下标的一定增量进行分组,对每个子数组使用插入排序算法排序

示意图:
image.png

希尔排序算法的具体做法:

假设待排序元素序列有N个元素,则先取一个小于N的整数增量值increment作为间隔,比如说increment=arr.length/2,将全部元素分为increment个子序列,

将所有距离为increment的元素都放在同一个子序列中,在每一个子序列中分别实行直接插入排序;

然后缩小间隔increment,重复上述子序列的划分和排序工作,

直到最后取increment=1,将所有元素都放在同一个子序列中时,执行最后一次插入排序终止。

·

从直观上看希尔排序:

  • 就是把数列进行分组(不停使用插入排序),直至从宏观上看起来有序,最后插入排序起来就容易了(无须多次移位或交换)。

那么,上面那里说了将一个序列分成好几个序列,那么到底怎么分呢?比如有10个元素的序列,分成几个才合适?每次缩减又是多少呢?

从专业的角度上讲,将一个序列分成好几个序列,用一个数来表示:那个数称为增量。显然的是,增量是不断递减的(直到增量为1)

往往的:
如果一个数列有10个元素,我们第一趟的增量是5,第二趟的增量是2,第三趟的增量是1。
如果一个数列有18个元素,我们第一趟的增量是9,第二趟的增量是4,第三趟的增量是2,第四趟的增量是1
很明显我们可以用一个序列来表示增量:{n/2,(n/2)/2…1},每次增量都/2;

举例:

现在我们有一个数组,该数组有6个元素
int[] arrays = {2, 5, 1, 3, 4, 6};

排序前:

  • 将该数组看成三个(arrays.length/2)数组,分别是: {2,3},{5,4},{1,6}

第一趟排序:

  • 对三个数组分别进行插入排序,因此我们三个数组得到的结果为{2,3},{4,5},{1,6} ;
  • 此时数组是这样子的:{2, 4, 1, 3, 5, 6}

第二趟排序:

  • 增量减少了,上面增量是3,此时增量应该为1了,因此把{2, 4, 1, 3, 5, 6}看成一个数组(从宏观上是有序的了),对其进行插入排序,直至有序。

最后:

我们发现希尔排序代码其实非常简单(相比对堆排序),理解起来也不难,就用增量来将数组进行分隔,直到增量为1。底层干的还是插入排序干的活~

希尔排序的增量选择:

在此我们选择增量gap=length/2,缩小增量继续以gap = gap/2的方式,
这种增量选择我们可以用一个序列来表示,{n/2,(n/2)/2…1},称为增量序列

希尔排序的增量序列的选择与证明是个数学难题,我们选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量。

希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;

当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。

希尔排序的特点:

  1. Shell排序比冒泡排序快5倍比插入排序大致快2倍
  2. Shell排序比起QuickSort快速排序,MergeSort归并排序,HeapSort堆排序慢很多。
  3. 但是它相对比较简单,它适合于数据量在5000以下并且速度并不是特别重要的场合。对于数据量较小的、原本顺序不是很乱的数列的排序,希尔排序的效果是非常好的。

4.2)希尔排序的时间复杂度:

其最坏时间复杂度依然为O(排序算法总结 - 图27),
希尔排序的时间复杂度为O(n^1.3)~O(n^2),平均时间复杂度为O(排序算法总结 - 图28)。
希尔排序的时间复杂度我们计算起来比较复杂,所以在这里我们直接记住这个结论即可:
1,在数组整体有序的情况下,希尔排序的时间复杂度是接近o(n^1.3)的;
2,在数组整体逆序的情况下,希尔排序的时间复杂度和插入排序接近,是O(排序算法总结 - 图29);

所以,希尔排序的时间复杂度我们进行如下表示:
希尔排序的时间复杂度:O(排序算法总结 - 图30) (1.3<= k <= 2),平均时间复杂度为O(排序算法总结 - 图31)。

注意: 有的同学认为,希尔排序在代码实现的时候,相当于嵌套了3层循环结构,那它的时间复杂程度应该是O(n3),这种认知是错误的。
通过算法的循环嵌套层数推断算法的事件复杂程度的确是一种技巧,但是并不是百试百灵的。一个算法的时间复杂程度,依然要通过分析算法的基本流程本身才能够得到。

4.3)希尔排序代码:

/**
* 1,设置增量increment,根据increment将数组分为多个子数组:每个元素和该元素的位置+increment所在位置的元素是一组;
* 2,将每个子数组进行插入排序;
* 3,增量increment/2,再次进行划分子数组并对每个子数组进行插入排序;
* 4,直到增量为1的时候,进行最后一次插入排序即可;
*
* @param arr
*/
public static void xierpaixu(int[] arr) {
    //1,一共几轮插入排序,每一轮设置一个增量increment,比如说从arr.length/2开始,每一轮increment缩小一半,到1停止;
    for (int increment = arr.length / 2; increment >= 1; increment /= 2) {
          //对于子数组执行插入排序:
          for (int i = 0; i < arr.length-increment; i++) {
                //i以及i左边的元素是已排好序的,i右边的是没排好序的:
                int daipaixuEle = arr[i + increment];
                for (int j = i; j >= 0; j-=increment) {
                      //如果待排序元素小于当前遍历元素,则当前遍历元素右移increment位,待排序元素暂时插入到当前遍历元素的原位置;
                      if (daipaixuEle < arr[j]) {
                            arr[j + increment] = arr[j];
                            arr[j] = daipaixuEle;
                            continue;
                      }
                      //如果待排序元素大于当前遍历元素,则此时正好待排序元素就在当前遍历元素的右边increment位置,子数组的本轮插入排序结束;
                      else{
                            break;
                      }
                }
          }
    }

}

5)快速排序:

详见:快速排序-学习笔记_Morning sunshine的博客-CSDN博客

6)归并排序:

原文链接:图解排序算法(四)之归并排序 - dreamcatcher-cx - 博客园

6.1)归并排序思想:

归并排序 是利用归并的思想实现的排序方法;

该算法采用经典的分治策略:
1、将问题成一些小的问题然后递归求解;
2、而的阶段则将分的阶段得到的各个答案”拼组”在一起,即分而治之)。
image.png
可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。
阶段可以理解为就是递归拆分子序列的过程,递归深度为log2n。

基本思想

归并排序的主要思想是分治法。主要过程是:

  1. 将n个元素从中间切开,分成两部分。(左边可能比右边多1个数)
  2. 将步骤1分成的两部分,再分别进行递归分解。直到所有部分的元素个数都为1
  3. 从最底层开始逐步合并两个排好序的数列


思考:

考虑一个问题,如何将两个有序数列合并成一个有序数列?

很简单,由于进行合并的时候,由于两个数列本身都已经有序,所以:

  1. 我们只需从两个数列的低位轮番拿出各自最小的数来PK就行了
  2. 输的一方为小值,将这个值放入临时数列,然后输的一方继续拿出一个值来PK
  3. 直至有一方没有元素后,将另一方的所有元素依次接在临时数列后面即可

此时,临时数列为两个数列的有序合并。归并排序中的归并就是利用这种思想。

6.2)归并排序的时间复杂度:

归并排序是稳定排序,它也是一种十分高效的排序

能利用完全二叉树特性的排序一般性能都不会太差

java中Arrays.sort()采用了一种名为TimSort的排序算法,就是归并排序的优化版本

归并排序的时间复杂度为O(n*logn),推导过程较复杂,不再详述。

PS:快速排序的时间复杂度也是O(n*logn);

从上文的图中可看出,每次合并操作的平均时间复杂度为O(n),而完全二叉树的深度为排序算法总结 - 图33总的平均时间复杂度为O(nlogn)

而且,归并排序的最好,最坏,平均时间复杂度均为O(nlogn)

6.3)归并排序代码:

步骤:

1,首先,将数组不断进行递归分解,直至分为一个一个的元素为最小的子数组

2,将两个相邻的(有序)子数组,合并为一个新的临时子数组,然后将临时子数组回填到原数组中去;

前提,首先两个子数组肯定是有序的、相邻的;

2.1,当两个子数组中都有元素的时候,从低位进行轮番比较,将较小元素放入临时数组,然后在较小元素所在数组中再取出一个元素,进行再次比较;

2.2,直至其中一个子数组没有元素了,则将另外一个数组的所有剩余元素依次放入临时子数组;

2.3,将临时子数组中的元素回填到原数组中;

3,排序完成;

代码示例:

public class GuiBingPaiXu01 {
  public static void main(String[] args) {
        int [] arr= new int[]{-9, 78, 100, 21, 41, -421, 23, 33, 78, -2, 33};
        guibingpaixu(arr);
        System.out.println("Arrays.toString(arr) = " + Arrays.toString(arr));
  }

  /**
   * 归并排序: 分为两个步骤:
   *          1,分解,将数组一层一层分解为一个个的单个元素的子数组;
   *          2,合并,将这些相邻的子数组进行比较之后,按照顺序合并,
   *          3,最终,合并成一个完整的有序数组;
   * <p>
   *          如何合并两个子数组为一个新的临时子数组:
   *                           前提,首先两个子数组本身肯定是有序的;
   *                            2.1,先将两个子数组的最小元素拿出来进行比较,较小者放入临时子数组的首位,
   *                            2.2,然后较小者所在的子数组继续拿出来第二个元素去进行再次比较;
   *                            2.3,重复2.1、2.2步骤,直至其中一个子数组没有元素了,则另外一个数组的所有剩余元素直接放入临时子数组;
   *                            2.4,将临时子数组中的元素回填到原数组中;
   * @param arr
   */
  private static void guibingpaixu(int[] arr) {
        //两个方法:一个分解方法,一个合并方法;
        //初始化一个临时数组:
        int[] tempArray = new int[arr.length];
        fenjie(arr, 0, arr.length - 1, tempArray);
  }

  /**
   * 分解方法:
   */
  public static void fenjie(int[] arr, int leftIndex, int rightIndex, int[] tempArray) {
        //递归出口:
        if (leftIndex < rightIndex) {
              fenjie(arr, leftIndex, (leftIndex + rightIndex) / 2, tempArray);
              fenjie(arr, (leftIndex + rightIndex) / 2 + 1, rightIndex, tempArray);
              //1,当上边的两个递归分解都到了最后一次递归结束,并回溯了一级之后,
              //     此时arr[leftIndex]和arr[rightIndex]是两个相邻的单个元素的子数组:
              //     对这两个子数组执行合并操作:
              //2,当继续往上回溯之后,两个子数组都会包含多个元素,
              //      此时两个元素的左边界为leftIndex 和 (leftIndex + rightIndex) / 2 + 1,右边界为(leftIndex + rightIndex) / 2 和 rightIndex;
              hebing(arr, leftIndex, (leftIndex + rightIndex) / 2, (leftIndex + rightIndex) / 2 + 1, rightIndex, tempArray);
        }
        //分解到最后一次时,此时leftIndex == rightIndex,说明此时只有一个元素了,
        // 只有一个元素当然不需要合并,所以停止递归分解;
        else {
              return;
        }

  }

  /**
   * 将两个子数组合并为一个新的临时子数组
   * (两个子数组一定是相邻着的)
   *
   * @param arr         数组
   * @param leftIndexA  子数组A左边界
   * @param rightIndexA 子数组A右边界
   * @param leftIndexB  子数组B左边界
   * @param rightIndexB 子数组B右边界
   * @param tempArray    临时的新数组,leftIndexA为其左边界,rightIndexB为其右边界
   */
  public static void hebing(int[] arr, int leftIndexA, int rightIndexA, int leftIndexB, int rightIndexB, int[] tempArray) {
        //边界不能动,设置两个子数组的游标:
        int leftYouBiaoA = leftIndexA;
        int leftYouBiaoB = leftIndexB;
        //初始化临时数组的左游标:
        int tempLeftYouBiao = leftIndexA;
        //当两个子数组中都有元素的时候,进行比较,并将较小元素放入临时数组,并在较小元素所在数组再取出一个元素进行再次比较;
        while (leftYouBiaoA <= rightIndexA && leftYouBiaoB <= rightIndexB) {
              if (arr[leftYouBiaoA] < arr[leftYouBiaoB]) {
                    //将两个子元素的较小元素放入临时数组,
                    tempArray[tempLeftYouBiao] = arr[leftYouBiaoA];
                    tempLeftYouBiao++;
                    leftYouBiaoA++;
              }else{
                    //将两个子元素的较小元素放入临时数组,
                    tempArray[tempLeftYouBiao] = arr[leftYouBiaoB];
                    tempLeftYouBiao++;
                    leftYouBiaoB++;
              }
        }
        //当A数组已经没有元素之后,将B数组剩余的元素依次放入临时数组:
        while (leftYouBiaoA > rightIndexA && leftYouBiaoB <= rightIndexB) {
              tempArray[tempLeftYouBiao] = arr[leftYouBiaoB];
              tempLeftYouBiao++;
              leftYouBiaoB++;
        }
        //当B数组已经没有元素之后,将A数组剩余的元素依次放入临时数组:
        while (leftYouBiaoA <= rightIndexA && leftYouBiaoB > rightIndexB) {
              tempArray[tempLeftYouBiao] = arr[leftYouBiaoA];
              tempLeftYouBiao++;
              leftYouBiaoA++;
        }
        //两个数组都一起结束了的情况不会出现,因为肯定会有一个数组先一步到达右边界;

        //最后,将临时子数组中的所有元素依次赋值到原数组中:
        for (int i = leftIndexA; i <=rightIndexB; i++) {
              arr[i] = tempArray[i];
        }
  }


}

image.png
·
·