19-求排列的逆序数

例题:求排列的逆序数

题目描述

考虑1,2,…,n (n <= 100000)的排列i1,i2,…,in,如果其中存在 j,k,满足j < k 且 ij > ik, 那么就称(ij,ik)是这个排列的一个逆序。一个排列含有逆序的个数称为这个排列的逆序数。例如排列2,6,3,4,5,1 含有8个逆序(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),因此该排列的逆序数就是8。
现给定1,2,…,n的一个排列,求它的逆序数。

问题分析

笨办法:O(n2)
分治O(nlogn):
1) 将数组分成两半,分别求出左半边的逆序数和右半边的逆序数
2) 再算有多少逆序是由左半边取一个数和右半边取一个数构成(O(n)实现)

  • 关键是左半边和右半边都是排好序的。比如,都是从小到大排序的。这样,左右半边只需要从头到尾各扫一遍,就可以找出由两边各取一个数构成的逆序个数

在这里插入图片描述

代码实现

#include <iostream>
using namespace std;
typedef int ElementType;
long long sum = 0;//逆序数的个数
void MergeSort(int  arr[], int tmp[],int L, int rightend);//归并排序
void MergeAndCountNum(int arr[], int tmp[], int L, int R, int rightend); //归并有序序列并计算逆序数的个数 

int main()
{
	int n;
	cin >> n;
	ElementType *a = (ElementType *)malloc(n*sizeof(ElementType));
	ElementType *tmp = (ElementType *)malloc(n*sizeof(ElementType));
	for (int i = 0; i < n; i++)
		cin >> a[i];
	MergeSort(a, tmp, 0, n - 1);
	cout << sum << endl;
	free(a);
	free(tmp);
	return 0;
}
//归并排序 
void MergeSort(int a[], int tmp[],int L, int rightend)
{
	if (L < rightend)
	{
		int center = L + (rightend - L) / 2;
		MergeSort(a, tmp, L, center);
		MergeSort(a, tmp, center+1, rightend);
		MergeAndCountNum(a, tmp, L, center + 1, rightend);
	}
}
//归并有序序列并计算逆序数的个数 
void MergeAndCountNum(int a[], int tmp[], int L, int R, int rightend)
{
	int leftend = R - 1;
	int i = L, j = R;
	int k = L;
	while (i <= leftend && j <= rightend)
	{
		if (a[i] < a[j])
			tmp[k++] = a[i++];
		else
		{
			tmp[k++] = a[j++];
			sum = sum + (leftend-i+1);
		}
	}
	while (i <= leftend)
		tmp[k++] = a[i++];
	while (j <= rightend)
		tmp[k++] = a[j++];
	for (i = L; i <= rightend; i++)
		a[i] = tmp[i];
}
在C++中,使用循环实现逆序排列有多种情况,以下针对不同数据类型分别介绍其实现方法: ### 一维数组逆序排列 对于一维数组,可以通过交换首尾元素逐步向中间靠拢的方式实现逆序排列。示例代码如下: ```cpp #include<iostream> using namespace std; int main() { // 定义一个一维数组 int arr[5] = { 1,3,2,5,4 }; // 起始元素下标 int start = 0; // 末尾元素下标 int end = (sizeof(arr) / sizeof(arr[0]) - 1); // 记录数组长度 int length = end + 1; cout << "逆置前的数组为:" << endl; for (int i = 0; i < length; i++) { cout << arr[i] << endl; } // 循环交换元素 while (start < end) { int temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; start++; end--; } cout << "逆置后的数组为: " << endl; for (int i = 0; i < length; i++) { cout << arr[i] << endl; } return 0; } ``` 在上述代码中,利用`while`循环,不断交换`start`和`end`位置的元素,然后`start`加1,`end`减1,直到`start`不小于`end`,从而实现数组的逆序排列[^5]。 ### 二维数组逆序排列 对于二维数组,以5*5的二维数组为例,可以通过嵌套`for`循环将最后面的数值与最前面的数值依次对换。示例代码如下: ```cpp #include <iostream> using namespace std; const int col = 5; int main() { int array[col][col] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15}, {16, 17, 18, 19, 20}, {21, 22, 23, 24, 25} }; // 逆序排列 for(int i = 0; i < col / 2; i++) { for(int j = 0; j < col; j++) { int temp = array[i][j]; array[i][j] = array[col - i - 1][col - j - 1]; array[col - i - 1][col - j - 1] = temp; } } // 输出逆序后的数组 for(int i = 0; i < col; i++) { for(int j = 0; j < col; j++) { cout << array[i][j] << " "; } cout << endl; } return 0; } ``` 上述代码使用嵌套`for`循环,外层循环控制行的交换,内层循环控制列的交换,实现了二维数组的逆序排列[^4]。 ### 字符串逆序排列 对于字符串,若不使用C函数库中的字符串操作函数,也可以使用循环实现逆序排列。示例代码如下: ```cpp #include <iostream> using namespace std; void reverseString(char arr[]) { int start = 0; int end = 0; // 计算字符串长度 while (arr[end] != '\0') { end++; } end--; // 交换元素 while (start < end) { char temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; start++; end--; } } int main() { char arr[] = "abcdef"; cout << "逆置前的字符串为:" << arr << endl; reverseString(arr); cout << "逆置后的字符串为:" << arr << endl; return 0; } ``` 此代码中,首先计算字符串的长度,然后使用`while`循环交换首尾字符,实现字符串的逆序排列[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值