歸併法排序和逆序數問題(分治)

一、歸併排序(分治法)
分治法,顧名思義,先“分”後“治”。
分——分解子問題,直到能在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;
}碼片

運行結果:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章