笔者给出递归归并排序及其应用,分别是剑指offer51的数组中的逆序对、小和问题以及LeetCode315计算右侧小于当前元素的个数,在最后还会给出一个非递归版本的归并排序。其中剑指offer51题和LeetCode315题能够通过在线OJ验证,其他的代码都通过了对数器的验证。
递归归并排序
//递归归并排序
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
using namespace std;
class MergeSort{
public:
static void Merge(vector<int>& arr, int start, int mid, int end){
int i = start, j = mid + 1, k = 0;
vector<int> temp(end - start + 1, 0);
while(i <= mid && j <= end){
temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
}
while(i <= mid){
temp[k++] = arr[i++];
}
while(j <= end){
temp[k++] = arr[j++];
}
for(i = 0; i < temp.size(); i++){
arr[start + i] = temp[i];
}
}
static void mSort(vector<int>& arr, int start, int end){
if(start == end){
return;
}
int mid = start + (end - start)/2;
mSort(arr, start, mid); //T(n/2)
mSort(arr, mid + 1, end); //T(n/2)
Merge(arr, start, mid, end); //时间复杂度O(n)
} // T(n) = 2T(n/2)+O(n)-->O(n*logn)
/* 以下是对数器,用来检测算法是否正确 */
static void comparator(vector<int>& arr){
sort(arr.begin(), arr.end());
}
static vector<int> generateRandomArray(int maxSize, int maxValue){
vector<int> arr(maxSize, 0);
for(int i = 0; i < maxSize; i++){
arr[i] = rand()%maxValue + 1;
}
return arr;
}
static vector<int> copyArr(vector<int>& arr){
if(arr.empty()){
return {};
}
vector<int> res(arr.begin(), arr.end());
return res;
}
static void printVector(vector<int>& arr){
for(int i : arr){
cout << i << ' ';
}
cout << endl;
}
};
int main(){
int testTime = 5000; /* 测试组数 */
int maxSize = 100;
int maxValue = 100;
bool succeed = true;
srand((unsigned)time(NULL)); /* 随机数种子一定要在for循环之前使用!!! */
for(int i = 0; i < testTime; i++){
vector<int> arr1 = MergeSort::generateRandomArray(maxSize, maxValue);
vector<int> arr2 = MergeSort::copyArr(arr1);
MergeSort::mSort(arr1, 0, maxSize-1);
MergeSort::comparator(arr2);
if(arr1 != arr2){
MergeSort::printVector(arr1);
MergeSort::printVector(arr2);
succeed = false;
break;
}
}
cout << (succeed ? "Nice" : "false") << endl;
return 0;
}
51. 数组中的逆序对
class Solution {
private:
int merge(vector<int>& nums, int left, int mid, int right){
vector<int> help(right - left + 1, 0);
int i = left, j = mid + 1, k = 0;
int res = 0;
while(i <= mid && j <= right){
res += nums[i] > nums[j] ? (right - j + 1) : 0;
help[k++] = nums[i] > nums[j] ? nums[i++] : nums[j++];
}
while(i <= mid){
help[k++] = nums[i++];
}
while(j <= right){
help[k++] = nums[j++];
}
for(i = 0; i < help.size(); i++){
nums[left + i] = help[i];
}
return res;
}
int mSort(vector<int>& nums, int left, int right){
if(left == right){
return 0;
}
int mid = left + ((right - left) >> 1);
return mSort(nums, left, mid) + mSort(nums, mid + 1, right) + merge(nums, left, mid, right);
}
public:
int reversePairs(vector<int>& nums) {
if(nums.empty()){
return 0;
}
return mSort(nums, 0, nums.size() - 1);
}
};
小和问题
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
using namespace std;
class smallSum{
public:
static int Merge(vector<int>& arr, int start, int mid, int end){
int i = start, j = mid + 1, k = 0;
vector<int> temp(end - start + 1, 0);
int res = 0;
while(i <= mid && j <= end){
/* 正常的归并排序,只是多了一步计算小和 */
res += arr[i] < arr[j] ? (end - j + 1) * arr[i] : 0;
temp[k++] = arr[i] < arr[j] ? arr[i++] : arr[j++];
}
while(i <= mid){
temp[k++] = arr[i++];
}
while(j <= end){
temp[k++] = arr[j++];
}
for(i = 0; i < temp.size(); i++){
arr[start + i] = temp[i];
}
return res;
}
static int mSort(vector<int>& arr, int start, int end){
if(start == end){
return 0;
}
int mid = start + ((end - start) >> 1);
return mSort(arr, start, mid) + mSort(arr, mid + 1, end) + Merge(arr, start, mid, end);
}
/* 以下是对数器 */
static int comparator(vector<int>& arr){
if(arr.empty() || arr.size() < 2){
return 0;
}
int res = 0;
for(int i = 1; i < arr.size(); i++){
for(int j = 0; j < i; j++){
res += arr[j] < arr[i] ? arr[j] : 0;
}
}
return res;
}
static vector<int> generateRandomArray(int maxSize, int maxValue){
vector<int> arr(maxSize, 0);
for(int i = 0; i < maxSize; i++){
arr[i] = rand()%maxValue + 1;
}
return arr;
}
static vector<int> copyArr(vector<int>& arr){
if(arr.empty()){
return {};
}
vector<int> res(arr.begin(), arr.end());
return res;
}
static void printVector(vector<int>& arr){
for(int i : arr){
cout << i << ' ';
}
cout << endl;
}
};
int main(){
int testTime = 5000; /* 测试组数 */
int maxSize = 100;
int maxValue = 100;
bool succeed = true;
srand((unsigned)time(NULL)); /* 随机数种子一定要在for循环之前使用!!! */
for(int i = 0; i < testTime; i++){
vector<int> arr1 = smallSum::generateRandomArray(maxSize, maxValue);
vector<int> arr2 = smallSum::copyArr(arr1);
int res1 = smallSum::mSort(arr1, 0, maxSize-1);
int res2 = smallSum::comparator(arr2);
if(res1 != res2){
smallSum::printVector(arr1);
smallSum::printVector(arr2);
succeed = false;
break;
}
}
cout << (succeed ? "Nice" : "false") << endl;
return 0;
}
315. 计算右侧小于当前元素的个数
/* 思路:这道题和“小和问题”以及“逆序对”问题差不多,用的都是归并排序,不同的是,原本的
归并排序会改变原数组所在的位置,而我们在统计一个元素的右边有多少个小于它的数时,统计
结果需要赋值到该元素在结果数组res中的对应位置,如果元素位置改变了,在res中无法定位它,
因此我们需要一个索引数组index,对应nums中每一个元素的下标,根据nums中数组的值,对index
归并排序,理解这句话是做出这道题的重点
index[i]等于nums[i]的下标,即i
res[i]等于nums[i]的解,即nums[i]的右边小于nums[i]的元素数量
*/
class Solution {
private:
void merge(vector<int>& nums, int left, int mid, int right, vector<int>& index){
vector<int> help(right - left + 1, 0);
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right){
if(nums[index[i]] > nums[index[j]]){
help[k] = index[i]; /* help和res的顺序不能颠倒 */
res[index[i]] += (right - j + 1);
k++;
i++;
}else{
help[k++] = index[j++];
}
}
while (i <= mid){
help[k++] = index[i++];
}
while (j <= right){
help[k++] = index[j++];
}
for(i = 0; i < help.size(); i++){
index[left + i] = help[i];
}
}
void mSort(vector<int>& nums, int left, int right, vector<int>& index){
if(left == right){
return;
}
int mid = left + ((right - left) >> 1);
mSort(nums, left, mid, index);
mSort(nums, mid + 1, right, index);
merge(nums, left, mid, right, index);
}
public:
vector<int> res;
vector<int> countSmaller(vector<int>& nums) {
int len = nums.size();
if(len == 0){
return {};
}
res = vector<int>(len, 0);
vector<int> index(len, 0);
for(int i = 0; i < len; i++){
index[i] = i;
}
mSort(nums, 0, len - 1, index);
return res;
}
};
非递归归并排序
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
using namespace std;
class MergeSort{
public:
static void Merge(vector<int>& arr, int start, int mid, int end){
int i = start, j = mid + 1, k = 0;
vector<int> temp(end - start + 1, 0);
while(i <= mid && j <= end){
temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
}
while(i <= mid){
temp[k++] = arr[i++];
}
while(j <= end){
temp[k++] = arr[j++];
}
for(i = 0; i < temp.size(); i++){
arr[start + i] = temp[i];
}
}
static void MergePass(vector<int>& arr, int k, int n){
int i = 0;
while(i <= n - 2 * k + 1){
Merge(arr, i, i + k - 1, i + 2 * k - 1);
i = i + 2 * k;
}
if(i < n - k + 1){
Merge(arr, i, i + k - 1, n);
}else{
return;
}
}
static void mSort(vector<int>& arr){
int k = 1;
while(k < arr.size()){
MergePass(arr, k, arr.size()-1);
k = 2 * k;
}
}
/* 以下是对数器,用来检测算法是否正确 */
static void comparator(vector<int>& arr){
sort(arr.begin(), arr.end());
}
static vector<int> generateRandomArray(int maxSize, int maxValue){
vector<int> arr(maxSize, 0);
for(int i = 0; i < maxSize; i++){
arr[i] = rand()%maxValue + 1;
}
return arr;
}
static vector<int> copyArr(vector<int>& arr){
if(arr.empty()){
return {};
}
vector<int> res(arr.begin(), arr.end());
return res;
}
static void printVector(vector<int>& arr){
for(int i : arr){
cout << i << ' ';
}
cout << endl;
}
};
int main(){
int testTime = 5000; /* 测试组数 */
int maxSize = 100;
int maxValue = 100;
bool succeed = true;
srand((unsigned)time(NULL)); /* 随机数种子一定要在for循环之前使用!!! */
for(int i = 0; i < testTime; i++){
vector<int> arr1 = MergeSort::generateRandomArray(maxSize, maxValue);
vector<int> arr2 = MergeSort::copyArr(arr1);
MergeSort::mSort(arr1);
MergeSort::comparator(arr2);
if(arr1 != arr2){
MergeSort::printVector(arr1);
MergeSort::printVector(arr2);
succeed = false;
break;
}
}
cout << (succeed ? "Nice" : "false") << endl;
return 0;
}