求逆序对的数目

本片文章旨在记录求逆序对数目的算法题,但涉及到之前写过另一道求逆序数的Leetcode算法题,则第一部分为之前写过的LeetCode [315],第二部分为求逆序对数目题目。(第二部分其实为第一部分改动而来,原理在第一部分介绍)

一、LeetCode [315] 计算右侧小于当前元素的个数(求逆序数)

题目描述

给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。

示例

输入:nums = [5,2,6,1]
输出:[2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素

算法思路

分治法的归并排序

这道题的重要思路是由归并排序扩展开的,先补充一下归并排序

#include<iostream>
#include<vector>
using namespace std;

// 把两个已排序的数组有序合成
void merge_sort_two_vec(vector<int>& sub_vec1, vector<int>& sub_vec2, vector<int>& vec) {
	int i = 0;
	int j = 0;
	while (i < sub_vec1.size() && j < sub_vec2.size())
	{
		if (sub_vec1[i] <= sub_vec2[j]) {
			vec.push_back(sub_vec1[i]);
			i++;
		}
		else
		{
			vec.push_back(sub_vec2[j]);
			j++;
		}
	}
	for (; i < sub_vec1.size(); i++)
	{
		vec.push_back(sub_vec1[i]);
	}
	for (; j < sub_vec2.size(); j++)
	{
		vec.push_back(sub_vec2[j]);
	}
}

// 归并排序
void merge_sort(vector<int>& vec) {
	
	// 只有一个数则直接返回
	// 即在递归中把问题分解到足够小时
	if (vec.size() < 2) { 
		return;
	}
	int mid = vec.size() / 2;
	vector<int> sub_vec1;
	vector<int> sub_vec2;
	for (int i = 0; i < mid; i++)
	{
		sub_vec1.push_back(vec[i]);
	}
	for (int i = mid; i < vec.size(); i++) {
		sub_vec2.push_back(vec[i]);
	}
	merge_sort(sub_vec1);
	merge_sort(sub_vec2);
	vec.clear();
	merge_sort_two_vec(sub_vec1, sub_vec2, vec);
}

int main()
{
	int nums[] = { -3,9,4,10,23,5,11,-6,21,-7 };
	vector<int> vec;
	for (int i = 0; i < 10; i++)
	{
		vec.push_back(nums[i]);
	}
	merge_sort(vec);
	for (int i = 0; i < 10; i++)
	{
		cout << vec[i] << " ";
	}
	return 0;
}

算法思路

在这里插入图片描述

图片来自小象学院教程

每次有两个已经排序好的两个数组,按上面归并排序第一个函数(把两个已排序的数组有序合成)的算法加入新的数组,而其中第一个数组中的count即为j的值(i为第一个数组的索引,j为第二个数组的索引)。

算法代码

class Solution {
public:
	vector<int> countSmaller(vector<int>& nums) {
		vector<pair<int, int>> vec;
		vector<int> count;
		for (int i = 0; i < nums.size(); i++)
		{
			vec.push_back(make_pair(nums[i], i));
			count.push_back(0); // 将nums[i]与 i 绑定为 pair
		}
		merge_sort(vec, count);
		return count;
	}
private:
	void merge_sort_two_vec(
		vector<pair<int, int>>& sub_vec1,
		vector<pair<int, int>>& sub_vec2,
		vector<pair<int, int>>& vec,
		vector<int>& count
	) {
		int i = 0;
		int j = 0;
		while (i < sub_vec1.size() && j < sub_vec2.size())
		{
			if (sub_vec1[i].first <= sub_vec2[j].first) { // 比较用pair的第一个元素
				count[sub_vec1[i].second] += j; // 第二个元素缩影用来记录count
				vec.push_back(sub_vec1[i]);
				i++;
			}
			else
			{
				vec.push_back(sub_vec2[j]);
				j++;
			}
		}
		for (; i < sub_vec1.size(); i++)
		{
			count[sub_vec1[i].second] += j;
			vec.push_back(sub_vec1[i]);
		}
		for (; j < sub_vec2.size(); j++)
		{
			vec.push_back(sub_vec2[j]);
		}

	}
	void merge_sort(vector<pair<int, int>>& vec, vector<int>& count) {
		// 只有一个数则直接返回
		// 即在递归中把问题分解到足够小时
		if (vec.size() < 2) {
			return;
		}
		int mid = vec.size() / 2;
		vector<pair<int, int>> sub_vec1;
		vector<pair<int, int>> sub_vec2;
		for (int i = 0; i < mid; i++)
		{
			sub_vec1.push_back(vec[i]);
		}
		for (int i = mid; i < vec.size(); i++) {
			sub_vec2.push_back(vec[i]);
		}
		merge_sort(sub_vec1,count);
		merge_sort(sub_vec2, count);
		vec.clear();
		merge_sort_two_vec(sub_vec1, sub_vec2, vec, count);

	}
};

ps: 小象学院教程 https://www.bilibili.com/video/BV1GW411Q77S?t=7029&p=2

二、求逆序对数目

统计一个数组中的“逆序对”的数目

给定一个数组,使用分治法,统计数组内的逆序对的数目。

逆序对是指,数组内序号较小位置的元素数值大于序号较大位置的元素数值

例如输入 3 5 2 4 6
输出 3
因为 3 2 构成了一个逆序对; 5 2构成一个逆序对; 5 4 构成一个逆序对
所以总共有3个。

分治算法思路

基于归并排序算法,
普通的归并排序中设计两个函数,第一个是对两个有序的数组进行有序合并;第二个是对整个数组做排序。大致操作是将一个数组分为两部分,然后把这两部分进行调用自身的排序,然后将这两个数组有序合并,即为分治的递归算法。

求逆序对数目算法思路如下:

  1. 在普通的归并排序算法中,在合并两个有序数组(vec1,vec2)时,我们设定两个索引值i和j,i索引第一个数组,j索引第二个数组,定义一个count记录逆序对个数;
  2. 在我们的合并数组函数中,有一个操作是将vec1[i] 和 vec2[j] 中将小的数放入一个新的合并最终数组里,在这个时候如果是vec1[i] <= vec2[j],下一步要把vec的元素加入新合并数组时,此时的索引j即为比vec1[i]这个数小但排在其后面的数的个数,即我们所求的逆序对个数,将其累积到count,归并完全后就能得到总的逆序对个数count。

代码:

#include<iostream>
#include<vector>
using namespace std;

class Solution {
public:
	int reversePairs(vector<int>& nums) {
		int count = 0;
		merge_sort(nums, count);
		return count;
	}
private:
	// 有序合并两个有序数组
	void merge_sort_two_vec(
		vector<int>& sub_vec1,
		vector<int>& sub_vec2,
		vector<int>& vec,
		int& count
	) {
		int i = 0;
		int j = 0;
		while (i < sub_vec1.size() && j < sub_vec2.size())
		{
			if (sub_vec1[i] <= sub_vec2[j]) {
				count += j; // 每次放入 sub_vec1[i] 记录count
				vec.push_back(sub_vec1[i]);
				i++;
			}
			else
			{
				vec.push_back(sub_vec2[j]);
				j++;
			}
		}
		for (; i < sub_vec1.size(); i++)
		{
			count += j;
			vec.push_back(sub_vec1[i]);
		}
		for (; j < sub_vec2.size(); j++)
		{
			vec.push_back(sub_vec2[j]);
		}

	}
	void merge_sort(vector<int>& vec, int& count) {
		// 只有一个数则直接返回
		// 即在递归中把问题分解到足够小时
		if (vec.size() < 2) {
			return;
		}
		int mid = vec.size() / 2;
		vector<int> sub_vec1;
		vector<int> sub_vec2;
		for (int i = 0; i < mid; i++)
		{
			sub_vec1.push_back(vec[i]);
		}
		for (int i = mid; i < vec.size(); i++) {
			sub_vec2.push_back(vec[i]);
		}
		merge_sort(sub_vec1, count);
		merge_sort(sub_vec2, count);
		vec.clear();
		merge_sort_two_vec(sub_vec1, sub_vec2, vec, count);

	}
};
int main()
{
	Solution so;
	vector<int> nums;
	int temp;
	do
	{
		cin >> temp;
		nums.push_back(temp);
	} while (getchar() != '\n');
	cout << so.reversePairs(nums);
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值