1. 排序数组
912. 排序数组
https://leetcode.cn/problems/sort-an-array/
给你一个整数数组
nums,请你将该数组升序排列。你必须在 不使用任何内置函数 的情况下解决问题,时间复杂度为
O(nlog(n)),并且空间复杂度尽可能小。
cppclass Solution { public: vector<int> tmp; vector<int> sortArray(vector<int>& nums) { tmp.resize(nums.size()); merge_sort(nums,0,nums.size()-1); return nums; } void merge_sort(vector<int>& nums,int left,int right) { if(left>=right) return; int mid = left + ((right - left) >> 1) merge_sort(nums,left,mid); merge_sort(nums,mid+1,right); int cur1=left,cur2=mid+1,i=0; while(cur1<=mid&&cur2<=right) tmp[i++]=nums[cur1]>=nums[cur2]?nums[cur2++]:nums[cur1++]; while(cur1<=mid) tmp[i++]=nums[cur1++]; while(cur2<=right) tmp[i++]=nums[cur2++]; for(int i=left;i<=right;i++) nums[i]=tmp[i-left]; } };
2. 数组逆序对的个数
LCR 170. 交易逆序对的总数
https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/
在股票交易中,如果前一天的股价高于后一天的股价,则可以认为存在一个「交易逆序对」。请设计一个程序,输入一段时间内的股票交易记录
record,返回其中存在的「交易逆序对」总数
cppclass Solution { public: vector<int> tmp; int reversePairs(vector<int>& nums) { tmp.resize(nums.size()); return merge_sort(nums,0,nums.size()-1); } int merge_sort(vector<int>& nums,int left,int right) { if(left>=right) return 0; int mid = left + ((right - left) >> 1); int ret=merge_sort(nums,left,mid)+merge_sort(nums,mid+1,right); int cur1=left,cur2=mid+1,i=0; while(cur1<=mid&&cur2<=right) { if(nums[cur1]>nums[cur2]) {tmp[i++]=nums[cur2++]; ret+=mid-cur1+1;} else tmp[i++]=nums[cur1++]; } while(cur1<=mid) tmp[i++]=nums[cur1++]; while(cur2<=right) tmp[i++]=nums[cur2++]; for(int i=left;i<=right;i++) nums[i]=tmp[i-left]; return ret; } };
3. 计算右侧小于当前元素的个数
315. 计算右侧小于当前元素的个数
https://leetcode.cn/problems/count-of-smaller-numbers-after-self/
给你一个整数数组
nums,按要求返回一个新数组counts。数组counts有该性质:counts[i]的值是nums[i]右侧小于nums[i]的元素的数量。
cppclass Solution { public: // (1)在求逆序对中,使用归并排序,是计算"左侧大于当前元素的总个数" // 在比较的时候, 当 nums[i] > nums[j] 时, 说明 i ~ mid 的这些数,都是大于nums[j]的, // 所以总数 += mid-i+1 // (2)在本题中,首先,我们先想,计算"右侧小于当前元素的总个数"(先不管每个数右边详细小于的个数) // 在比较的时候, // 当 nums[i] > nums[j] 时,先不管,j往后走; // 当 nums[i] <=nums[j] 时,说明 mid+1 ~ j-1 的这些数,都是小于nums[i]的, // 所以总数 += j-1 - (mid+1) +1,即总数 += j-mid-1 // 然后,因为本题要具体到,每个数右边详细的小于个数, // 所以,可以在一开始,使用pair,对 nums 里的数附加一个索引,变成num_index,记录数的值和数对应的下标位置, // 然后,就可以在添加时,具体到这个数对应的位置了: (pair中,first为num的值,second为num的下标) // 当 num_index[i].first <= num_index[j].first ,说明 mid+1 ~ j-1 的这些数,都是小于nums[i]的, // 对应下标的个数增加: count[num_index[i].second] += j-mid-1 vector<pair<int, int>> temp; vector<int> count; vector<int> countSmaller(vector<int>& nums) { int n = nums.size(); vector<pair<int, int>> num_index; for (int i = 0; i < n; i++) num_index.push_back(pair<int, int>(nums[i], i)); temp = vector<pair<int, int>>(n); count = vector<int>(n, 0); merge_sort(num_index, 0, n-1); return count; } void merge_sort(vector<pair<int, int>>& num_index, int l, int r){ if (l >= r) return; int mid = l + (r - l) / 2; merge_sort(num_index, l, mid); merge_sort(num_index, mid+1, r); merge(num_index, l, mid, r); } void merge(vector<pair<int, int>>& num_index, int l, int mid, int r){ int i = l, j = mid + 1; int k = l; while (i <= mid && j <= r){ // 当 num_index[i].first <= num_index[j].first 时,说明 mid+1 ~ j-1的这些数,都是小于nums[i]的, // 所以count[num_index[i].second] += j-mid-1 if (num_index[i].first <= num_index[j].first){ count[num_index[i].second] += j - mid - 1; temp[k++] = num_index[i++]; } else temp[k++] = num_index[j++]; } while (i <= mid) { // 如果右边的j走完了,现在的i开始的数,其实都是大于所有右边的数的 count[num_index[i].second] += j - mid - 1; temp[k++] = num_index[i++]; } while (j <= r) temp[k++] = num_index[j++]; for (i = l; i <= r; i++) num_index[i] = temp[i]; } };
4. 翻转对
493. 翻转对
https://leetcode.cn/problems/reverse-pairs/
给定一个数组
nums,如果i < j且nums[i] > 2*nums[j]我们就将(i, j)称作一个重要翻转对。你需要返回给定数组中的重要翻转对的数量
cppclass Solution { public: vector<int> tmp; int reversePairs(vector<int>& nums) { tmp.resize(nums.size()); return merge_sort(nums,0,nums.size()-1); } int merge_sort(vector<int>& nums,int left,int right) { if(left>=right) return 0; int mid = left + ((right - left) >> 1); int ret=merge_sort(nums,left,mid)+merge_sort(nums,mid+1,right); int cur1=left,cur2=mid+1,i=0; while(cur2<=right) { while(cur1<=mid&&nums[cur1] <= 2 * (long long)nums[cur2]) cur1++; if(cur1>mid)break; ret+=mid-cur1+1; cur2++; } cur1=left,cur2=mid+1; while(cur1<=mid&&cur2<=right) { if(nums[cur1]>nums[cur2]) tmp[i++]=nums[cur2++]; else tmp[i++]=nums[cur1++]; } while(cur1<=mid) tmp[i++]=nums[cur1++]; while(cur2<=right) tmp[i++]=nums[cur2++]; for(int i=left;i<=right;i++) nums[i]=tmp[i-left]; return ret; } };


