归并排序及其应用

笔者给出递归归并排序及其应用,分别是剑指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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值