前引
综合性比较强的一道题,要求时间复杂度必须O(logn)才能通过,最适合链表的排序算法就是归并。
这里采用自顶向下的方法
步骤:
- 找到链表中点(双指针)
- 对两个子链表排序(递归,直到只有一个结点,记得将子链表最后指向nullptr)
- 归并(引入dummy结点)
题目
代码(首刷看题解)
class Solution {
public:
ListNode* sortList(ListNode* head) {
return sortList(head, nullptr);
}
ListNode* sortList(ListNode* head, ListNode* tail) {
if(!head) // 为空
return head;
if(head->next == tail) { // 两个结点
head->next = nullptr;
return head;
}
auto mid = middleNode(head, tail);
return merge(sortList(head, mid), sortList(mid, tail));
}
// 中间结点
ListNode* middleNode(ListNode* head, ListNode* tail) {
auto fast = head;
auto slow = head;
while (fast != tail && fast->next != tail) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
// 归并
ListNode* merge(ListNode* head1, ListNode* head2) {
auto dummy = new ListNode(0);
auto tmp = dummy;
ListNode* tmp1 = head1, *tmp2 = head2;
while(tmp1 && tmp2) {
if(tmp1->val < tmp2->val) {
tmp->next = tmp1;
tmp1 = tmp1->next;
} else {
tmp->next = tmp2;
tmp2 = tmp2->next;
}
tmp = tmp->next;
}
while(tmp1) { // 其实只需要执行一次,因为左右两边链表数只相差一
tmp->next = tmp1;
tmp = tmp->next;
tmp1 = tmp1->next;
}
while(tmp2) {
tmp->next = tmp2;
tmp = tmp->next;
tmp2 = tmp2->next;
}
return dummy->next;
}
};
代码(8.9 二刷部分看解析)
用到的是归并排序
class Solution {
public:
ListNode* sortInList(ListNode* head) {
return mergeSort(head, nullptr);
}
ListNode* middleNode(ListNode* head, ListNode* tail) {
auto fast = head, slow = head;
while(fast != tail && fast->next != tail) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
ListNode* mergeSort(ListNode* p1, ListNode* p2) { // p2代表末尾
if(p1 ->next == p2) {
p1->next = nullptr;
return p1;
}
auto middle = middleNode(p1, p2);
auto q1 = mergeSort(p1, middle);
auto q2 = mergeSort(middle, p2);
return merge(q1, q2);
}
ListNode* merge(ListNode* p1, ListNode* p2) {
auto dummy = new ListNode(-1);
auto p = dummy;
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 dummy->next;
}
};
GO
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func sortList(head *ListNode) *ListNode {
return mergeSort(head, nil)
}
func middleNode(head, tail *ListNode) *ListNode {
fast, slow := head, head
for fast != tail && fast.Next != tail {
fast = fast.Next.Next
slow = slow.Next
}
return slow
}
func mergeSort(head, tail *ListNode) *ListNode {
if head == nil {
return head
}
if head.Next == tail {
head.Next = nil
return head
}
mid := middleNode(head, tail)
p1 := mergeSort(head, mid)
p2 := mergeSort(mid, tail)
return merge(p1, p2)
}
func merge(head, tail *ListNode) *ListNode {
dummy := &ListNode{}
p := dummy
for head != nil && tail != nil {
if head.Val <= tail.Val {
p.Next = head
head = head.Next
} else {
p.Next = tail
tail = tail.Next
}
p = p.Next
}
for head != nil {
p.Next = head
head = head.Next
p = p.Next
}
for tail != nil {
p.Next = tail
tail = tail.Next
p = p.Next
}
return dummy.Next
}
代码(9.15 三刷部分看解析)
细节问题:如果head->next = tail,需要将head->next置为nullptr,方便后面在归并的时候有边界条件
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(!head)
return head;
return mergeSort(head, nullptr);
}
ListNode* midddle(ListNode* head, ListNode* tail) {
auto fast = head, slow = head;
while(fast != tail && fast->next != tail) {
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
ListNode* mergeSort(ListNode* head, ListNode* tail) {
if(!head)
return head;
if(head->next == tail) {
head->next = nullptr;
return head;
}
auto mid = midddle(head, tail);
auto p1 = mergeSort(head, mid);
auto p2 = mergeSort(mid, tail);
auto dummy = new ListNode();
auto p = dummy;
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;
p = p->next;
p1 = p1->next;
}
while(p2) {
p->next = p2;
p = p->next;
p2 = p2->next;
}
return dummy->next;
}
};