归并排序(leetcode148)

148. 排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表

进阶:

  • 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

示例 1:

输入:head = [4,2,1,3]
输出:[1,2,3,4]

示例 2:

输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

提示:

  • 链表中节点的数目在范围 [0, 5 * 104]
  • -105 <= Node.val <= 105

题解

参考

思路

看到时间复杂度的要求,而且是链表,归并排序比较好做。

都知道归并排序要先归(二分),再并。两个有序的链表是才比较容易合并的。

二分到不能再二分,即递归压栈到了底部,链只有一个结点,本身就是有序的,于是在递归出栈时进行合并。

// 伪代码
func sortList (head) {
	对链表进行二分
	l = sortList(左链) // 已排序的左链
	r = sortList(右链) // 已排序的右链
	merged = mergeList(l, r) // 进行合并
	return merged		// 返回合并的结果给父调用
}

二分、merge 函数

  • 二分,用快慢指针法,快指针走两步,慢指针走一步,快指针越界时,慢指针正好到达中点,只需记录慢指针的前一个指针,就能断成两链。

  • merge 函数做的事是合并两个有序的左右链

    • 设置虚拟头结点,用 prev 指针去“穿针引线”,prev 初始指向 dummy

    • 每次都确定 prev.Next 的指向,并注意 l1 / l2指针的推进,和 prev 指针的推进

    • 最后返回 dummy.Next ,即合并后的链。

代码实现(golang)

节点的定义:

//Definition for singly-linked list.
type ListNode struct {
	Val int
	Next *ListNode
 }

MERGESORT:

// 把无序数组变为有序数组
func sortList(head *ListNode) *ListNode {
	if head == nil || head.Next == nil { // 递归的出口,不用排序 直接返回
		return head
	}
	slow, fast := head, head // 快慢指针
	var preSlow *ListNode    // 保存slow的前一个结点
	for fast != nil && fast.Next != nil {
		preSlow = slow
		slow = slow.Next      // 慢指针走一步
		fast = fast.Next.Next // 快指针走两步
	}
    
	preSlow.Next = nil  // 断开,分成两链
    // 分治,把左边的无序数组变为有序数组
	l := sortList(head) // 已排序的左链
    // 分治,把右边的边的无序数组变为有序数组
	r := sortList(slow) // 已排序的右链
	return mergeList(l, r) // 合并已排序的左右链,一层层向上返回
}

MERGE

两种写法:

  • 循环实现分治

    // 非递归写法
    func mergeList(l1, l2 *ListNode) *ListNode {
    	dummy := &ListNode{Val: 0}   // 虚拟头结点
    	prev := dummy                // 用prev去扫,先指向dummy
    	for l1 != nil && l2 != nil { // l1 l2 都存在
    		if l1.Val < l2.Val {   // l1值较小
    			prev.Next = l1 // prev.Next指向l1
    			l1 = l1.Next   // 考察l1的下一个结点
    		} else {
    			prev.Next = l2
    			l2 = l2.Next
    		}
    		prev = prev.Next // prev.Next确定了,prev指针推进
    	}
    	if l1 != nil {    // l1存在,l2不存在,让prev.Next指向l1
    		prev.Next = l1
    	}
    	if l2 != nil {
    		prev.Next = l2
    	}
    	return dummy.Next // 真实头结点
    }
    
  • 递归实现分治

    // 递归写法,l1,l2均为有序数组,将两个有序数组合并为1个有序数组
    func mergeList(l1, l2 *ListNode) *ListNode {
        if l1==nil {
            return l2
        }
        if l2==nil {
            return l1
        }
        if l1.Val > l2.Val{
            l2.Next = mergeList(l1, l2.Next)
            return l2
        }
        l1.Next = mergeList(l2, l1.Next)
        return l1
    }
    

MERGE和MERGESORT的算法描述

MERGESORT(A)

  1. if len(A) <= 1 return A
  2. q \(\leftarrow\) \(\frac{len(A)}{2}\)
  3. L \(\leftarrow\) A[0...q], R \(\leftarrow\) A[q + 1...len(A) - 1]
  4. L \(\leftarrow\) MERGESORT(L)
  5. R \(\leftarrow\) MERGESORT(R)
  6. return MERGE(L,R)

MERGE(L,R)

  1. i \(\leftarrow\) 0; j \(\leftarrow\) 0; aux \(\leftarrow\) [ ]
  2. while i < len(L) and j < len(R)
  3. if L[i] \(\leq\) R[j]
  4. ​ aux:add(L[i]) ; i \(\leftarrow\) i + 1
  5. else aux:add(R[j]) ; j \(\leftarrow\) j + 1
  6. if i == len(L)
  7. ​ aux.add(R[j : len(R) - 1])
  8. else aux.add(L[i : len(L) - 1])
  9. return
posted @ 2020-12-05 21:11  TR_Goldfish  阅读(163)  评论(0编辑  收藏  举报