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;
}
}