1 思路
归并排序采用了经典的分治策略,通过递归求解。
要解决的问题就分为“分”和“治”两个步骤。
分的阶段可以看做递归拆分子序列的过程,递归深度为log2N。
治的阶段也就是归并的阶段,将拆分开的子序列,相邻的两两合并。
时间复杂度 O(nlogn)
2 模板
1.分治合并
public static void mergeSort(int nums[], int first, int last, int temp[])
{
if (first < last){
int mid = (first + last) / 2;
mergeSort(nums, first, mid, temp); //左边有序
mergeSort(nums, mid + 1, last, temp); //右边有序
mergeArray(nums, first, mid, last, temp); //再将二个有序数列合并
}
}
2.合并部分代码
public static void mergeArray(int nums[], int first, int mid, int last, int temp[])
{
//i是左边有序数组索引,j是右边有序数组索引,k是temp数组索引,
//为了便于理解保持temp和nums的索引范围相同
int i = first, j = mid + 1,k=first;
/**
* 下面这段就是合并两个有序数组的代码
*/
while (i <= mid && j <= last) {
if (nums[i] <= nums[j])
temp[k++] = nums[i++];
else
temp[k++] = nums[j++];
}
while (i <= mid)
temp[k++] = nums[i++];
while (j <= last)
temp[k++] = nums[j++];
//temp数组是nums[first]到nums[last]这段的有序排列,对应索引赋值给nums
for (i = first; i < k; i++)
nums[i] = temp[i];
}
3.测试代码
public static void main(String[] args) {
int []nums={5,7,8,9,6,4,7,5,5};
//为了不在合并过程中反复创建temp数组,提前创建好与nums同样大小的数组不断重用。
int []temp =new int[nums.length];
mergeSort(nums,0,nums.length-1,temp);
for(int num:nums){
System.out.println(num);
}
}
3 例题——剑指offer(数组中的逆序对)
题目
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例
输入: [7,5,6,4]
输出: 5
思路
在归并排序的过程中进行逆序对的计数。只需要再归并排序的框架中增加计数的代码。
代码
class Solution {
public int reversePairs(int[] nums) {
int[] temp = new int[nums.length];
return mergeSort(nums, 0, nums.length-1, temp);
}
public int mergeSort(int[] nums, int first, int last, int[] temp) {
if (first < last){
int mid = (first + last) / 2;
//左边有序并计数
int countLeft = mergeSort(nums, first, mid, temp);
//右边有序并计数
int countRight=mergeSort(nums, mid + 1, last, temp);
//再将二个有序数列合并并计数
int count =mergeArray(nums, first, mid, last, temp);
return count+countLeft+countRight;
}
else
return 0;
}
public static int mergeArray(int nums[], int first, int mid, int last, int temp[]){
int i = first, j = mid + 1,k=first,count=0;
while (i <= mid && j <= last) {
if (nums[i] <= nums[j])
temp[k++] = nums[i++];
else{
temp[k++] = nums[j++];
//除了归并过程之外,加上归并过程中的逆序对计数
//每当右半数组有元素小于左数组时,左数组中当前的个数就是要增加的逆序对的个数
count=count+mid-i+1;
}
}
while (i <= mid)
temp[k++] = nums[i++];
while (j <= last)
temp[k++] = nums[j++];
for (i = first; i < k; i++)
nums[i] = temp[i];
return count;
}
}