求逆序数 分治思想求解(基于归并排序改编而来)

求逆序数 分治思想求解

问题描述

给定一长度为n的序列,请求出该序列的逆序数。要求使用分治法实现。
输入

2,5,4,3,8,11

输出

逆序数:3

思想

 1.假如两个数字大小顺序是相反的,比如31,那么这两个数字就构成一组逆序数;
 2.假设有序列A(2,5,4,3,8,11),对于求该整个序列的逆序数,等价于求左右两边各一半每个序列的逆序数;
 3.因此,可以采用分治的方法将原问题缩减成规模更小的,具有相同性质的子问题;
 4.当问题分解到规模只有一个时,可直接解决该问题;
 5.当左右两边的子问题解决完之后,即可解决大的问题;


 而对于归并排序,容易知道,虽然整个序列在一趟归并排序之前是无序的,但是,左右两边的子序列是相对有序的,也就是左边的数都小于右边的数(假如按从小到大排序),那么此时,当我们在对两个序列进行归并排序的同时,便可以十分快速地计算出每个数字的逆序数,具体计算方法如下:
 对于序列low—(mid)—>high,比较左边的某个数字arr[i]与右边的某个数字arr[j]时,假如arr[i]>arr[j],那么这两个数字就构成一对逆序数,由于左边序列的数字都比右边序列的数字大,而arr[i]到arr[mid]之间的数又比arr[i],那么arr[i]后面的数字就更比当前的arr[j]大了,因此逆序数应该加mid-i+1.

注意!我们是在归并排序的基础上计算逆序数的,因此原本的归并排序还是需要进行,而且必须进行,否则就算出来不对的结果。

Code

请结合代码加深对该算法的实现的理解。

#include"stdio.h"
#include"iostream"
using namespace std;

#define Maxn 9999
// 归并排序
int t[Maxn];
void merge(int* arr,int low,int mid,int high)
{
	int i=low,j=mid+1,k=low;
	while(i<=mid&&j<=high)
	{
		if(arr[i]<arr[j])
			t[k++]=arr[i++];
		else
			t[k++] = arr[j++];
	}
	while(i<=mid)t[k++]=arr[i++];
	while(j<=high)t[k++]=arr[j++];
	for(i=low;i<=high;i++)
		arr[i] = t[i];
}
void mergeSort(int* arr,int i,int j)
{
	if(i<j)
	{
		int mid = (i+j)/2;
		mergeSort(arr,i,mid);
		mergeSort(arr,mid+1,j);
		merge(arr,i,mid,j);
	}
}

// 计算逆序数
int cnt=0;
void count(int*arr,int low,int mid,int high)
{
	int i=low,j=mid+1,k=low;
	while(i<=mid&&j<=high)
	{
		if(arr[i]<=arr[j])
			t[k++] = arr[i++];
		else
		{
			cout<< "cnt=" <<cnt<<endl;
			cnt += mid-i+1;
			t[k++] = arr[j++];
		}
	}
	while(i<=mid)t[k++] = arr[i++];
	while(j<=high)t[k++] = arr[j++];
	for(i=low;i<=high;i++)
		arr[i]=t[i];
}
void reverseNumber(int*arr,int i,int j)
{
	if(i<j)
	{
		int mid = (i+j)/2;
		reverseNumber(arr,i,mid);
		reverseNumber(arr,mid+1,j);
		count(arr,i,mid,j);
	}
}

void main()
{
	int arr[] = {2,5,4,3,8,11};
	int size = sizeof(arr)/4;
	int i;
//	mergeSort(arr,0,size-1);
	for(i=0;i<size;i++)
		cout<< arr[i] <<" ";
	reverseNumber(arr,0,size-1);
	cout<<endl;
	for(i=0;i<size;i++)
		cout<< arr[i] <<" ";
	cout<< "逆序数:" <<cnt<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
归并排序是一种分治算法,它的基本思想是将一个大问题分解成若干个小问题,然后递归地解决这些小问题,最后将它们合并成一个整体的解。在归并排序中,我们可以通过在合并两个有序数组的过程中统计逆序对的数量来求解逆序对。 具体来说,我们可以将原数组分成左右两个子数组,然后递归地对左右两个子数组进行排序,并统计左右两个子数组内部的逆序对数量。接着,我们再将左右两个子数组合并成一个有序数组,并统计左右两个子数组之间的逆序对数量。最后,我们将左右两个子数组的逆序对数量加起来,就得到了原数组的逆序对数量。 下面是使用归并排序逆序对的代码实现: ```python def merge_sort(arr): if len(arr) <= 1: return arr, 0 mid = len(arr) // 2 left, left_count = merge_sort(arr[:mid]) right, right_count = merge_sort(arr[mid:]) merged, merge_count = merge(left, right) return merged, left_count + right_count + merge_count def merge(left, right): i, j = 0, 0 count = 0 merged = [] while i < len(left) and j < len(right): if left[i] <= right[j]: merged.append(left[i]) i += 1 else: merged.append(right[j]) j += 1 count += len(left) - i merged += left[i:] merged += right[j:] return merged, count ``` 其中,`merge_sort` 函数用于对数组进行归并排序,并返回排序后的数组和逆序对数量。`merge` 函数用于合并两个有序数组,并统计它们之间的逆序对数量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值