归并排序&求逆序对数

来源:互联网 发布:ios开发中的数据库 编辑:程序博客网 时间:2024/05/04 23:20

归并排序:

基本原理:与插入排序使用的“增量”方法不同,归并排序使用另外一种策略:分治法

 

分治策略基本思想:将原问题划分成n个规模较小而结构与原问题相似的小问题;递归地解决这些子问题,然后再合并其结果,就得到原问题的解。

 

分治模式在每一层递归上都有三个步骤;

分解(Divide):将原问题分解成这一系列子问题。

解决(Conquer):递归地解各子问题。若子问题足够小,则直接求解。

合并(Combine):将子问题的结果合并成原问题的解。

 

合并排序算法完全依照了上述模式,直观地操作如下:

分解:将n个元素分成各含n/2个元素的子序列;

解决:用合并排序法对两个子序列递归地排序;

合并:合并两个已排序的子序列以得到排序结果;

 

时间复杂度:O(nlgn)

 

代码实现:

#include<stdio.h>#define N 10 int A[N] ;void Merge(/*A*/int p,int q,int r) ;void MergeSort(/*A*/int p,int r) ;int main(void){int i,n ;freopen("in.txt","r",stdin) ;while(scanf("%d",&n) != EOF){for(i = 0 ; i < n ; i++){scanf("%d",&A[i]) ;}printf("You have input the List:\n") ;for(i = 0 ; i < n ; i++){printf("%-3d",A[i]) ;}printf("\n") ;MergeSort(/*A*/0,n-1) ; //not n but n-1printf("After Sort:\n") ;for(i = 0 ; i < n ; ++i){printf("%-3d",A[i]) ;}printf("\n\n") ;}return 0 ;}void MergeSort(/*A*/int p,int r) {int q = 0 ;if(p < r){q = p + (r-p)/2 ; //noticeMergeSort(/*A*/p,q) ;MergeSort(/*A*/q+1,r) ;Merge(/*A*/p,q,r) ;}}void Merge(/*A*/p,q,r) {int L[N], R[N] ;int i,j,k ;int nLeft,nRight ;nLeft = q + 1 - p ; //noticenRight = r - q ; //noticefor(i = 0 ; i < nLeft ; ++i ){L[i] = A[p+i] ;}for(j = 0 ; j < nRight ; ++j){R[j] = A[q+j+1] ;  //notice}i = 0 ;j = 0 ;for(k = p ; k < r && i<nLeft && j<nRight ; k++ ){if(L[i] <= R[j]){A[k] = L[i] ; i++ ;}else{A[k] = R[j] ; j++ ;}}if(i >= nLeft){for(; k <= r ; ++k,++j) {A[k] = R[j] ;}}else{for(; k <= r ; ++k,++i){A[k] = L[i] ;}}}


 

 

以上结论摘自《算法导论》

 

----------------------------------------------------------------------------

算法导论思考题:2-4逆序对

d)给出一个算法,它能用O(nlgn)的最坏情况运行时间,确定n个元素的任何排列中逆序对的数目。(提示:修改合并排序)

答:其实就是合并两个子序列时,出现右边元素小于左边元素的情况,亦即R[j]<L[i]时,出现逆序对。此时L[i+1...n1]里的元素均比R[j]大,而R[j]又在它们的后面。所以,此时的逆序对数:n1-i。后面的元素以此类推。

 

代码实现:

#include<stdio.h>#define N 10 int A[N] ;int nTotalInversion = 0 ;void Merge(/*A*/int p,int q,int r) ;void MergeSort(/*A*/int p,int r) ;int main(void){int i,n ;freopen("in.txt","r",stdin) ;while(scanf("%d",&n) != EOF){nTotalInversion = 0 ;for(i = 0 ; i < n ; i++){scanf("%d",&A[i]) ;}printf("You have input the List:\n") ;for(i = 0 ; i < n ; i++){printf("%-3d",A[i]) ;}printf("\n") ;MergeSort(/*A*/0,n-1) ; //not n but n-1printf("The Inversions are %d.\n\n",nTotalInversion) ;}return 0 ;}void MergeSort(/*A*/int p,int r) {int q = 0 ;if(p < r){q = p + (r-p)/2 ; //noticeMergeSort(/*A*/p,q) ;MergeSort(/*A*/q+1,r) ;Merge(/*A*/p,q,r) ;}}void Merge(/*A*/p,q,r) {int L[N], R[N] ;int i,j,k ;int nLeft,nRight ;nLeft = q + 1 - p ; //noticenRight = r - q ; //noticefor(i = 0 ; i < nLeft ; ++i ){L[i] = A[p+i] ;}for(j = 0 ; j < nRight ; ++j){R[j] = A[q+j+1] ;  //notice}i = 0 ;j = 0 ;for(k = p ; k < r && i<nLeft && j<nRight ; k++ ){if(L[i] <= R[j]){A[k] = L[i] ; i++ ;}else{nTotalInversion += nLeft - i ;  //hereA[k] = R[j] ; j++ ;}}if(i >= nLeft){for(; k <= r ; ++k,++j) {A[k] = R[j] ;}}else{for(; k <= r ; ++k,++i){A[k] = L[i] ;}}}


 

练习题6.5-8

大概思路:先利用每个链表的头元素来建立一个最大堆或者最大堆,然后再做类似堆排序的操作。但是这里要注意的是用到了额外空间数组B来储存排序后的元素。而且在交换树根和堆的最后一个元素之后,还要检查链表是否为空,从而更新heap-size[A]的值。时间复杂度:O(nlgk)

 

伪代码实现:

 K-Road-MegerSort(A,k,n){BuildMinHeap(A,k) ; //use the K List's first data to build the heapi <- n ;j <- 0 ;nHeapSize <- k ;while i > 0do B[j] <- A[1]->data ;   j <- j + 1 ;    if A[1]->next = NULL  //if the one List is empty.     then  exchange  A[1] <-> A[nHeapSize] ;     nHeapSize <- nHeapSize - 1 ;  MinHeapify(A,1) ;   else then  MinHeapify(A,1) ;    i <- i - 1 ;  }


 

原创粉丝点击