首页 > 递归/归并:count of smaller numbers求逆序数

递归/归并:count of smaller numbers求逆序数

已知数组nums,求新数组count,count[i]代表了在nums[i]右侧且比 nums[i]小的元素个数。

例如:

nums = [5, 2, 6, 1], count = [2, 1, 1, 0];

nums = [6, 6, 6, 1, 1, 1], count = [3, 3, 3, 0, 0, 0];

nums = [5, -7, 9, 1, 3, 5, -2, 1], count = [5, 0, 5, 1, 2, 2, 0, 0];

这个问题的寻找过程如下:

对于nums数组[5,2,6,1]中:

对于nums[0]=5,其后面比他小的元素有两个,则count[0]=2

对于nums[1]=2,其后面比她小的元素有一个,则count[1]=1

此时,一个比较普通但是时间复杂度较高(O(n^2))的算法实现即为先排序,再一个一个元素遍历,构造count数组。

但是我们直到分治算法的过程时间复杂度为(O(nlogn)),那可不可以尝试一下使用归并的过程构建count数组。

分治算法的步骤:

  1. 分解,将要解决的问题划分成若 干规模较小的同类问题;
  2. 求解,当子问题划分得足够小时 ,用较简单的方法解决;
  3. 合并,按原问题的要求,将子问题 的解逐层合并构成原问题的解。

分解:

我们要构造count数组,假如这个数组 被分为两个有序的数组

nums: [-7 1 5 9],[-2 1 3 5]

假设:

nums1[-7 1 5 9], 下标从i开始

nums2[-2 1 3 5], 下标从j开始

进行合并两个有序链表的过程:

i =0, j= 0, nums[i] = -7时,count[i] = 0

num1[i] < nums2[j], i++

对于i=1,j=0,count[i] = 1

nums1[i] > nums2[j], j++

此时i=1,j=1,count[i] = 1

综合上面的过程,我们能够发现,对于count1[i] ,它总是等于同一时刻j的数值

即count[0] = 0, count[1] =1, count[2] = 3;

此时我们能够对以上逆序数组通过归并排序进行构造:

/*pair first元素保存当前数组的值,second元素保存当前元素的下标*/void merge_two_arr(std::vector<pair<int,int>> &arr1, std::vector<pair<int,int>> &arr2, std::vector<pair<int,int>> &result,std::vector<int> &count) { int i = 0;int j = 0;while(i < arr1.size() && j < arr2.size()) { if (arr1[i].first <= arr2[j].first) {  //归并的筛选条件/*对于此时的arr[i]的元素,它的count值为上一个j所代表的数值因为之前的arr2[j]以及比它小的元素肯定比当前的arr[i]小。*/count[arr1[i].second] += j; result.push_back(arr1[i]);i++;} else { result.push_back(arr2[j]);j++;}}if (i == arr1.size()) { for (;j < arr2.size(); ++j) { result.push_back(arr2[j]);}} else { //对于剩余的元素做同样的处理for (;i < arr1.size(); ++i) { count[arr1[i].second] += j;result.push_back(arr1[i]);}}
}

测试代码如下:

#include 
#include 
#include using namespace std;/*
已知数组nums,求新数组count,count[i]代表了在nums[i]右侧且比 nums[i]小的元素个数。
例如:
nums = [5, 2, 6, 1], count = [2, 1, 1, 0];
nums = [6, 6, 6, 1, 1, 1], count = [3, 3, 3, 0, 0, 0];
nums = [5, -7, 9, 1, 3, 5, -2, 1], count = [5, 0, 5, 1, 2, 2, 0, 0];
*//**/
void merge_two_arr(std::vector<pair<int,int>> &arr1,std::vector<pair<int,int>> &arr2,std::vector<pair<int,int>> &result,std::vector<int> &count) { int i = 0;int j = 0;while(i < arr1.size() && j < arr2.size()) { if (arr1[i].first <= arr2[j].first) { count[arr1[i].second] += j;result.push_back(arr1[i]);i++;} else { result.push_back(arr2[j]);j++;}}if (i == arr1.size()) { for (;j < arr2.size(); ++j) { result.push_back(arr2[j]);}} else { for (;i < arr1.size(); ++i) { count[arr1[i].second] += j;result.push_back(arr1[i]);}}
}/*归并排序*/
void merge_sort(std::vector<pair<int,int>> &arr, std::vector<int> &count) { if (arr.size() < 2) { return ;}int mid = arr.size() / 2;std::vector<pair<int,int>> arr1;std::vector<pair<int,int>> arr2;for (int i = 0; i < mid; ++i) { arr1.push_back(arr[i]);}for (int i = mid; i< arr.size(); ++i){ arr2.push_back(arr[i]);}/*分治*/merge_sort(arr1, count);merge_sort(arr2, count);arr.clear();/*求解*/merge_two_arr(arr1,arr2,arr,count);
}int main(int argc, char const *argv[])
{ std::vector<pair<int,int>> arr;std::vector<int> count;int n;int tmp;cin >> n;for (int i = 0;i < n; ++i) { cin >> tmp;arr.push_back(make_pair(tmp,i));count.push_back(0);}merge_sort(arr, count);for (int i = 0;i < count.size(); ++i) { cout << count[i] << " ";}cout << endl;return 0;
}

最终输出如下:

#输入
8
5 -7 9 1 3 5 -2 1
#输出
5 0 5 1 2 2 0 0 

更多相关:

  •         Apache POI是一个开源的利用Java读写Excel,WORD等微软OLE2组件文档的项目。        我的需求是对Excel的数据进行导入或将数据以Excel的形式导出。先上简单的测试代码:package com.xing.studyTest.poi;import java.io.FileInputSt...

  • 要取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 要取得a到b之间的...

  • 利用本征图像分解(Intrinsic Image Decomposition)算法,将图像分解为shading(illumination) image 和 reflectance(albedo) image,计算图像的reflectance image。 Reflectance Image 是指在变化的光照条件下能够维持不变的图像部分...

  • 题目:面试题39. 数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2 限制: 1 <= 数组长度 <= 50000 解题: cl...

  • 题目:二叉搜索树的后序遍历序列 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树:      5     /    2   6   /  1   3示例 1: 输入: [1,6,3,2,5] 输出...

  • 如何使用Python快速高效地统计出大文件的总行数, 下面是一些实现方法和性能的比较。1.readline读所有行使用readlines方法读取所有行:def readline_count(file_name):return len(open(file_name).readlines())2.依次读取每行依次读取文件每行内容进行计数:...

  • 关于逆序数的问题描述如下: 已知数组nums,求新数组count,count[i]代表了在nums[i]右侧且比 nums[i]小的元素个数。 例如: nums = [5, 2, 6, 1], count = [2, 1, 1, 0]; nums = [6, 6, 6, 1, 1, 1], count = [3, 3, 3, 0,...

  • 题目 设计一个算法,计算出n阶乘中尾部零的个数 样例 11! = 39916800,因此应该返回 2   题解 一开始就用最简单对1-n找出5的个数,然后超时了。虽然都直到是要找5,因为2肯定比5多,所以5的个数就是0的个数,只是计算方法得简单明了。既然1-n里5的个数就是0,我们就看看规律。5 10 15 。。。n 那n/...

  • EditText 限定中文8个英文16个的解决方法。 在EditText上控件提供的属性中有限定最大最小长度的方法。可是,对于输入时,限定中文8个英文16个时,怎么办?相当于一个中文的长度是两个英文的长度。 原理就不说了。自己看一下android的源代码。 以上直接上代码。 private final int maxLen =...

  • /**172. Factorial Trailing Zeroes *2016-6-4 by Mingyang* 首先别忘了什么是factorial,就是阶乘。那么很容易想到需要统计* (2,5)对的个数,因为2×5=10。但是这个条件放松一下就会发现其实只要数5的个数就好了,* 因为2实在是比5要多的多。那么这道题目就转...

  • 题目:调整数组顺序使奇数位于偶数前面 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。 示例: 输入:nums = [1,2,3,4] 输出:[1,3,2,4] 注:[3,1,2,4] 也是正确的答案之一。 提示: 1 <= nums.length <=...

  • 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 示例: 给定 nums = [2, 7, 11, 15], target = 9 因为 nums[0] + nums[1]...

  • 题目描述: 给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。 示例 1: 输入: [2,3,-2,4] 输出: 6 解释: 子数组 [2,3] 有最大乘积 6。 示例 2: 输入: [-2,0,-1] 输出: 0 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。 该题目...

  • 题目描述如下: 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 示例: 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 方法一(暴力求解): 针对数组中的每一个元素,穷举所有的可能性,...

  • WinForm绘制带有升序、降序的柱形图   private void HuiZhiTu( string strPaiXu){//初始数据int[] nums = { 150, 89, 200, 60, 70, 90 };if (strPaiXu == "升序"){//冒泡排序for (int i = 0; i <...