148.排序链表
题目描述:
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3 输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0 输出: -1->0->3->4->5
思路:
要求 O(n log n) 时间复杂度和常数级空间复杂度,首先想到的就是快速排序和归并排序。
此处使用归并排序,
归并排序大致思想是: 1.将链表划分左右两部分--> 2.对左右链表进行排序 --> 3.将左右两个有序链表进行合并
如果三个步骤如果分开实现的,都比较容易,但有人容易在第二步骤懵逼———为什么划分后,对左右链表进行排序,再合并,这不是多此一举么。直接使用第二步对原链表进行排序不就好了?
所以,在归并排序中,实现第二步的实现,其实是靠第三部来完成的。
当两个链表长度都为1时,即两个链表只有一个结点,那么我们就认为这两个链表是有序的,直接进行 步骤3——将两个有序链表进行合并,于是两个链表合并成了一个长度为2的有序链表。
于是问题转化为,如何把一个长链表 划分为 长度为1的短链表? 很简单,递归划分,不断的将长链表进行二分,最终分成多个长度为1的链表,然后进行合并, 过程类似于二叉树。
感觉和快速排序的分治思想没有太大区别。
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode sortList(ListNode head) { return head == null ? head : merge(head); } //划分 public ListNode merge(ListNode head){ if (head.next == null) { //递归退出条件:即链表被划分后只剩一个结点 return head; } ListNode fast = head, slow = head, pre = head; //这里使用快慢指针 进行链表划分 while (fast != null && fast.next != null) { pre = slow; slow = slow.next; fast = fast.next.next; } pre.next = null; //划分结果是,fast为原链表尾.next, slow为划分后链表右半部分表头,pre为左半部分表尾结点,所以pre.next要为null ListNode l = merge(head); //左半部分递归划分 ListNode r = merge(slow); //右半部分递归划分 return mergeList(l,r); //合并链表 } //合并 此处f是合并两个有序链表。 public ListNode mergeList(ListNode h1,ListNode h2){ ListNode dummy = new ListNode(0); ListNode head = dummy; while (h1 != null && h2 != null) { if (h1.val < h2.val) { head.next = h1; head = head.next; h1 = h1.next; }else { head.next = h2; head = head.next; h2 = h2.next; } } if (h1 == null) { head.next = h2; }else { head.next = h1; } return dummy.next; } }