1、合并K个升序链表
1.1 题目
给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]] 输出:[1,1,2,3,4,4,5,6] 解释:链表数组如下: [ 1->4->5, 1->3->4, 2->6 ] 将它们合并到一个有序链表中得到。 1->1->2->3->4->4->5->6
示例 2:
输入:lists = [] 输出:[]
示例 3:
输入:lists = [[]] 输出:[]
1.2 思路
分治/归并合并k个排序链表。
- 对这k个排序链表组成的数组进行分治划分:从中间划分为左右两部分,左右两部分依次再从中间划分为左右两部分,直到划分到最底层时返回的是单个链表,这时就需要对两个链表进行合并排序,从底向上两两合并;
对两个排序链表合并成一个链表,并返回链表的头节点:这其实也是一个独立的题目,即合并两个排序链表,有两种方法:
- 同时遍历两个链表,依次比较头节点的值,再组装成一个新的链表返回;
-
1.3 代码
class Solution {public ListNode mergeKLists(ListNode[] lists) {return merge(lists, 0, lists.length - 1);}private ListNode merge(ListNode[] lists, int left, int right) {if (left == right) {return lists[left];}if (left > right) {return null;}int mid = (left + right) >> 1;return mergeTwoList(merge(lists, left, mid), merge(lists, mid + 1, right));}private ListNode mergeTwoList(ListNode head1, ListNode head2) {if (head1 == null) {return head2;}if (head2 == null) {return head1;}if (head1.val <= head2.val) {head1.next = mergeTwoList(head1.next, head2);return head1;} else {head2.next = mergeTwoList(head1, head2.next);return head2;}}}
下面这种写法更贴近归并排序的模板:
class Solution {public ListNode mergeKLists(ListNode[] lists) {if (lists == null || lists.length == 0) {return null;}return merge(lists, 0, lists.length - 1);}private ListNode merge(ListNode[] lists, int left, int right) {if (left < right) {int mid = (left +right) >> 1;ListNode leftNode = merge(lists, left, mid);ListNode rightNode = merge(lists, mid + 1, right);return mergeSort(leftNode, rightNode);}return lists[left];}private ListNode mergeSort(ListNode head1, ListNode head2) {if (head1 == null) {return head2;}if (head2 == null) {return head1;}if (head1.val < head2.val) {head1.next = mergeSort(head1.next, head2);return head1;} else {head2.next = mergeSort(head1, head2.next);return head2;}}}
2、剑指 Offer 51.数组中的逆序对
2.1 题目
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:输入: [7,5,6,4] 输出: 5
2.2 思路
- 本体是归并排序的变种,大致流程可以参考归并排序的模板;
至于何时判断数组中的逆序对,合并阶段本质上是合并两个排序数组的过程,而每当遇到左子数组当前元素 > 右子数组当前元素时,意味着 「左子数组当前元素至末尾元素」 与 「右子数组当前元素」 构成了若干 「逆序对」。因此,考虑在归并排序的合并阶段统计「逆序对」数量,完成归并排序时,也随之完成所有逆序对的统计。
2.3 代码
class Solution {private int cnt;public int reversePairs(int[] nums) {merge(nums, 0, nums.length - 1);return this.cnt;}private void merge(int[] nums, int left, int right) {int mid = left + ((right - left) >> 1);// 当left == right时,数组里只有一个元素,不需要再划分了if (left < right) {// 划分左子数组merge(nums, left, mid);// 划分右子数组merge(nums, mid + 1, right);// 将左右子数组综合排序mergeSort(nums, left, right, mid);}}private void mergeSort(int[] nums, int left, int right, int mid) {int[] tempArray = new int[right - left + 1];int indexTempArray = 0, leftArrayIndex = left, rightArrayIndex = mid + 1;while (leftArrayIndex <= mid && rightArrayIndex <= right) {if (nums[leftArrayIndex] <= nums[rightArrayIndex]) {tempArray[indexTempArray++] = nums[leftArrayIndex++];} else {// 计算逆序对this.cnt += mid - leftArrayIndex + 1;tempArray[indexTempArray++] = nums[rightArrayIndex++];}}// 右子数组剩余部分放进tempArraywhile (rightArrayIndex <= right) {tempArray[indexTempArray++] = nums[rightArrayIndex++];}// 左子数组剩余部分放进tempArraywhile (leftArrayIndex <= mid) {tempArray[indexTempArray++] = nums[leftArrayIndex++];}// 将排好序的tempArray替换到原数组nums里对应的起始位置for (int k = 0; k < tempArray.length; ++k) {nums[left + k] = tempArray[k];}}}
3、148. 排序链表
3.1 题目
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
3.2 思路
整体思路还是用分治的思想,先分(划分排序链表),再治(合并两个排序链表),这题用到了三个题目:
划分排序链表,注意返回条件(链表为null或者链表仅有一个元素,就返回);
- 取链表中间的节点,这里是快慢指针求链表中点的题目,注意链表跟数组还不完全一样,找到链表中点后,要把对应的尾节点置为null;
-
3.3 代码
class Solution {public ListNode sortList(ListNode head) {if (head == null || head.next == null) {return head;}ListNode mid = getMid(head);ListNode tmp = mid.next;mid.next = null;return mergeTwo(sortList(head), sortList(tmp));}private ListNode getMid(ListNode head) {ListNode fast = head, slow = head;while (fast.next != null && fast.next.next != null) {fast = fast.next.next;slow = slow.next;}return slow;}private ListNode mergeTwo(ListNode head1, ListNode head2) {if (head1 == null) {return head2;}if (head2 == null) {return head1;}if (head1.val < head2.val) {head1.next = mergeTwo(head1.next, head2);return head1;} else {head2.next = mergeTwo(head1, head2.next);return head2;}}}
