完整代码
采用快排实现链表的排序,比较难处理的地方是如何实现分隔,由于链表只能从前往后遍历不能从后往前遍历,那么
常规的分隔方法:
- 最左边的元素为基准,两个指针一个指向最左边,一个指向最右边。
- 先从右往左判断,右指针停在比基准小的元素,将此元素放在左指针指示的位置,
- 再从左往右判断,将左指针停在比基准大的元素,将此元素放在右指针指示的位置
- 直到左指针不再小于右指针,停止判断,该位置就是基准所放的位置
常规的分隔方法不适用,另外一种分隔方法:
- 最左面的元素为基准,两个指针,一个指针p1指向小于基准元素的子序列的最后一个位置,一个指针p2指向待判别的元素,初始时p1指向基准元素,p2指向基准元素的下一个位置
- 如果当前元素(p2所指向的元素)大于基准元素则p2向后移动
- 否则, p1向后移动一个位置(此时指向大于基准元素的第一个元素),将p2所指的元素和p1所指的元素进行交换,p2再向后移动一个位置
- 当p2移动到最后时,结束循环,将p1所指元素(小于等于基准元素的最后一个位置)和基准元素交换
链表实现起来比较困难,涉及到交换两个节点
如何交换链表的两个节点:
- 需要记住两个节点的前驱指针
- 如果两个节点是相邻节点,需要特别处理
交换完两个节点后需要将链表按照基准元素的位置分成两部分,分别进行快排,快排结束还需要将这两部分进行合并
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* quickSortList(ListNode* head) {
ListNode * h = new ListNode(0);
h -> next = head;
return qsort(h)->next;
}
private:
ListNode* qsort(ListNode* phead){
if(phead->next == NULL)
return phead;
ListNode *pivot = phead;
ListNode *pleft = phead->next;
while(pleft->next){
if(pleft->next->val <= phead->next->val){
pivot = pivot->next;
if(pleft->next != pivot->next)
swap(pivot,pleft);
else
pleft = pleft->next;
}
else
pleft = pleft->next;
}
ListNode *nhead = new ListNode(0);
if(phead != pivot){
swap(phead, pivot);
ListNode *next = pivot->next;
nhead->next = next;
pivot->next = NULL;
qsort(phead);
qsort(nhead);
}
else{
//基准数已经放在了正确的位置,只需对基准数后面的排序
qsort(phead->next);
}
//merge
ListNode * p1 = phead->next, * p2 = nhead->next, *h = new ListNode(-1), *p = h;
while(p1 && p2){
if(p1->val < p2->val){
p->next = p1;
p1 = p1->next;
}
else{
p->next = p2;
p2 = p2->next;
}
p = p->next;
}
while(p1){
p->next = p1;
p1 = p1->next;
p = p->next;
}
while(p2){
p->next = p2;
p2 = p2->next;
p = p->next;
}
return h;
}
void swap(ListNode* p1, ListNode *p2){
if(p1-> next != p2){//两节点不相邻
ListNode *cp1 = p1->next, *np1 = cp1->next;
ListNode *cp2 = p2->next, *np2 = cp2->next;
p1->next = cp2;
cp2->next = np1;
p2->next = cp1;
cp1->next = np2;
}
else{
ListNode *np2 = p2->next, *np = np2->next;
p1->next = np2;
np2->next = p2;
p2->next = np;
}
}
};
参考评论区的思想:
链表分隔的方法:
- 遍历链表按照基准元素的大小拆分成三个不同的链表
- 大于基准元素的链表,小于基准元素的链表,等于基准元素的链表
拆分完分别对左右拆分好的小于和大于基准元素的链表进行递归,最终再将三个链表合并
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* quickSortList(ListNode* head) {
return quickSort(head);
}
private:
ListNode* getTail(ListNode* head){
ListNode *p = head;
while(p->next){
p = p->next;
}
return p;
}
ListNode* quickSort(ListNode* head){
if(!head || !head->next)
return head;
ListNode *left = new ListNode(-1), *right = new ListNode(-1), *mid = new ListNode(-1), *p = head;
ListNode *lhead = left, *mhead = mid, *rhead = right;
//分隔
while(p){
if(p->val < head->val){
left = left->next = p;
}
else if(p->val == head->val){
mid = mid->next = p;
}
else{
right = right->next = p;
}
p = p->next;
}
left->next = NULL;
mid->next = NULL;
right->next = NULL;
//递归,说明:设置了头节点避免了分割后一端为空的时候需要特别处理的步骤
lhead->next = quickSort(lhead->next);
rhead->next = quickSort(rhead->next);
//合并
getTail(lhead)->next = mhead->next;
getTail(lhead)->next = rhead->next;
return lhead->next;
}
};