归并算法
思路:
- 分解成两个待排序的区间;
- 使用递归将两个子序列分别排好序;
- 申请空间,定义两个指针指向两个序列的起始位置,比较指针所指向的元素,选择相对小的元素放入合并空间。
统一格式:
- 先递归、后merge;
- 递归的跳出条件是:
if(right == left){ return 0; }
稳定性:
主要取决于合并的过程,也就是两个有序子数组合并成一个有序数组的时候,将左侧的优先放在数组中,即可实现稳定性
public void merge(int[] nums, int left, int mid, int right){ int[] temp = new int[nums.length]; System.arraycopy(nums, left, temp, left, right - left + 1); int i = left; int j = mid + 1; for(int k = 0; k <= right; k++){ 边界问题 if(i == mid + 1){ nums[k] = temp[j++]; }else if(j == right + 1){ nums[k] = temp[i++]; }else if(temp[i] <= temp[j]){ 加上等号、遇到相同元素的时候优先放入左侧的数据,保证稳定性 nums[k] = temp[i++]; }else{ nums[k] = temp[j++]; } } }
时间复杂度:
- 归并排序的执行效率与要排序的原始数组的有序程度无关;无论数据如何,都是根据中间位置分两半递归之后合并;
- 最好时间复杂度、最坏时间复杂度、平均时间复杂度均为:O(nlogn);
例题
例题:912. 排序数组
class Solution { public int[] sortArray(int[] nums) { // 归并排序 if(nums.length == 0){ return new int[]{}; } reverse(nums, 0, nums.length - 1); return nums; } public void reverse(int[] nums, int left, int right){ if(left == right){ return; } // 排好序 int mid = (right - left) / 2 + left; reverse(nums, left, mid); reverse(nums, mid + 1, right); // 合并 merge(nums, left, mid, right); } public void merge(int[] nums, int left, int mid, int right){ int[] temp = new int[nums.length]; System.arraycopy(nums, left, temp, left, right - left + 1); int i = left; int j = mid + 1; for(int k = left; k <= right; k++){ if(i == mid + 1){ nums[k] = temp[j]; j++; }else if(j == right + 1){ nums[k] = temp[i]; i++; }else if(temp[i] <= temp[j]){ nums[k] = temp[i]; i++; }else{ nums[k] = temp[j]; j++; } } } }
例题:剑指 Offer II 077. 链表排序
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } 归并 */ class Solution { public ListNode sortList(ListNode head) { if(head == null || head.next == null){ return head; } // 中间偏前的节点 ListNode fast = head, slow = head; while(fast.next != null && fast.next.next != null){ fast = fast.next.next; slow = slow.next; } ListNode mid = slow.next; // 将前面断开 slow.next = null; ListNode l1 = sortList(head); ListNode l2 = sortList(mid); return merge(l1, l2); } // 合并 public ListNode merge(ListNode l1, ListNode l2){ ListNode head = new ListNode(); ListNode node = head; while(l1 != null && l2 != null){ if(l1.val < l2.val){ node.next = l1; l1 = l1.next; }else{ node.next = l2; l2 = l2.next; } node = node.next; } if(l1 != null){ node.next = l1; }else if(l2 != null){ node.next = l2; } return head.next; } }
例题:剑指 Offer 51. 数组中的逆序对
class Solution { public int reversePairs(int[] nums) { if(nums.length < 2){ return 0; } return reversePairs(nums, 0, nums.length - 1, new int[nums.length]); } public int reversePairs(int[] nums, int left, int right, int[] temp) { if(left == right){ return 0; } //排序 int mid = (right - left) / 2 + left; int leftPairs = reversePairs(nums, left, mid, temp); int rightPairs = reversePairs(nums, mid + 1, right, temp); if(nums[mid] <= nums[mid + 1]){ return leftPairs + rightPairs; } //合并两个子序列的过程 int crossPairs = reversePairs(nums, left, mid, right, temp); return leftPairs + rightPairs + crossPairs; } public int reversePairs(int[] nums, int left, int mid, int right, int[] temp) { System.arraycopy(nums, left, temp, left, right - left + 1); int i = left; int j = mid + 1; int count = 0; for(int k = left; k <= right; k++){ if(i == mid + 1){ nums[k] = temp[j]; j++; }else if(j == right + 1){ nums[k] = temp[i]; i++; }else if(temp[i] <= temp[j]){ nums[k] = temp[i]; i++; }else{ nums[k] = temp[j]; j++; count += mid - i + 1; } } return count; } }