单链表有一个重要的特性,就是只能从前往后遍历。所以单链表的排序方法和普通的数组排序不一样。
一、快排
数组快排需要两个指针,一前一后往中间靠拢。由于单链表只能单向遍历,故需要设计不同的算法。
设定p和q指针,并且规定p和q之间的数都是大于pivot的数(包含p)。
1)p从start开始,也就是一开始p就是满足条件的p指针,q从p的下一个指针开始,也就是待判定的节点。
2)当q的数大于等于pivot(start),则该数可以在p和q之间,故只需要将q前进一位
3)如果q的数小于pivot则该节点应该在p之前。故将p前进一位,并且交换p q。
4)循环知道q == end
可以知道, p一直指向最后一个小于pivot的节点,故循环最后,交换p和start,就完成一次partion操作
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *pivot(ListNode *start, ListNode *end){
ListNode *p = start, *q;
q = p->next;
while(q != end){
if(q->val < start->val){
p = p->next;
int tmp = p->val;
p->val = q->val;
q->val = tmp;
}
q = q->next;
}
int tmp = p->val;
p->val = start->val;
start->val = tmp;
return p;
}
void qc(ListNode *start, ListNode *end){
if(start != end){
ListNode *par = pivot(start, end);
qc(start, par);
qc(par->next, end);
}
}
ListNode* sortList(ListNode* head) {
qc(head, nullptr);
return head;
}
};
二、归并排序
归并排序要注意的地方是,怎么把两个有序的链表合并起来,一般采用傻逼节点方式。也就是设定一个傻逼节点,作为开始节点的前面一个节点。具体可以看代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* merge(ListNode *l1, ListNode *l2){
ListNode idiot(0);
ListNode *p = &idiot;
while(l1 && l2){
if(l1->val > l2->val){
p->next = l2;
l2 = l2->next;
} else {
p->next = l1;
l1 = l1->next;
}
p = p->next;
}
p->next = l1 ? l1 : l2;
return idiot.next;
}
ListNode* sortList(ListNode* head) {
if(head == nullptr || head->next == nullptr)
return head;
ListNode *hare = head, *turtle = head;
ListNode *turtle_shit = head;
while(hare != nullptr && hare->next != nullptr){
turtle_shit = turtle;
turtle = turtle->next;
hare = hare->next->next;
}
turtle_shit->next = nullptr;
ListNode *l1 = sortList(head);
ListNode *l2 = sortList(turtle);
return merge(l1, l2);
}
};