写给自己看的单链表(4):快速排序

大憨熊 提交于 2020-01-29 03:04:51

搬运自我的CSDN https://blog.csdn.net/u013213111/article/details/88670136

!!!Attention:以下操作中的单链表均带有头结点!!!
参考了这三篇文章:
单链表快速排序算法的实现
单链表的快速排序
单链表的快排实现
快速排序的思路是:首先,选取一个pivot;然后以pivot作为基准,对待排序的数据进行分区,得到两个部分,一个部分的数据均小于pivot,另一个部分的数据均大于pivot,然后对这两个部分再进行之前的操作,直至排序完成。
对数组来说,pivot通常会选取位于中间的数据,然后用两个指针分别从头、尾两端向中间移动。
但是对于单链表来说,由于只能从头到尾单方向进行移动,所以其快速排序的实现方式有所不同,取第一个节点为pivot(这里要注意开区间和闭区间的问题,下面细说),指针p从pivot开始向后移动,指针q从pivot->next开始向后移动,最后一步是交换pivot和p的值,最终呈现的结果是:从头结点至p-1(含p-1)之间的数据均小于pivot,从p+1(含p+1)至q之间的值均大于pivot。
用一个例子来看看Partition函数的思路:

下面看一下不同区间表达式下的代码,开区间和闭区间导致代码不同的思路来自于这篇文章:你常写的二分查找,真的是没有bug吗?
1.左闭右开,初始区间为 [head->next,NULL)
这是比较省事的一种表达方式。首先看QuickSort函数的循环条件,当left != right时继续排序,借用上面那篇文章中的一个例子来说就是:

由于我们采用的是左闭右开表示法,也就是 区间为 [left ,right) 且left == right 循环停止,,如果用个例子来解释,大家就会豁然开朗了, 假如[ left=4, right=4),此时用左闭右闭来解释的话,区间实际上是出现了右边界=3 , 左边界=4 , 这种情形,右边界 < 左边界,这意味着区间长度<=0 了,无法继续了。

在迭代的过程中,仍然要保持区间是左闭右开的。在第一次分区后,数据的两个部分分别表示为 [head->next,par)[par->next, NULL),所以迭代的语句为QuickSort(head, left, par); QuickSort(head, par->next, NULL);注意Partation函数中的语句也要根据区间的左闭右开性质来写。

 1 void QuickSort(Lnode *head, Lnode *left, Lnode *right) //left is head->next, right is null
 2 {
 3     if ( left != right) {    //ATTENTION!!!
 4         Lnode *par = Partation(left, right); 
 5         QuickSort(head, left, par);
 6         QuickSort(head, par->next, NULL);
 7     }
 8 }
 9 
10 Lnode *Partation(Lnode *left, Lnode *right)
11 {
12     if (left == right)
13         return left;
14     
15     Lnode *p, *q;
16     eletype pivot = left->data;    
17     p = left;
18     q = p->next;
19     while (q != right) {
20         if (q->data < pivot) {
21             p = p->next;
22             SwapData(p, q);
23         }
24         q = q->next;
25     }
26     SwapData(left, p);
27     return p;
28 }
29 
30 void SwapData(Lnode *p, Lnode *q)
31 {
32     eletype tmp;
33     tmp = p->data;
34     p->data = q->data;
35     q->data = tmp;
36 }

2.左开右开,初始区间为 (head,NULL)
这种表达方式使得调用函数时比较方便,因为参数直接就是head和NULL,但是写法比上一种稍麻烦了一点点。
QuickSort函数的循环条件发生了改变,当left->next != right时继续排序。在第一次分区后,数据的两个部分分别表示为 (head,par)(par, NULL),所以迭代的语句为QuickSort(head, left, par); QuickSort(head, par, NULL);,Partation函数中的语句也要根据区间的左开右开性质来写。

 1 void QuickSort(Lnode *head, Lnode *left, Lnode *right)
 2 {
 3     if (left->next != right) {    
 4         Lnode *par = Partation(left, right);
 5         QuickSort(head, left, par);
 6         QuickSort(head, par, NULL);
 7     }
 8 }
 9 
10 Lnode *Partation(Lnode *left, Lnode *right)
11 {
12     if (left->next->next == right)
13         return left->next;
14     
15     Lnode *p, *q;
16     eletype pivot = left->next->data;    
17     p = left->next;
18     q = p->next;
19     while (q != right) {
20         if (q->data < pivot) {
21             p = p->next;
22             SwapData(p, q);
23         }
24         q = q->next;
25     }
26     SwapData(left->next, p);
27     return p;
28 }
29 
30 void SwapData(Lnode *p, Lnode *q)
31 {
32     eletype tmp;
33     tmp = p->data;
34     p->data = q->data;
35     q->data = tmp;
36 }

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!