解法一:排序法
利用数组存储所有节点的val,排好序后生成一条新链表即可
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func sortList(head *ListNode) *ListNode {
if head == nil || head.Next == nil{
return head
}
// 将元素加入到数组中排好序
nums := make([]int, 0)
cur := head
for cur != nil{
nums = append(nums, cur.Val)
cur = cur.Next
}
sort.Slice(nums, func(i, j int) bool{
return nums[i] < nums[j]
})
// 生成结果链表
res := &ListNode{Val:nums[0]}
cur = res
for _, v := range nums[1:]{
cur.Next = &ListNode{Val:v}
cur = cur.Next
}
return res
}
但是这个方法,申请了额外另一整个链表还有对应长度的数组的空间,空间复杂度太高了。
解法二:归并排序
归并的关键就是分治思想,需要寻找子问题结构,然后递归。
对原链表一分为二,分开为两段更短的链表,然后对这两个更短的链表进行排序,然后再合并两个有序子链表就得到答案了。
这时候,我们发现,对这两个更短链表的排序,就又回到原问题了,说明这就是子问题结构,可以用递归!
可以对子链表再切分,直到最后只剩下一个节点就无需排序直接返回排序结果,这就是递归退出条件。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func sortList(head *ListNode) *ListNode {
if head == nil || head.Next == nil{
return head
}
l1, l2 := splitList(head)
sortedL1 := sortList(l1)
sortedL2 := sortList(l2)
res := mergeTwoList(sortedL1, sortedL2)
return res
}
func splitList(head *ListNode) (*ListNode, *ListNode){
// 找到链表中点,并断开其与下一节点的连接
pre, slow, fast := head, head, head
for fast != nil && fast.Next != nil { // 链表长度为奇数或偶数的两种退出条件
pre = slow
slow = slow.Next
fast = fast.Next.Next
}
pre.Next = nil // 断开 slow 的前一个节点和 slow 的连接
return head, slow
}
func mergeTwoList(l1 *ListNode, l2 *ListNode) *ListNode{
dummy := &ListNode{}
cur := dummy
for l1 != nil && l2 != nil{
if l1.Val < l2.Val{
cur.Next = l1
l1 = l1.Next
}else{
cur.Next = l2
l2 = l2.Next
}
cur = cur.Next
}
if l1 == nil{
cur.Next = l2
}
if l2 == nil{
cur.Next = l1
}
return dummy.Next
}
- 时间复杂度:O(n*logn)
- 空间复杂度:O(logn)
实际上,用了递归需要O(logn)的栈开销
好在所有的递归基本上都可以转为迭代,进一步降低空间复杂度
前面递归思想是自顶向下,改成自底向上,就可将空间复杂度优化成O(1)
具体步骤:
- 遍历链表,获取链表长度length。
- 初始化步长step=1。
- 循环直到step≥length。
- 每轮循环,从链表头节点开始。
- 分割出两段长为step的链表,合并,把合并后的链表插到新链表的末尾。重复该步骤,直到链表遍历完毕。
- 把step扩大一倍。回到第4步。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func sortList(head *ListNode) *ListNode {
if head == nil || head.Next == nil{
return head
}
length := getListLength(head) // 获取链表长度
dummy := ListNode{Next: head} // 用哨兵节点简化代码逻辑
// step 为步长,即参与合并的链表长度
for step := 1; step < length; step *= 2 {
newListTail := &dummy // 新链表的末尾
cur := dummy.Next // 每轮循环的起始节点
for cur != nil {
// 从 cur 开始,分割出两段长为 step 的链表,头节点分别为 head1 和 head2
head1 := cur
head2 := splitList(head1, step)
cur = splitList(head2, step) // 下一轮循环的起始节点
// 合并两段长为 step 的链表
head, tail := mergeTwoLists(head1, head2)
// 合并后的头节点 head,插到 newListTail 的后面
newListTail.Next = head
newListTail = tail // tail 现在是新链表的末尾
}
}
return dummy.Next
}
// 获取链表长度
func getListLength(head *ListNode) (length int) {
for head != nil {
length++
head = head.Next
}
return
}
// 分割链表
// 如果链表长度 <= size,不做任何操作,返回空节点
// 如果链表长度 > size,把链表的前 size 个节点分割出来(断开连接),并返回剩余链表的头节点
func splitList(head *ListNode, size int) *ListNode {
// 先找到 nextHead 的前一个节点
cur := head
for i := 0; i < size-1 && cur != nil; i++ {
cur = cur.Next
}
// 如果链表长度 <= size
if cur == nil || cur.Next == nil {
return nil // 不做任何操作,返回空节点
}
nextHead := cur.Next
cur.Next = nil // 断开 nextHead 的前一个节点和 nextHead 的连接
return nextHead
}
// 返回合并后的链表的头节点和尾节点
func mergeTwoLists(list1, list2 *ListNode) (head, tail *ListNode) {
dummy := ListNode{} // 用哨兵节点简化代码逻辑
cur := &dummy // cur 指向新链表的末尾
for list1 != nil && list2 != nil {
if list1.Val < list2.Val {
cur.Next = list1 // 把 list1 加到新链表中
list1 = list1.Next
} else { // 注:相等的情况加哪个节点都是可以的
cur.Next = list2 // 把 list2 加到新链表中
list2 = list2.Next
}
cur = cur.Next
}
// 拼接剩余链表
if list1 != nil {
cur.Next = list1
} else {
cur.Next = list2
}
for cur.Next != nil {
cur = cur.Next
}
// 循环结束后,cur 是合并后的链表的尾节点
return dummy.Next, cur
}