148、排序链表
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
进阶:
- 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
前言
方法一:自顶向下的归并排序
1.1 思路分析
要求用O(nlogn)的时间复杂度,排序算法中的归并排序、快速排序和堆排序时间复杂度是O(nlogn)。这道题适用归并排序,可以将链表切分成子序列,递归方式进行排序和合并。
递归的结束条件是head节点为空,或只有一个节点。将两个有序链表合并可以用【21、合并两个有序链表】。
1.2 代码实现
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func sortList(head *ListNode) *ListNode {
return sort(head, nil)
}
// 合并两个有序链表
func merge(head1, head2 *ListNode) *ListNode{
res := &ListNode{}
ans := res
for head1 != nil && head2 !=nil{
if head1.Val <= head2.Val{
ans.Next = head1
head1 = head1.Next
}else{
ans.Next = head2
head2 = head2.Next
}
ans = ans.Next
}
if head1==nil{
ans.Next = head2
}
if head2==nil{
ans.Next = head1
}
return res.Next
}
// 排序
func sort(head *ListNode, tail *ListNode) *ListNode{
if head == nil{
return head
}
if head.Next == tail{ // 把中间节点变为尾节点
head.Next = nil
return head
}
l, r := head, head
for r != tail{
l = l.Next
r = r.Next
if r != tail{
r = r.Next
}
}
mid := l
return merge(sort(head, mid), sort(mid, tail))
}
1.3 测试结果
1.4 复杂度
- 时间复杂度:O(nlogn),其中 n 是链表的长度。
- 空间复杂度:O(logn),其中 n 是链表的长度。空间复杂度主要取决于递归调用的栈空间。
方法二:自底向上的归并排序
2.1 思路分析
2.2 代码实现
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func sortList(head *ListNode) *ListNode {
// 空链表
if head == nil{
return head
}
// 1. 获取链表长度
length := 0
for node:=head; node!=nil; node=node.Next{
length++
}
// 2. 初始化,引入dummyNode
dummyNode := &ListNode{Next:head}
// 3. 每次将链表拆分成若干个长度为subLen的子链表 , 并按照每两个子链表一组进行合并
for subLength:=1; subLength<length; subLength <<=1{
// subLen每次左移一位(即sublen = sublen*2) PS:位运算对CPU来说效率更高
prev, cur := dummyNode, dummyNode.Next // curr用于记录拆分链表的位置
for cur != nil{ // 如果链表没有被拆完
// 3.1 拆分subLen长度的链表1
head1 := cur // 第一个链表的头 即 curr初始的位置
for i:=1; i<subLength && cur!=nil && cur.Next!=nil; i++{ // 拆分出长度为subLen的链表1
cur = cur.Next
}
// 3.2 拆分subLen长度的链表2
head2 := cur.Next // 第二个链表的头 即 链表1尾部的下一个位置
cur.Next = nil // 断开第一个链表和第二个链表的链接
cur = head2 // 第二个链表头 重新赋值给curr
for i:=1; i<subLength && cur!=nil && cur.Next!=nil; i++{ // 再拆分出长度为subLen的链表2
cur = cur.Next
}
// 3.3 再次断开 第二个链表最后的next的链接
var next *ListNode
if cur != nil{
next = cur.Next // next用于记录 拆分完两个链表的结束位置
cur.Next = nil // 断开连接
}
// 3.4 合并两个subLen长度的有序链表
merged := merge(head1, head2)
prev.Next = merged // prev.next 指向排好序链表的头
for prev.Next != nil{
prev = prev.Next
}
cur = next // next用于记录 拆分完两个链表的结束位置
}
}
// 返回新排好序的链表
return dummyNode.Next
}
// 合并两个有序链表
func merge(head1, head2 *ListNode) *ListNode{
res := &ListNode{}
ans := res
for head1 != nil && head2 !=nil{
if head1.Val <= head2.Val{
ans.Next = head1
head1 = head1.Next
}else{
ans.Next = head2
head2 = head2.Next
}
ans = ans.Next
}
if head1==nil{
ans.Next = head2
}
if head2==nil{
ans.Next = head1
}
return res.Next
}
2.3 测试结果
2.4 复杂度
- 时间复杂度:O(nlogn),其中 n 是链表的长度。
- 空间复杂度:O(1)。