图解链表的插入排序
本文最后更新于:4 years ago
今天有个同学问我一个考研题,链表的插入排序。
之前写的都删了,一切从简,不探讨过多非主流插入排序的写法。
在文末,再简要对比 稳定 插入排序和 不稳定 插入排序的区别。
1.题目
2.思想
先抛开题目不谈,先说常见的插入排序代码。
- 无序部分的待排序元素,依次与有序部分的每个元素进行比较,找到合适的插入位置。
- 将待排序元素,插入该位置。
- 重复上述两步操作,直到无序部分的待排序元素为 nullptr。
3.图解
3.0 代码
【为表述方便,我将外层的 while 循环(第 11 行的 while )称为大循环,内层的 while 循环(第 16 层的 while )称为小循环。】
先贴代码,根据后面解释,对照着看。
1 |
|
3.1 第一次进入大循环
在已进入 while 大循环,未进入小 while 循环之前,也就是只执行了以下代码:
1 |
|
其中 L 是头结点指针,此时 r 和 L 指向的都是头结点,q 指向第一个有序结点(data = 1)。
3.2 进入小循环体内
小循环的 while 循环判断中,因为满足 q值(1)<= p值(4),进入小循环体内部;
执行以小循环内代码:
- 将 r、q 后移。
1 |
|
后移 r、q 后,如下图所示:
3.3 跳出小循环体后
因为 q == nullptr
,不满足循环条件,所以退出小循环。
并执行以下代码:
- p 插入 r 和 q 之间。
1 |
|
得到下图:
执行以下代码:
- p 指向 u 所指的位置。
1 |
|
整理得到下图:
3.4 第二次进入大循环
进入第二次 大while 循环,r、q 归位。
主要执行的以下代码:
- r、q 从头开始。
1 |
|
为什么 r、q 要回到最开始的位置? 因为 r、q 要从头开始,找到合适的待插入的位置。
3.5 进入小循环体内
因为 q值(1)<= p值(2),满足小循环体的判断条件。
进入小循环,执行小循环体内代码:
- r、q 后移。
1 |
|
得到下图:
3.6 跳出小循环体后
因为 q值(4)> p值(2),不满足小循环的判断条件,跳出小循环体;
将待排序节点 p 插入 r、q 之间。
执行以下代码:
- p 插入 r 和 q 之间。
1 |
|
得到下图:
再执行以下代码:
- p 指向 u 所指的位置。
1 |
|
整理之后,得到下图:
3.7 第三次进入大循环
进入第二次 大while 循环,r、q 归位。
主要执行的以下代码:
- r、q 从头开始。
1 |
|
3.8 进入小循环体内
因为 q值(1)<= p值(4),所以 r、q 后移。
并将小循环体内的代码,循环执行三次。当 q 指向 nullptr 时,跳出小循环体内。
3.9 跳出小循环体后
此时,将 p 插入到 r、q 之间。
3.10 分析
这样不知不觉,全部排序完成。其实我们一直在做两件事:
- 【找位置】找到 待排序元素p 合适插入的位置。
- 【插入】将待排序元素p 插入 合适的位置(r、q 之间)。
4.扩展
4.1 稳定的和不稳定的插入排序
在排序过程中,如果两个相同数据的前后顺序不发生改变,则为稳定排序。
按以前学过的知识来讲,插入排序、冒泡排序、归并排序都是稳定排序,为什么自己写的插入排序不稳定呢?
因为「小循环的判断条件内没有 等号」。也就是以下代码:
1 |
|
当判断条件没有 等号时,就会变成不稳定的排序。具体就不论证了,大家演示一下便知。
4.2 复杂度分析
时间:O(n2)
最坏的情况,完全逆序的链表,两个 while 循环,要循环 1+2+3+…+n-2+n-1 次,也就是 n2 / 2 次。
最好的情况,不用插入,遍历一次。O(n)。
空间:O(1)
因为临时占用的空间为 0。