利用归并排序求有限序列的逆序数

利用归并排序求有限序列的逆序数

逆序数对:在一个序列A={a1, a2, … , an}中,满足i<j, ai>aj­­的一对元素称为一个逆序数对。

那么,怎么求出一个序列的逆序数对的个数呢?

最简单的方法就是顺序扫描序列,求出每个元素的逆序数对,累加即可,时间复杂度是O(n2)。下面我介绍一种时间复杂度是O(nlgn)(注:lgn=log2n)的算法,它由归并排序修改而来。

在归并排序的合并过程中,左半部分元素的位置在原序列中的位置总是小于右半部分的元素,而且各个元素的相对位置在合并之前不会改变,因此利用合并的过程就可以计算出逆序数对的个数。具体做法如下,当左半部分的某个元素ai被合并时,那么右半部分中与ai比较的那个元素设为bj,则右半部分bj之前的那些元素肯定比ai小,因此形成j个逆序数对,因此总逆序数对累加j,当归并排序完成时,即可得出结果。

程序如下:

#include <iostream>

using namespace std;

//合并有序序列A[p,...,q]和A[q+1,...,r],求取A[p,...,r]区间内的逆序数
int update(int *A, int p, int q, int r)
{
	int *merge = new int[r-p+1];
	if (!merge)
		return 0;
    int i=p, j=q+1, k=0, c=0;
    while(i<=q && j<=r)
    {
        if(A[i] <= A[j])
        {
			merge[k++] = A[i++];
            c += j-q-1; //A[i]大于A[q+1,...,j-1],因此c增加j-q-1
        }
        else
        {
			merge[k++] = A[j++];
        }
    }

    while(i<=q)//左半部分的元素没有合并完,则继续更新c
    {
		merge[k++] = A[i++];
        c += j-q-1; //A[i]大于A[q+1,...,j-1],因此c增加j-q-1
    }
	while(j<=r)
		merge[k++] = A[j++];

	k=0;
	while(p<=r)
	{
		A[p++] = merge[k++];
	}
	delete []merge;
	return c;
}

//求A中p~r区间里逆序数对的个数,用参数c返回结果
int  nixushu(int *A, int p, int r)
{
	int count = 0;
    if(p < r)
    {
        int q = (p+r)/2;
        count += nixushu(A, p, q);
        count += nixushu(A, q+1, r);
        count += update(A, p, q, r);
    }
	return count;
}

int nixushu(const int *A, int n)
{
	int *copyA = new int[n];
	if (!copyA)
		return 0;
	for (int i=0; i<n; i++)
		copyA[i] = A[i];
	int count = nixushu(copyA, 0, n-1);
	delete []copyA;
	return count;
}
int main()
{
	int A1[] = {0, 1, 2, 3, 4, 5, 6};
	int A2[] = {6, 5, 4, 3, 2, 1, 0};
	int A3[] = {4, 5, 2, 5, 6, 1, 7, 1};
	int c1 = nixushu(A1, 7);
	int c2 = nixushu(A2, 7);
	int c3 = nixushu(A3, 8);
	cout <<"A1的逆序数:" << c1 << endl;
	cout <<"A2的逆序数:" << c2 << endl;
	cout <<"A3的逆序数:" << c3 << endl;
	return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值