剑指 Offer 51. 数组中的逆序对

本文介绍了一种解决LeetCode上逆序对问题的有效方法。通过对数组进行分治法处理,采用归并排序的思想,实现了O(nlogn)的时间复杂度。文中详细解释了如何在合并过程中计算逆序对的数量,并附带了C++代码实现。

题目链接: leetcode.

这种0 <= 数组长度 <= 50000的O(N^2)一般都会超时,下次不用试了

/*
暴力 超时 35 / 139 个通过测试用例 
*/
class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int N = nums.size();
        int ans = 0;
        for(int i = 0;i < N;++i)
        {
        	for(int j = 0;j < i;++j)
        	{
        		if(nums[j] > nums[i])
        			ans++;
        	}
        }
        return ans;
    }
};

分治法

  • 时间复杂度O(nlogn)
  • 空间复杂度O(n)

利用归并排序 merge(num, left, right)
1) 将其分解为递归调用 merge(num, left, mid)对左半边排序
2) 递归调用merge(num, mid+1, right) 对右半边排序
3) 合并各自有序的两边

在合并时,使用两个指针ij分别指向左右两边需要合并的数 如果num[i] < num[j],把num[i]放入合并数组的正确位置
否则,num[j]放入正确位置,此时num[i]num[mid]的数都比num[j]大, 其位置也在num[j]之前,所以共会构成(mid - i + 1)个逆序对

/*
执行用时:420 ms, 在所有 C++ 提交中击败了25.90%的用户
内存消耗:106.1 MB, 在所有 C++ 提交中击败了19.80%的用户
*/ 
class Solution {
public:
	int mergee(vector<int>& nums, int left, int right)
	{
		if(left >= right) //如果相等(指向同一个数)或大于(0,-1)返回0 
			return 0;
		int mid = left + (right - left) / 2;
		//左半边包含的逆序对 
		int cnt_left = mergee(nums, left, mid);
		//右半边包含的逆序对 
		int cnt_right = mergee(nums, mid + 1, right);
		//合并
		vector<int> tmp(right - left + 1);//临时数组存放合并后的正确顺序 
		int i = left, j = mid + 1;
		int cnt = 0;
		for(int k = 0;k < right - left + 1;++k)
		{
			//边界判断,i,j不能超出范围
			if(i == mid + 1)//左边合并完毕,只剩右边 
			{
				tmp[k] = nums[j];
				j++;
			} 
			else if(j == right + 1)//右边合并完毕,只剩左边
			{
				tmp[k] = nums[i];
				i++;
			} 
			//边界合法,才进行比较 
			else if(nums[i] <= nums[j])//用等于,保证稳定排序 
			{
				tmp[k] = nums[i];
				i++;
			}
			else
			{
				tmp[k] = nums[j];
				j++;
				cnt += mid - i + 1;
			}
		} 
		//将合并好的数组覆盖nums
		for(int i = 0, j = left;j <= right;++i,++j)
		{
			nums[j] = tmp[i];
		} 
		//返回逆序对个数 左边+右边+合并时两者中间出现的
		return cnt_left + cnt_right + cnt; 
	} 
    int reversePairs(vector<int>& nums) {
        int N = nums.size();
        return mergee(nums, 0, N - 1);
    }
}; 

还有树状数组的解法,没看,下次遇到再研究吧orz

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值