相比樹狀數組求逆序對,歸並排序的邏輯復雜度稍微小一點。
首先我們來理解歸並排序。首先用mergeSort將一個序列不斷二分,直到每個子序列只有長度2
然后遞歸到了棧底。我們再用merge函數,將遞增有序的序列拼接起來。因為序列遞增有序,所有時間復雜度為O( max(m+n) ),這里的m、n分別是兩個序列的長度。加上二分,總的時間復雜度接近O(NlogN)
在拼接的過程中,用a、b分別記錄兩個序列的索引,然后不斷取其中最小的。最后用兩個while循環,將還滿足“未越界”的a或b循環變量遍歷完。最后,將臨時數組放到arr工作數組中。
然后討論怎樣用這個過程求逆序數:
假設我們有兩個遞增有序的序列A和B:
A:1 3 5
B:2 4
循環取數的順序是:1 2 3 4 5
加粗的表示取到了B序列。而這一過程中統計的逆序數的數目,就是A的循環下標a的右端數的數目。
這很好理解:3、5都比2(當前B序列的數)大,5比4(當前B序列的數)大
牢記以上原則,就可以編寫出求逆序對的代碼。
板子:
int arr[LEN]; int tmp[LEN]; int ans=0; void merge(int s,int mid,int e){ int i=0,a=s,b=mid+1,j=0; while(a<=mid && b<=e){ if(arr[a]<=arr[b]){ tmp[i++]=arr[a++]; }else{ tmp[i++]=arr[b++]; ans+=mid-a+1; } } while(a<=mid) tmp[i++]=arr[a++]; while(b<=e) tmp[i++]=arr[b++]; FF(j,i) arr[s+j]=tmp[j]; } void mergeSort(int s,int e){ if(s<e){ //能夠被划為。如果只有一個數就不能被划分 int mid=(s+e) /2; mergeSort(s,mid); mergeSort(mid+1,e); merge(s,mid,e); } }