深圳幻海软件技术有限公司 欢迎您!

【LeetCode】剑指 Offer(26)

2023-04-22

目录题目:剑指Offer51.数组中的逆序对-力扣(Leetcode)题目的接口:解题思路:代码:过啦!!!写在最后:题目:剑指Offer51.数组中的逆序对-力扣(Leetcode)题目的接口:classSolution{public:intreversePairs(vector<int&g

目录

题目:剑指 Offer 51. 数组中的逆序对 - 力扣(Leetcode)

题目的接口:

解题思路:

代码:

过啦!!!

写在最后:


题目:剑指 Offer 51. 数组中的逆序对 - 力扣(Leetcode)

题目的接口:

  1. class Solution {
  2. public:
  3. int reversePairs(vector<int>& nums) {
  4. }
  5. };

解题思路:

这一道题,我的思路是用双指针暴力求解,

但这个数组长度,O(N^2)的时间复杂度肯定是不可能把所有样例跑完,

看了大佬的思路,用的是归并排序,(如果不会归并排序,最好先去学一下)

我一开始看了很久没有搞明白为什么,

实际上,这个思路是利用的归并排序的一个特性,具体思路如下:

例: 数组 [ 7, 5, 2, 6, 0, 1, 5, 4 ]

我们就拿这个数组归并到最后一步的时候作为样例:

[ 2, 5, 6, 7 ] 和 [ 0, 1, 4, 5 ]

由于他们通过先前的归并已经是两个有序的数组,

而最后一步就两个数组每个元素比大小,然后尾插到临时数组上,最后再拷贝回来,

重点来了:

第一个数比大小 2 > 0 所以尾插 0 并让第二个数组的下标begin2++,

因为这两个是升序数组,2 > 0,也表明 2 之后的所有数都大于 0 ,

那么这里就有 4 ( mid + 1,也就是第一个数组的长度) 个逆序数对,

那么,当 2 和 4 比较,2 < 4 ,就尾插 2 进临时数组,让第一个数组下标begin1++,

这个时候,继续往下比较,5 > 4, 尾插 4 并让第二个数组的下标begin2++,

因为这两个是升序数组,5 > 4,也表明 5 之后的所有数都大于 4,

但是因为第一个数组已经是第二个数了,所以:

这里就有 3 ( mid + 1 - begin1 (也就是减去第一个数组的下标))个逆序数对。

综上所述,

我们只需要在第一个数组的值 > 第二个数组的值的时候,记录逆序数对 (mid + 1 - begin1) 即可。

下面是代码:

代码:

  1. class Solution {
  2. public:
  3. //计数
  4. int res = 0;
  5. int reversePairs(vector<int>& nums) {
  6. //创建临时数组
  7. vector<int> tmp(nums.size());
  8. //归并排序,我用的是我学的归并排序模板
  9. merge_sort(nums, tmp, 0, nums.size() - 1);
  10. return res;
  11. }
  12. private:
  13. void merge_sort(vector<int>& nums, vector<int>& tmp, int begin, int end) {
  14. if(begin >= end) return; //返回
  15. //分治
  16. int mid = (begin + end) >> 1;
  17. merge_sort(nums, tmp, begin, mid);
  18. merge_sort(nums, tmp, mid + 1, end);
  19. //[begin][mid], [mid + 1][end]
  20. //两个数组的下标
  21. int begin1 = begin, end1 = mid;
  22. int begin2 = mid + 1, end2 = end;
  23. //临时数组的下标
  24. int i = begin;
  25. //比较大小之后尾插进临时数组
  26. while(begin1 <= end1 && begin2 <= end2) {
  27. if(nums[begin1] <= nums[begin2]) {
  28. tmp[i++] = nums[begin1++];
  29. }
  30. else {
  31. tmp[i++] = nums[begin2++];
  32. //计算这一段的逆序数对数量//具体推导看文章的文字
  33. res += mid + 1 - begin1; //整个思路的核心
  34. }
  35. }
  36. //将没有插完的值全部尾插进临时数组
  37. while(begin1 <= end1) tmp[i++] = nums[begin1++];
  38. while(begin2 <= end2) tmp[i++] = nums[begin2++];
  39. //将临时数组拷贝回原数组
  40. for(int i = begin; i <= end; i++) {
  41. nums[i] = tmp[i];
  42. }
  43. }
  44. };

过啦!!!

 

写在最后:

以上就是本篇文章的内容了,感谢你的阅读。

如果喜欢本文的话,欢迎点赞和评论,写下你的见解。

如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。

之后我还会输出更多高质量内容,欢迎收看

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览44726 人正在系统学习中