1. 程式人生 > >劍指Offer面試題36(Java版):陣列中的逆序對

劍指Offer面試題36(Java版):陣列中的逆序對

題目:在陣列中的兩個數字如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個陣列中的逆序對的總數

例如在陣列{7,5,6,4}中,一共存在5對逆序對,分別是{7,6},{7,5},{7,4},{6,4},{5,4}。

看到這個題目,我們的第一反應就是順序掃描整個陣列。每掃描到一個數組的時候,逐個比較該數字和它後面的數字的大小。如果後面的數字比它小,則這兩個數字就組成一個逆序對。假設陣列中含有n個數字。由於每個數字都要和O(n)個數字做比較,因此這個演算法的時間複雜度為O(n2)。我們嘗試找找更快的演算法。

我們以陣列{7,5,6,4}為例來分析統計逆序對的過程,每次掃描到一個數字的時候,我們不能拿它和後面的每一個數字做比較,否則時間複雜度就是O(n2)因此我們可以考慮先比較兩個相鄰的數字。

如下圖所示,我們先把陣列分解稱兩個長度為2的子陣列,再把這兩個子陣列分別茶城兩個長度為1的子陣列。接下來一邊合併相鄰的子陣列,一邊統計逆序對的數目。在第一對長度為1的子陣列{7},{5}中7大於5,因此{7,5}組成一個逆序對。同樣在第二對長度為1的子陣列{6},{4}中也有逆序對{6,4}。由於我們已經統計了這兩隊子陣列內部逆序對,因此需要把這兩對子陣列排序,以免在以後的統計過程中再重複統計。


接下來我們統計兩個長度為2的子陣列之間的逆序對。

我們先用兩個指標分別指向兩個子陣列的末尾,並每次比較兩個指標指向的數字。如果第一個子陣列中的數字大於第二個子陣列中的數字,則構成逆序對,並且逆序對的數目等於第二個子陣列中的剩餘數字的個數。如果第一個陣列中的數字小於或等於第二個陣列中的數字,則不構成逆序對。每一次比較的時候,我們都把較大的數字從後往前複製到一個輔助陣列中去,確保輔助陣列中的數字是遞增排序的。在把較大的數字複製到陣列之後,把對應的指標向前移動一位,接著來進行下一輪的比較。

經過前面詳細的討論,我們可疑總結出統計逆序對的過程:先把陣列分隔成子陣列,先統計出子陣列內部的逆序對的數目,然後再統計出兩個相鄰子陣列之間的逆序對的數目。在統計逆序對的過程中,還需要對陣列進行排序。如果對排序演算法很熟悉,我們不難發現這個排序的過程就是歸併排序。

用Java程式碼實現上面的過程

/**
 * 逆序對:
 * 在陣列中的兩個數字如果前面一個數字大於後面一個數字,則這兩個數字組成一個逆序對。
 * 輸入一個數組,求出這個陣列中的逆序對的總數。
 */
package swordForOffer;

import java.util.ArrayList;

/**
 * @author JInShuangQi
 *
 * 2015年8月9日
 */
public class E36InversePairs {
	
	private ArrayList<Integer> assignList(ArrayList<Integer> list ,int start,int end){
		ArrayList<Integer> des = new ArrayList<Integer>();
		for(int i = start;i<end;i++){
			des.add(list.get(i));
		}
		return des;
	}
	public long mergeTwoList(ArrayList<Integer> list,int start,int half,int end){
		long count = 0;
		ArrayList<Integer> tempLeft = assignList(list,start,half);
		ArrayList<Integer> tempRight = assignList(list,half,end);
		int leftIndex = 0;
		int rightIndex = 0;
		int index = start;
		while(leftIndex < tempLeft.size() && rightIndex <tempRight.size()){
			int temp1 = tempLeft.get(leftIndex);
			int temp2 = tempRight.get(rightIndex);
			if(temp1 > temp2){
				count+=tempLeft.size() - leftIndex;
				list.set(index, temp2);
				index++;
				rightIndex++;
			}else{
				list.set(index, temp1);
				index++;
				leftIndex++;
			}
		}
		for(;leftIndex < tempLeft.size();leftIndex++){
			list.set(index, tempLeft.get(leftIndex));
			index++;
		}
		for(;rightIndex <tempRight.size();rightIndex++){
			list.set(index, tempRight.get(rightIndex));
			index++;
		}
		return count;
	}
	public long getInversions(ArrayList<Integer> list,int start,int end){
		long count = 0;
		if((end-start)<= 1)
			return 0;
		int half = start+(end-start)/2;
		count += getInversions(list,start,half);
		count += getInversions(list,half,end);
		count += mergeTwoList(list,start,half,end);
		return count;
	}
	public long getInversePairs(int[] arr){
		ArrayList<Integer> al = new ArrayList<Integer>();
		for(int i = 0;i<arr.length;i++){
			al.add(arr[i]);
		}
		int end =arr.length;
		return getInversions(al,0,end);
	}
	public static void main(String[] args){
		int[] arr={7,5,6,4};
		E36InversePairs test = new E36InversePairs();
		System.out.println(test.getInversePairs(arr));
	}
}