LeetCode排序链表

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

示例 1:

LeetCode排序链表

 

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

示例 2:

LeetCode排序链表

 

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

示例 3:

输入:head = []输出:[]

思路:

  这个题乍一看是将链表进行排序。以前好像从来没有接触过如何排序链表,可以用像排序普通数组那样的排序方法来排序吗?是可以的。

  这里我们可以使用归并排序的思路来解决。归并排序依然是用递归方法,总体思路就是:把要排序的数组分成两部分,对两部分再进行归并排序,排好后将这两部分有序数组合并到一起。只要定义好归并排序的base case(比如个数为1时直接返回),这个问题就解决了。

  如果有归并排序的基础,上面写的就很好理解了。现在问题转变为:在链表上进行归并排序,怎么类似地把链表分成两部分?用快慢指针。我们虽然不知道链表的长度,但我们定义一个一次走一步的慢指针和一个一次走两步的快指针,当快指针到达终点时,满指针所处的位置就是中间切分点

  那base case如何定义呢?我们对分出来的每一部分继续进行归并排序,那么当只剩一个节点或者传入为空的时候,我们就不需要再分成两部分了吧,直接返回当前即可,这就是base case。

  那么如何把排序好的两部分进行合并呢?这不就是合并两个有序链表问题吗?我这里采用非递归的方法,开辟第三个结果链表,逐一把两个链表中较小的值添加到结果链表。因为这个也不是这道题的重点,具体细节就不再描述了,代码里体现即可。

代码:

class Solution(object):

    def sortList(self, head):

        def merge_sort(head):#merge_sort功能就是对传入链表归并排序,返回排序好结果

            #定义base case

            if not head or not head.next:#如果为空,或只有一个节点

                return head#直接返回自己

            slow,fast=head,head.next#fast初始化为head.next,如果初始化head则死循环

                #fast初始化为head.next也可以保证slow无论怎样都正好到达前半段的结尾

            while fast and fast.next:#快指针的判断到头与否,和环形链表题方法一致

                slow=slow.next#慢指针走一步

                fast=fast.next.next#快指针走两步

            #现在慢指针来到了第一部分的末尾

            head2 = slow.next#慢指针的下一个节点当成新的头结点head2

            slow.next=None#慢指针指向空,让第一部分链表独立出来

            return merge(merge_sort(head),merge_sort(head2))#两部分分别排序,再merge

        def merge(root1,root2):#merge方法,合并两个有序链表问题而已

            p = ListNode()#定义第三个链表

            q = p#q暂存头结点

            while root1  and root2:#两个指针分别游走于两个链表,取小放进第三个链表

                if root1.val<=root2.val:#root1小

                    p.next=root1

                    p=p.next

                    root1= root1.next

                else:#root2小

                    p.next=root2

                    p=p.next

                    root2 = root2.next

            p.next=root1 if root1 else root2#把其中没有遍历完链表剩下部分全部加进结果

            return q.next

        return merge_sort(head)

小结:

  从递归函数的整体来看,merge_sort先利用快慢指针将链表分成两部分,分别进行归并排序后,将结果合并,返回排序好后的头结点,逻辑是自洽的,不需跳进下一层的递归。一切都自然而然地交给自动递归和base case来处理。

   实现起来要注意的细节有对fast指针的初始化,要初始化为head.next,这样可以保证当fast到达终点(最终节点或者最终节点的下一位)时,slow的位置在前半段的结尾。这种小结论是因结果出错(实践出真知)所总结出来的,自己动手画一下图就可以理解了。

上一篇:归并排序和一些例题


下一篇:【当git合并时发生代码冲突如何解决】