一、歸併排序(分治法)
分治法,顧名思義,先“分”後“治”。
分——分解子問題,直到能在O(1)時間複雜度解決,歸併排序問題中即是將一個元素數目爲N的序列分成單個元素(或者視爲單個元素對象),所耗費的時間爲log2 N。
數組[49, 38, 65, 97, 76, 13 ,27]
分解爲下面初始序列:
治——歸併排序,實時更新子序列排序狀況,時間複雜度爲N。
二、逆序對
逆序對:對於給定的一段正整數序列,逆序對就是序列中 ai>aj 且 i<j 的有序對。
逆序數:一個排列所擁有逆序對的個數即爲該排列的逆序數。
解題思路:在歸併排序的基礎上尋找逆序對;在每次合併之前,先計算逆序對數,左右兩組數此時皆是從小到大的順序排列。若左邊第一個數大於右邊第一個數,則左邊剩下的數皆可和右邊第一個數形成逆序對;此時繼續看右邊第二個數,比較左邊第一個數和右邊第二個數,以此類推。若左邊第一個數小於右邊第一個數,則比較左邊第二個數和右邊第一個數,如上述同樣方式類推。
#include<bits/stdc++.h>
using namespace std;
int sum = 0;//記錄逆序對的個數
void MergeAndCount(int* num,int* result, int start, int end){
int Lmark = start;
int mid = (start + end)/2;
int Rmark = mid;
int rltmark = start;
int temp1 = Lmark;
int temp2 = Rmark;
//基於現有排序計算逆序對的個數
while(temp1<mid && temp2<end){
if(num[temp1] > num[temp2]){
sum+= (mid-temp1);
temp2++;
}
else{
temp1++;
}
}
//執行下一輪排序
while(Lmark<mid && Rmark<end){
if(num[Lmark] >= num[Rmark]){
result[rltmark] = num[Rmark];
rltmark++;
Rmark++;
}
else{
result[rltmark] = num[Lmark];
rltmark++;
Lmark++;
}
}
while(Lmark<mid){
result[rltmark] = num[Lmark];
rltmark++;
Lmark++;
}
while(Rmark<end){
result[rltmark] = num[Rmark];
rltmark++;
Rmark++;
}
}
void mergeSort(int* num,int* result, int start, int end){
if(end - start == 1)//只有1個元素
return;
else{
mergeSort(num, result, start, (start+end)/2);
mergeSort(num, result,(start+end)/2,end);
MergeAndCount(num,result,start,end);
for(int i=start; i<end; i++)
num[i] = result[i];
}
}
int main()
{
int n;
cout<<"序列所含元素數目:"<<endl;
cin>>n;
int *num = new int[n];
int *result = new int[n];
cout<<"輸入元素:"<<endl;
for(int i=0;i<n;i++)
cin>>num[i];
mergeSort(num,result,0,n);
cout<<"排序後的序列:"<<endl;
for(int i=0;i<n;i++)
cout<<num[i]<<" ";
cout<<endl;
cout<<"該序列的逆序數:"<<endl;
cout<<sum<<endl;
return 0;
}碼片
運行結果: