Merge Sort and Its Application (LeetCode #493)

Sorting algorithms can come in very handy when we are comparing numbers in an array. In today's problem, we will be revisiting merge sort, a sorting algorithm with time complexity of O(nlogn).

Let's have a quick recap on how merge sort works:

1) Divide the entire array into two equal(or almost equal) parts.

2) Sort the left partition

3) Sort the right partition

4) Combine the two partition by comparing their first element that was not put back into the original array repeatedly

As for how we should sort the two partitions, MERGE SORT! Recursion is truly powerful:

vector<int> temp;
void merge_sort(vector<int>& nums, int left, int right){
        if(left == right) return;
        int mid = (left + right) / 2; // find out the middle
        merge_sort(nums, left, mid); // sort left partition
        merge_sort(nums, mid + 1, right); // sort right partition
        for(int i = left; i <= right; i++){
            temp.push_back(nums[I]); // create a copy
        }
        int i1 = left, i2 = mid + 1;
        for(int curr = left; curr <= right; curr++){
            // combining the two partitions in ascending order
            if(i1 == mid + 1)
                nums[curr] = temp[i2++];
            else if(i2 > right)
                nums[curr] = temp[i1++];
            else if (temp[i1] <= temp[i2])
                nums[curr] = temp[i1++];
            else
                nums[curr] = temp[i2++];
        }
        return;
}

Merge sort's quick sorting time can save us from some unwanted runtime error when encoutering LeetCode problem:

Given an integer array nums, return the number of reverse pairs in the array.

A reverse pair is a pair (i, j) where 0 <= i < j < nums.length and nums[i] > 2 * nums[j].

Tackling this problem in a brute-force manner is doable, yet LeetCode does not like it. I have tried solving it with bubble sort, another sorting algorithm but with a time complexity of O(n^2) in worst scenario, and resulted in a catastrophic failure. Such reckless approach would not be acceptable, and was also incorrect on multiple attempts.

But here comes our saviour - merge sort, saving us from the formidable runtime error:

class Solution {
    vector<int> temp;
    int count = 0; // stores the number of reverse pairs
public:
    void merge_sort(vector<int>& nums, int left, int right){
        // good old merge sort
        if(left == right) return;
        int mid = (left + right) / 2;
        merge_sort(nums, left, mid);
        merge_sort(nums, mid + 1, right);
        for(int i = left; i <= right; i++){
            temp[i] = nums[i];
        }
        // ** counting logic **
        int g = mid + 1;
        for(int i = left; i <= mid; i++){
            for(; g <= right && long(temp[i]) > 2*long(temp[g]); g++);
            count += g - mid - 1;
        }
        // back to good old merge sort
        int i1 = left, i2 = mid + 1;
        for(int curr = left; curr <= right; curr++){
            if(i1 == mid + 1)
                nums[curr] = temp[i2++];
            else if(i2 > right)
                nums[curr] = temp[i1++];
            else if (temp[i1] <= temp[i2])
                nums[curr] = temp[i1++];
            else
                nums[curr] = temp[i2++];
        }
        return;
    }
    int reversePairs(vector<int>& nums) {
        if(nums.size() == 1)
            return 0;
        for(int i = 0; i < nums.size(); i++)
            temp.push_back(nums[i]); // create a copy
        merge_sort(nums, 0, nums.size() - 1);
        return count;
    }
};

My solution is almost identical to that of the merge sort codes above. But one thing to be extremely careful of is where the counting takes place. Counting has to be done before merging the two partitions together. If it were done otherwise, the result may not be entirely correct as the places between elements were changed and by then you cannot figure out which actually comes first in the array.

But then you may ask, why is it possible for you to merge and count on the go? That is because we count and order the elements from the lowest level, i.e. splitting the elements until they were either alone or paired with another one. We then carry out the comparison and sorting and move on to a level upwards. From the perspective on the higher level, i.e. the groupings of 1-2 elements, the high value elements (those that we care very much about as it may satisfy the requiremenet) still remain in the same position / block. When we carry out comparison, their index would still come before those that were less than half of their value. The same applies up to the surface level.

Another point worth nothing is how counting is implemented. We first check for the first element in the left partition and look for the range of elements in the right partition that satisfy the condition. We will then add the range to the total count and repeate the entire process with the next element. Note that the int g in for-loop was never reset since both partition were sorted, the range can only increase. 


Again, special thanks to justin :)

P.S. Please pay close attention to the ';' after the for-loop in the counting logic. It had been missing for quite a while and I had no idea where my code is defected. It costs me at least an hour to find out... TT

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值