逆序数的几种求法

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

求一个数列的逆序数

逆序对:数列a[1],a[2],a[3]…中的任意两个数a[i],a[j] (i<j),如果a[i]>a[j],那么我们就说这两个数构成了一个逆序对

逆序数:一个数列中逆序对的总数

如数列 3 5 4 8 2 6 9

(5,4)是一个逆序对,同样还有(3,2),(5,2),(4,2)等等

那么如何求得一个数列的逆序数呢?

方法1:一个一个的数

最简单也是最容易想到的方法就是,对于数列中的每一个数a[i],遍历数列中的数a[j](其中j<i),a[i]<a[j],则逆序数加1,这样就能统计出该数列的逆序数总和

该方法的时间复杂度为O(n^2),具体过程就不细说了

方法2:归并的思想

有一种排序的方法是归并排序,归并排序的主要思想是将整个序列分成两部分,分别递归将这两部分排好序之后,再和并为一个有序的序列,核心代码如下

MergeSort(first,last)

{

If(first==last)

return

Int med=(first+last)/2;

MergeSort(first,med);

MergeSort(med+1,last);

Merge(first,last);

}

在合并的过程中是将两个相邻并且有序的序列合并成一个有序序列,如以下两个有序序列

Seq13  4  5

Seq22  6  8  9

合并成一个有序序:

Seq2  3  4  5  6  8  9

对于序列seq1中的某个数a[i],序列seq2中的某个数a[j],如果a[i]<a[j],没有逆序数,如果a[i]>a[j],那么逆序数为seq1a[i]后边元素的个数(包括a[i]),即len1-i+1,

这样累加每次递归过程的逆序数,在完成整个递归过程之后,最后的累加和就是逆序的总数

实现代码如下:

const int LENGTH=100;
int temp[LENGTH];  //额外的辅助数组

int count=0;

void Merge(int * array,int first,int med,int last)
{
	int i=first,j=med+1;
	int cur=0;
	while (i<=med&&j<=last)
	{
		if (array[i]<array[j])
		{
			temp[cur++]=array[i++];
		}
		else
		{
			temp[cur++]=array[j++];
			count+=med-i+1;  //核心代码,逆序数增加
		}
	}
	while (i<=med)
	{
		temp[cur++]=array[i++];
	}
	while (j<=last)
	{
		temp[cur++]=array[j++];
	}
	for (int m=0;m<cur;m++)
	{
		array[first++]=temp[m++];
	}
}
void MergeSort(int *array,int first,int last)
{
	if (first==last)
	{
		return ;
	}
	int med=first+(last-first)/2;
	MergeSort(array,first,med);
	MergeSort(array,med+1,last);
	Merge(array,first,med,last);
}

归并排序的复杂度为O(nlogn),当然此方法的复杂度也为O(nlogn)

方法3:用树状数组

还是以刚才的序列

3  5  4  8  2  6  9

大体思路为:新建一个数组,将数组中每个元素置0

0  0  0  0  0  0  0

取数列中最大的元素,将该元素所在位置置1

0  0  0  0  0  0  1

统计该位置前放置元素的个数,为0

接着放第二大元素8,将第四个位置置1

0  0  0  1  0  0  1

统计该位置前放置元素的个数,为0

继续放第三大元素6,将第六个位置置1

0  0  0  1  0  1  1

统计该位置前放置元素的个数,为1

这样直到把最小元素放完,累加每次放元素是该元素前边已放元素的个数,这样就算出总的逆序数来了

在统计和计算每次放某个元素时,该元素前边已放元素的个数时如果一个一个地数,那么一趟复杂度为O(n),总共操作n趟,复杂度为O(n^2),和第一种方法的复杂度一样了,那我们为什么还用这么复杂的方法

当然,在每次统计的过程中用树状数组可以把每一趟计数个数的复杂度降为O(logn),这样整个复杂度就变为O(nlogn)

 

树状数组是一种很好的数据结构,这有一篇专门描述树状数组的文章

将序列中的每个数按照从大到小的顺序插入到树状数组中,给当前插入节点及其父节点的个数加1,然后统计该节点下边及右边放置元素的个数

具体代码如下:

//用树状数组求逆序数
const int Length=8;
struct Node
{
	int data;
	int pos;
};
int LowBit(int num)
{
	return num&(num^(num-1));
}
//自定义排序方法
int cmp(const void * num1,const void * num2)
{
	return ((Node*)num2)->data-((Node *)num1)->data;
}
//统计pos节点下方及右方节点元素的个数
int sum(int *TreeArray,int pos)
{
	int result=0;
	while (pos)
	{
		result+=TreeArray[pos];
		pos-=LowBit(pos);
	}
	return result;
}
//使pos位置的节点及其父节点的元素个数加1
void INC(int *TreeArray,int pos)
{
	while(pos<Length)
	{
		TreeArray[pos]++;
		pos+=LowBit(pos);
	}
}
void insertNum(int *TreeArray,Node * seq,int &reverseNum)
{
	for (int i=1;i<Length;i++)
	{
		reverseNum+=sum(TreeArray,seq[i].pos);  //累加逆序数
		INC(TreeArray,seq[i].pos);    //将该节点及父节点的数加1
	}
}
int main(int argc, char* argv[])
{
	int array[]={3,5,4,8,2,6,9};
	Node seq[Length];
	int TreeArray[Length];
	memset(TreeArray,0,sizeof(TreeArray));
	for (int i=1;i<Length+1;i++)
	{
		seq[i].data=array[i-1];
		seq[i].pos=i;
	}
	//从大到小排序
	qsort(seq+1,Length-1,sizeof(Node),cmp);
	int reverseNum=0;
	//边插入边计数
	insertNum(TreeArray,seq,reverseNum);
	cout<<reverseNum<<endl;
	return 0;
}


 

线性代数|逆序数 1。 阅读详情

相关推荐

逆序数的两种方式(归并排序+树状数组)

题目描述 猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。 最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai​>aj​且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有...

weixin_45177251的博客 1393

序列中逆序数的四大算法(倾心之作)(2024年851真题就是考了归并排序逆序数(>—<))

通过归并排序、树状数组、线段树、trie数的基本原理,来快速解一个序列中的逆序数

weixin_54442315的博客 1万+

逆序数的两种重要方法 (51nod逆序数

利用这个题来了解一下逆序对是神马http://www.51nod.com/Challenge/Problem.html#!#problemId=1019 解决逆序数问题有三种方法,但是第一种暴力解不赞成使用  所以这里不详细说明 那么第一种:分治法——归并排序逆序数  时间复杂度为O(nlogn) 归并排序基本思想:将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序...

奋斗的女孩儿 4631

逆序数的三种求法

目录 归并排序 树状数组 线段树 ACM题刷多了,逆序数应该都会了,今天就说一下逆序数的三种求法 归并排序 归并排序应该是用的最多的了,其思路为:        对某个序列进行归并,在前半部分和后半部分两段子序列进行归并时,比较两个子序列首元素中较小者。而当前半部分首元素大于后半部分时,就产生了逆序,子序列2的首元素下标减去已归并的元素数量就是该元素的逆序,代码如下: #inc...

Revenant_Di的博客 9375

几种逆序数问题(更新ing)

最近蒟蒻的我在上牛客的算法入门课,遇到了非常经典的逆序数问题,因此想写篇博客总结一下。 首先看一下逆序数的定义。(摘自百度百科) 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。 打个比方,一个序列 (4,5,1,3,2),其中有(4,1)、(4,3)、(4,2)、(5,1)、(5,3)、(5,2)、(3,2)七个逆序对,所以这个序列的逆序数为 7 。(概念还是很容易理解的) 了解完基本概念后,我们来看一个经典

hhhcbw的博客,欢迎各位来访 2548

神奇的逆序数

神奇的逆序数 2017-12-16 11:10 先说什么是逆序。比如有两个数2,5,如果从左到右按照从小到大的顺序排列,即25,那么,我们说这种排列是顺序排列。但如果反过来,5在左2在右,即小的数2排在了大的数5的后面,即52,我们就说52是逆序排列。好的,现在来看一看多个数的排列。比如有4个数1,2,3,4。显然,对排列1234,其中任意两个数的顺序都是顺序排列,没有逆序排列。而排列1324中,...

代码过客 790

逆序数几种方法

一个数列的逆序数 逆序对:数列a[1],a[2],a[3]…中的任意两个数a[i],a[j] (i,如果a[i]>a[j],那么我们就说这两个数构成了一个逆序对 逆序数:一个数列中逆序对的总数 如数列 3 5 4 8 2 6 9 (5,4)是一个逆序对,同样还有(3,2),(5,2),(4,2)等等 那么如何得一个数列的逆序数呢? 方法1:一个一个的数

my_acm 2837

全排列和逆序数

从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。 比如1、2、3这三个数的全排列就是 123  132  213  231  312  321 一共有6中排列方式,也就是3*2*1 = 3!种排列方式。 一般的,把n个不同的元素排成一列,共有几种不同的排法呢? 从n个元素中任取一个放在第一个位置

9153
上一篇: 质数(素数)的几种求法
下一篇: 如何选择到最好女孩
dlengong
博客等级 码龄18年 25粉丝 15原创
评论 10
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值