归并排序&求逆序对数

归并排序:

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

 

分治策略基本思想:将原问题划分成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-1
		
		printf("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 ; //notice
		MergeSort(/*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 ; //notice
	nRight = r - q ; //notice

	for(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-1
		
		printf("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 ; //notice
		MergeSort(/*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 ; //notice
	nRight = r - q ; //notice

	for(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 ;  //here
			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] ;
		}
	}
}


 

练习题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 heap
	i <- n ;
	j <- 0 ;

	nHeapSize <- k ;

	while i > 0
		do 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 ;			  
}


 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值