1、算法原理:
首先归并排序的基本是将两个数组合并,将两个有序的数组合并为一个有序的数组,需要一个额外的辅助数组,例如a、b数组,合并为c
- c[0] 是 a[0] 和 b[0] 中较小的数,假设是a[0],然后a数组的指针加1 变为a[1]
- c[1] 是 a[1] 和 b[0] 中较小的数,如此类推
这样进行一次合并时间复杂度是O(n)
归并排序的思想是,将整个数组2等分地无限拆分为每组只有1个数的小单元,一共要拆分logn次
然后小的1个数的单元进行有序的合并,形成有序的每组有2个数的小单元,然后再进行合并形成有4个数的单元,一共进行logn次,而每次合并都是一次合并的时间复杂度O(n)。
所以整体的时间复杂度是 logn * O(n) = O(nlogn)
空间复杂度是利用的额外数组 O(n)
2、算法实现
private void mergeSort(int[] nums, int begin, int end, int[] auxiliaryArray){
if(begin < end){
int mid = (begin + end) / 2;
mergeSort(nums, begin, mid, auxiliaryArray);
mergeSort(nums, mid+1, end, auxiliaryArray);
mergeArray(nums, begin, mid, end, auxiliaryArray);
}
}
private void mergeArray(int[] nums, int begin, int mid, int end, int[] auxiliaryArray){
int leftPointer = begin, rightPointer = mid + 1;
int auxIndex = 0;
while(leftPointer <= mid && rightPointer <= end){
if(nums[leftPointer] <= nums[rightPointer]){
auxiliaryArray[auxIndex++] = nums[leftPointer++];
}else{
auxiliaryArray[auxIndex++] = nums[rightPointer++];
}
}
while(leftPointer <= mid){
auxiliaryArray[auxIndex++] = nums[leftPointer++];
}
while(rightPointer <= end){
auxiliaryArray[auxIndex++] = nums[rightPointer++];
}
for(int i = 0; i < auxIndex; i++){
nums[begin + i] = auxiliaryArray[i];
}
}
3、算法习题
lintcode 532. 逆序对
解题思路:
利用归并排序,归并排序其实就是一个消除逆序对的过程,从最小的单元开始消除,2个一组消除,4个一组消除,可以在O(nlogn)的时间复杂度中,找出所有的逆序对。
public class Solution {
/**
* @param A: an array
* @return: total of reverse pairs
*/
public long reversePairs(int[] A) {
// write your code here
int[] auxiliaryArray = new int[A.length];
int sum = 0;
sum = mergeSort(A, 0, A.length-1, auxiliaryArray);
return sum;
}
private int mergeSort(int[] nums, int begin, int end, int[] auxiliaryArray){
int sum = 0;
if(begin < end){
int mid = (begin + end) / 2;
sum += mergeSort(nums, begin, mid, auxiliaryArray);
sum += mergeSort(nums, mid+1, end, auxiliaryArray);
sum += mergeArrayAndCountReversePairs(nums, begin, mid, end, auxiliaryArray);
}
return sum;
}
private int mergeArrayAndCountReversePairs(int[] nums, int begin, int mid, int end, int[] auxiliaryArray){
int leftPointer = begin, rightPointer = mid + 1;
int auxIndex = 0;
int sum = 0;
while(leftPointer <= mid && rightPointer <= end){
if(nums[leftPointer] <= nums[rightPointer]){
auxiliaryArray[auxIndex++] = nums[leftPointer++];
}else{
auxiliaryArray[auxIndex++] = nums[rightPointer++];
sum += mid + 1 - leftPointer;
}
}
while(leftPointer <= mid){
auxiliaryArray[auxIndex++] = nums[leftPointer++];
}
while(rightPointer <= end){
auxiliaryArray[auxIndex++] = nums[rightPointer++];
}
for(int i = 0; i < auxIndex; i++){
nums[begin + i] = auxiliaryArray[i];
}
return sum;
}
}