归并排序

归并排序求逆序对

         将里面reverseCount删除就可以作为归并排序代码。归并排序是稳定的。

#include <iostream>

using namespace std;

#define MAX_VALUE 0X7FFFFFFF

int Merge(int arr[],int start,int mid,int high)
{
	int i = start;
	int j = mid + 1;
	int k = start;
    int reverseCount = 0;
	int *temp = new int[high+1];//据说这种每次分配,非常浪费时间,所以顶层分配好传下来

    while(i<=mid&&j<=high){
		if(arr[i]<=arr[j]){
			temp[k++] = arr[i++];
		}else{
			temp[k++] = arr[j];
			reverseCount += mid - i + 1;
// 			for(int w=i;w<=mid;w++){
// 				cout<<"("<<arr[w]<<","<<arr[j]<<")"<<endl;
// 			}
			j++;
		}
	}
	while(i<=mid)temp[k++] = arr[i++];
	while(j<=high)temp[k++] = arr[j++];

	for(int i=start;i<=high;++i){
		arr[i] = temp[i];
	}

	delete[] temp;

	return reverseCount;
}

int Merge1(int arr[],int low,int mid,int high)
{
	int num1 = mid - low + 1;
	int num2 = high - mid;

	int *temp1 = new int[num1+1];
	int *temp2 = new int[num2+1];

	int reverseCount = 0;

	for(int i=0;i<num1;++i)temp1[i] = arr[i+low];
	temp1[num1] = MAX_VALUE;//哨兵节点
	for(int i=0;i<num2;++i)temp2[i] = arr[i+mid + 1];
	temp2[num2] = MAX_VALUE;//哨兵节点

	int index1 = 0;
	int index2 = 0;

	for(int k=low;k<=high;k++){//直到high,滤除哨兵节点
		if(temp1[index1] <= temp2[index2]){
			arr[k] = temp1[index1++];
		}else{
			arr[k] = temp2[index2++];
			reverseCount += num1 - index1;
		}
	}

	delete[] temp1;
	delete[] temp2;

	return reverseCount;

}

int MergeSort(int arr[],int start,int end)
{
	int reverseCount = 0;
	if(start<end){
   	    int mid = start + (end - start>>1);
		//左子数组逆序对
		reverseCount += MergeSort(arr,start,mid);
		//右子数组逆序对
		reverseCount += MergeSort(arr,mid+1,end);
		//数组与数组之间的逆序对
		reverseCount += Merge(arr,start,mid,end);
	}
	return reverseCount;
}

int main()
{
	int arr[]={1,3,1,8,2,4,6,5};
	for(int i=0;i<8;i++){
		cout<<arr[i]<<" ";
	}
	cout<<endl;
	cout<<"The reverse count:"<<MergeSort(arr,0,7)<<endl;
	for(int i=0;i<8;i++){
		cout<<arr[i]<<" ";
	}
	cout<<endl;
	system("pause");
	return 0;
}

下面的内容参考:http://blog.csdn.net/prstaxy/article/details/8166360

归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的,然后再把有序子序列合并为整体有序序列。

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

时间复杂度为O(nlogn),空间复杂度为 O(n),归并排序比较占用内存,但却效率高且是稳定的算法。

以下为递归实现的版本:

[cpp]  view plain copy
  1. #include<iostream>  
  2. using namespace std;  
  3. void Merge(int arr[],int low,int mid,int high)  
  4. {//low为第1有序区的第1个元素,i指向第1个元素, mid为第1有序区的最后1个元素  
  5.     int i=low,j=mid+1,k=0;  //mid+1为第2有序区第1个元素,j指向第1个元素   
  6.     int *temp=new int[high-low+1]; //temp数组暂存合并的有序序列  
  7.     if(!temp)//内存分配失败   
  8.     {  
  9.         cout<<"ERROR!";  
  10.         return;  
  11.     }  
  12.     while(i<=mid && j<=high)//顺序选取两个有序区的较小元素,存储到t数组中  
  13.     {  
  14.         if(arr[i]<=arr[j])//较小的先存入temp中  
  15.             temp[k++]=arr[i++];  
  16.         else  
  17.             temp[k++]=arr[j++];  
  18.     }  
  19.     while(i<=mid)//若比较完之后,第一个有序区仍有剩余,则直接复制到t数组中  
  20.         temp[k++]=arr[i++];  
  21.     while(j<=high)//同上  
  22.         temp[k++]=arr[j++];  
  23.     for(i=low,k=0;i<=high;i++,k++)//将排好序的存回arr中low到high这区间   
  24.         arr[i]=temp[k];  
  25.     delete []temp;//删除指针,由于指向的是数组,必须用delete []   
  26. }  
  27. void MergeSort(int arr[],int low,int high)   
  28. {//用递归应用二路归并函数实现排序——分治法  
  29.     if(low<high)//(是if,不是while!,且不含等号!否则死循环!)  
  30.     {  
  31.         int mid=(low+high)/2;  
  32.         MergeSort(arr,low,mid);  
  33.         MergeSort(arr,mid+1,high);  
  34.         Merge(arr,low,mid,high);  
  35.     }  
  36. }  
  37. int main()  
  38. {  
  39.     int x[]={0,5,-2,1,-8,7,6,-3};  
  40.     MergeSort(x,0,7);  
  41.     for(int i=0;i<8;i++)  
  42.     {  
  43.         cout<<x[i]<<" ";  
  44.     }  
  45.     return 0;  
  46. }  





参考 http://www.cnblogs.com/hackerain/archive/2011/01/07/2130424.html


非递归实现版本:

Merge函数和上面递归实现是完全一样的,MergeSort形参改成了待排序数组和数组大小。

[cpp]  view plain copy
  1. #include<iostream>  
  2. using namespace std;  
  3. void Merge(int arr[],int low,int mid,int high)//递归和非递归均一样   
  4. {//将两个有序区归并为一个有序区  
  5.     int i=low,j=mid+1,k=0;  
  6.     int *temp=new int[high-low+1];  
  7.     while(i<=mid&&j<=high)  
  8.     {  
  9.         if(arr[i]<=arr[j])  
  10.             temp[k++]=arr[i++];  
  11.         else  
  12.             temp[k++]=arr[j++];  
  13.     }  
  14.     while(i<=mid) temp[k++]=arr[i++];  
  15.     while(j<=high) temp[k++]=arr[j++];  
  16.     for(i=low,k=0;i<=high;i++,k++)  
  17.         arr[i]=temp[k];   
  18.     delete []temp;  
  19. }  
  20. //void MergeSort(int arr[],int low,int high)//递归版本的形参表   
  21. void MergeSort(int arr[],int n)//参数和递归略不同,n代表数组中元素个数,即数组最大下标是n-1   
  22. {//非递归实现   
  23.     /* 
  24.     int step = 1; 
  25.     while(step<n) //当元素个数不是2的幂时可能会出错,未考虑第2个序列个数不足的情况  
  26.     { 
  27.         for(int i=0;i<=n-step-1;i+=2*step) 
  28.             Merge(arr,i,i+step-1,i+2*step-1); 
  29.         step*=2; 
  30.     }*/  
  31.   
  32.     int size=1,low,mid,high;  
  33.     while(size<=n-1)  
  34.     {  
  35.         low=0;  
  36.         while(low+size<=n-1)  
  37.         {  
  38.             mid=low+size-1;  
  39.             high=mid+size;  
  40.             if(high>n-1)//第二个序列个数不足size   
  41.                 high=n-1;         
  42.             Merge(arr,low,mid,high);//调用归并子函数   
  43.             cout<<"low:"<<low<<" mid:"<<mid<<" high:"<<high<<endl;//打印出每次归并的区间   
  44.             low=high+1;//下一次归并时第一关序列的下界   
  45.         }  
  46.         size*=2;//范围扩大一倍   
  47.     }  
  48.       
  49. }  
  50. int main()  
  51. {  
  52.     int x[]={4,-5,0,3,-1,12,9,-7,8,-4,11};  
  53.     MergeSort(x, 11);  
  54.     for(int i=0;i<11;i++)  
  55.     {  
  56.         cout<<x[i]<<" ";  
  57.     }  
  58.     return 0;  
  59. }  


图中的low mid high输出的是每次调用Merge函数时传的3个参数,便于分析每次合并所在的区间。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值