归并排序的应用

归并排序的特点是左右两个已经排好序的子数组进行合并成更大的数组的一个过程

    void Merge(int left, int right) {
        int mid = (left + right) / 2;
        int i = left;
        int j = mid;
        int index = left;
        while (i < mid && j < right) {
            if (m_data[i] <= m_data[j]) {
                m_midData[index] = m_data[i];
                ++i;
            }
            else {
                m_midData[index] = m_data[j];
                ++j;
            }
            ++index;
        }

        while (i < mid) {
            m_midData[index] = m_data[i];
            ++i;
            ++index;
        }

        while (j < right) {
            m_midData[index] = m_data[j];
            ++j;
            ++index;
        }

        while (left < right) {
            m_data[left] = m_midData[left];
            ++left;
        }
    }

    void MergeSort(int left, int right) {
        if (left + 1 >= right)
            return;

        int mid = (left + right) / 2;
        MergeSort(left, mid);
        MergeSort(mid, right);
        Merge(left, right);
    }

如上所示,进行递归排序;

 

递归排序的应用:

1、https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/

计算右侧小于当前元素的个数

假设当前已经排好序的两个数组,计算右侧数组中分别小于左侧数组中的每个元素个数则可以在O(n)的时间内计算出来

可行性:1、当前只有两个元素,统计右侧小于左侧的元素;2、当前有4个元素左右两边各两个,则由于第一点已经统计了各自内部的小于左侧的个数,则内部无需再进行统计,只需统计右边两个元素分别小于左边两个元素的个数;同理可以推到多个多个元素的合并

class Solution {
    void Merge(int left, int right) {
        int mid = (left + right) / 2;
        int i = left;
        int j = mid;
        int index = left;
        while (i < mid && j < right) {
            if (m_data[i] <= m_data[j]) {
                m_midIndex[index] = m_index[i];
                m_midData[index] = m_data[i];
                m_ret[m_index[i]] += j - mid;
                ++i;
            } else {
                m_midIndex[index] = m_index[j];
                m_midData[index] = m_data[j];
                ++j;
            }
            ++index;
        }

        while (i < mid) {
            m_midIndex[index] = m_index[i];
            m_midData[index] = m_data[i];
            m_ret[m_index[i]] += right - mid;
            ++i;
            ++index;
        }

        while (j < right) {
            m_midIndex[index] = m_index[j];
            m_midData[index] = m_data[j];
            ++j;
            ++index;
        }

        while (left < right) {
            m_index[left] = m_midIndex[left];
            m_data[left] = m_midData[left];
            ++left;
        }
    }

    void MergeSort(int left, int right) {
        if (left + 1 >= right)
            return;

        int mid = (left + right) / 2;
        MergeSort(left, mid);
        MergeSort(mid, right);
        Merge(left, right);
    }
public:
    vector<int> countSmaller(vector<int>& nums) {
        m_midIndex = vector<int>(nums.size());
        m_midData = vector<int>(nums.size());
        m_ret = vector<int>(nums.size(), 0);
        m_index = vector<int>(nums.size());
        for (int i = 0; i < nums.size(); ++i)
            m_index[i] = i;
        m_data = nums;

        MergeSort(0, nums.size());

        return m_ret;
    }

    vector<int> m_index;
    vector<int> m_data;
    vector<int> m_ret;
    vector<int> m_midData;
    vector<int> m_midIndex;
};
View Code

 

 

2、https://leetcode-cn.com/problems/count-of-range-sum/

区间和的个数;

由于计算区间和,正常需要先进行预处理:计算前缀和;sums

则问题可转化为从i+1~j的区间和为:sums[j]-sum[i]的差值是否在对应的区间

同理:对sums进行从小到大排序。。。。。。。。

 

 

总结:

统计某个元素右侧相对当前元素的关系的个数,均可转化为归并排序的过程

 

区间数量统计的另外两个算法:线段树和树状数组;如上两个也可转化为线段树或者树状数组的统计

 

posted @ 2021-03-09 08:27  LCAC  阅读(103)  评论(0)    收藏  举报