记录排序链表 思路

104 阅读2分钟

思路

首先分析思路,链表+排序等于归并排序

归并: 如果是正常学习的数组,就要考虑空间置换的问题,但是链表是指针的行为,可以直接通过操作指针就行了,不像数组一样操作对应的东西。

步骤:

1.从中间分割开两边,使用快慢指针去获取中间位数 2.分割前面的链表 3.分割后面的链表 4.最后合并起来

code

先准备每个步骤的测试代码,然后再拼接起来

func sliceToList(arr []int) *ListNode { if len(arr) == 0 { return nil } head := &ListNode{Val: arr[0]} current := head for _, v := range arr[1:] { current.Next = &ListNode{Val: v} current = current.Next } return head }

首先的先写最简单的通过快慢指针去找到中位数(好像有几道 leetcode 也是这样子做的)

    func getMiddle(head *ListNode) *ListNode {
    slow := head
    fast := head
    for fast != nil && fast.Next != nil {
        slow = slow.Next //一个先走一步
        fast = fast.Next.Next //这个就是直接走两步
    }
    return slow //最后获取到中间的

}

//开始测试
func TestGetMiddle(t *testing.T) {
   tests := []struct {
      input    []int
      expected int
   }{
      {[]int{1, 2, 3, 4, 5}, 3},
      {[]int{1, 2, 3, 4}, 2},
      {[]int{1}, 1},
      {[]int{}, 0},
   }

   for _, tt := range tests {
      t.Run("getMiddle", func(t *testing.T) {
         input := sliceToList(tt.input)
         mid := getMiddle(input)
         if mid != nil && mid.Val != tt.expected {
            t.Errorf("expected middle value %v, but got %v", tt.expected, mid.Val)
         } else if mid == nil && tt.expected != 0 {
            t.Errorf("expected middle value %v, but got nil", tt.expected)
         }
      })
   }
}

然后做一个 合并两个排序的 方法,这个比较写用一个 for 然后去比较谁比较大,然后把剩下的加到另外一个链表即可


func merge(left, right *ListNode) *ListNode {

    dummy := &ListNode{}
    tail := dummy
    for left != nil && right != nil {
        if left.Val < right.Val {
            tail.Next = left
            left = left.Next
        } else {
            tail.Next = right
            right = right.Next
        }
        tail = tail.Next
    }
    if left != nil {
        tail.Next = left
    }
    if right != nil {
        tail.Next = right
    }
    return dummy.Next

}

//这里还能优化一下  只用 l1 的空间
func merge(list1, list2 *ListNode) *ListNode {
    // 如果 list1 为空,直接返回 list2
    if list1 == nil {
       return list2
    }
    // 如果 list2 为空,直接返回 list1
    if list2 == nil {
       return list1
    }

    // 比较头结点,确定哪个链表的头结点较小
    if list1.Val < list2.Val {
       list1.Next = merge(list1.Next, list2) // 递归合并剩余部分
       return list1
    } else {
       list2.Next = merge(list1, list2.Next) // 递归合并剩余部分
       return list2
    }
}

最后合并起来

// mergeSort 实现链表的归并排序
func mergeSort(head *ListNode) *ListNode {
    if head == nil || head.Next == nil {
       return head
    }

    // 1. 找到链表的中间节点
    mid := getMiddle(head)
    left := head
    right := mid.Next
    mid.Next = nil // 切断中间节点与右侧部分的连接

    // 2. 递归对左右部分进行归并排序
    left = mergeSort(left)
    right = mergeSort(right)

    // 3. 合并排序后的两个链表
    return merge(left, right)
}

然后成功发生死循环,断电打在

        left = mergeSort(left)

为什么为什么?因为这里需要slow 指针无论如何也不会移动


// 获取链表的中间节点
func getMiddle(head *ListNode) *ListNode {
    if head == nil || head.Next == nil {
       return head
    }
    slow, fast := head, head

    // 快慢指针,找到中间节点 ,这里有问题,slow 一直返回最后一个
    for fast != nil && fast.Next != nil {
       slow = slow.Next
       fast = fast.Next.Next
    }
    //新增加代码
    if fast == nil {
       return head
    }

    return slow
}

最后ac