归并排序(Merge-Sort):从二路到多路

概念

分治
归并排序中最重要的环节:如何将两个有序数组合并为一个有序数组

算法思想:
左边处理一下,得到左边的信息
右边处理一下,得到右边的信息
最后再处理,横跨左右两边的信息

二路归并

void merge_sort(int *arr, int l, int r){
	if(l >= r) return;
	int mid = (l + r)>>1;
	merge_sort(arr, l, mid); // left sort
	merge_sort(arr, mid + 1, r); // right sort
	int *temp = (int *)malloc(sizeof(int) *(r - l + 1));
	int k = 0, p1 = 1, p2 = mid + 1;
	while(p1 <= mid || p2 <= r){
		if((p2 > r) || (p1 <= mid && arr[p1] <= arr[p2])){
			temp[k++] = arr[p1++];
		}else {
			temp[k++] = arr[p2++];
		}
	}
	for(int i = l; i <= r; i++) arr[i] = temp[i - l];
	free(temp);
	return;
}

多路归并

归并排序在大数据场景下的应用

问题:电脑内存大小2GB 如何对40GB的文件进行排序?

temp只需要执行向末尾添加数据,所以额外的存储区不仅可以存在内存中,还可以存在外部存储区(如硬盘等)中
40GB的文件分成20分2GB的小数据进行归并排序,这时候就转化为了将20份有序的小文件合并成一个有序的大文件
这就是外部排序和内部排序的区别,如快速排序就必须在内存中执行,但归并排序的额外存储区可以使用外存

经典面试题 - 归并排序基础

剑指 Offer 51. 数组中的逆序对

  • 首先求的左边区间的逆序对
  • 求的右边区间的逆序对
  • 求的横跨左右两边的逆序数(在归并排序的过程中,如果把a这个数字放入结果数列中,左边区间还剩k个元素,那么证明有k个元素大于a,证明有k个逆序对,所以每次右边区间放入结果的时候,将结果进行累加,可以得到所有横跨左右两边的逆序对)
class Solution {
public:
    vector<int> temp;
    int countResult(vector<int> &nums, int l, int r){
        if(l >= r) return 0;
        int ans = 0, mid = (l + r) >> 1;
        ans += countResult(nums, l, mid);
        ans += countResult(nums, mid + 1, r);
        int k = l, p1 = l, p2 = mid + 1;
        while(p1 <= mid || p2 <= r){
            if((p2 > r) || (p1 <= mid && nums[p1] <= nums[p2])){
                temp[k++] = nums[p1++];
            }else{
                temp[k++] = nums[p2++];
                ans += (mid - p1 + 1);
            }
        }
        for(int i = l; i <= r; i++) nums[i] = temp[i];
        return ans;
    }
    int reversePairs(vector<int>& nums) {
        while(temp.size() < nums.size()) temp.push_back(0);
        return countResult(nums, 0, nums.size() - 1);
    }
};

23. 合并 K 个升序链表

class Solution {
public:
    struct CMP {
        bool operator()(ListNode *p, ListNode *q) {
            return p->val > q->val;
        }
    };
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        priority_queue<ListNode *, vector<ListNode *>, CMP> q;
        for(auto x: lists){
            if(x == nullptr) continue;
            q.push(x);
        }
        ListNode ret, *p = &ret;
        while(!q.empty()){
            ListNode *cur = q.top();
            q.pop();
            p -> next = cur;
            p = cur;
            if(cur -> next) q.push(cur -> next);
        }
        return ret.next;
    }
};

148. 排序链表

class Solution {
public:
    ListNode* mergeSort(ListNode* head, int n){
        if(head == nullptr || head->next == nullptr) return head;
        int l = n / 2, r = n - l;
        ListNode *lp = head, * rp = lp, *p;
        for(int i = 1; i < l; i++, rp = rp->next);
        p = rp, rp = rp -> next;
        p->next = nullptr;
        lp = mergeSort(lp, l); // left sort
        rp = mergeSort(rp, r); // right sort
        ListNode ret;
        p = &ret;
        while(lp || rp){
            if((rp == nullptr) || (lp && lp->val <= rp->val)){
                p->next = lp;
                lp = lp->next;
                p = p->next;
            }else{
                p->next = rp;
                rp = rp->next;
                p = p->next;
            }
        }
        return ret.next;

    }
    ListNode* sortList(ListNode* head) {
        int n = 0;
        ListNode *p = head;
        while(p) p = p -> next, n += 1;
        return mergeSort(head, n);
    }
};

1305. 两棵二叉搜索树中的所有元素

class Solution {
public:
    void getResult(TreeNode *root, vector<int> &arr){
        if(root == nullptr) return;
        getResult(root->left, arr);
        arr.push_back(root->val);
        getResult(root->right, arr);
        return ;
    }
    vector<int> getAllElements(TreeNode* root1, TreeNode* root2) {
        vector<int> lnums, rnums;
        getResult(root1, lnums);
        getResult(root2, rnums);
        vector<int> ret;
        int p1 = 0, p2 = 0;
        while(p1 < lnums.size() || p2 < rnums.size()){
            if((p2 >= rnums.size()) || p1 < lnums.size() && lnums[p1] < rnums[p2]){
                ret.push_back(lnums[p1++]);
            }else{
                ret.push_back(rnums[p2++]);
            }
        }
        return ret;
    }
};

经典面试题 - 归并排序进阶

327.区间和的个数

原序列的区间和的值等于前缀和数组两项相减的结果
问题转化为 lower <= sum[j] - sum[i] <= upper (i <j)
sum[j] - lower >= sum[i] >= sum[j] - upper
b => sum[i] >= a

class Solution {
public:
    int countTwoPart(vector<long long>&sum, int l1, int r1, int l2, int r2, int lower, int upper){
        int ans = 0, k1 = l1 , k2 = l1;
        for(int j = l2; j <= r2; j++){
            long long a = sum[j] - upper;
            long long b = sum[j] - lower;
            while(k1 <= r1 && sum[k1] < a) k1 += 1;
            while(k2 <= r1 && sum[k2] <= b) k2 += 1;
            ans += k2 - k1;
        }
        return ans;
    }

    int mergeSort(vector<long long> &sum, int l, int r, int lower, int upper){
        if(l >= r) return 0;
        int mid = (l + r) >> 1, ans = 0;
        ans += mergeSort(sum, l, mid, lower, upper);
        ans += mergeSort(sum, mid + 1, r, lower, upper);
        ans += countTwoPart(sum, l ,mid, mid + 1, r, lower, upper);
        int k = l, p1 = l, p2 = mid + 1;
        while(p1 <= mid || p2 <= r){
            if((p2 > r) || (p1 <= mid && sum[p1] <= sum[p2])){
                temp[k++] = sum[p1++];
            }else{
                temp[k++] = sum[p2++];
            }
        }
        for(int i = l; i <= r; i++) sum[i] = temp[i];
        return ans;
    }


    vector<long long> temp;
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        vector<long long> sum(nums.size() + 1);
        while(temp.size() < sum.size()) temp.push_back(0);
        sum[0] = 0;
        for(int i = 0; i < nums.size(); i++) sum[i + 1] = sum[i] + nums[i];
        return mergeSort(sum, 0, sum.size() - 1, lower, upper);
    }
};

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

class Solution {
public:
    struct Data {
        Data(int val, int ind) : val(val), ind(ind), cnt(0) {}
        bool operator>(const Data &a){
            return val > a.val;
        }
        int val, ind, cnt;
    };
    void mergeSort(vector<Data> &arr, int l ,int r){
        if(l >= r) return;
        int mid = (l + r) >> 1;
        mergeSort(arr, l ,mid);
        mergeSort(arr, mid + 1, r);
        int k = l, p1 = l, p2 = mid + 1;
        while(p1 <= mid || p2 <=r){
            if((p2 > r) || (p1 <= mid && arr[p1] > arr[p2])){
                arr[p1].cnt += (r - p2 + 1);
               temp[k++] = arr[p1++]; 
            }else{
                temp[k++] = arr[p2++];
            }
        }
        for(int i = l;i <= r; i++) arr[i] = temp[i];
        return;
    }
    vector<Data> temp;
    vector<int> countSmaller(vector<int>& nums) {
        vector<Data> arr;
        for(int i = 0; i < nums.size(); i++) arr.push_back(Data{nums[i], i});
        while(temp.size() < arr.size()) temp.push_back(Data{0,0});
        mergeSort(arr, 0, arr.size() - 1);
        vector<int> ret(nums.size());
        for(auto x: arr) ret[x.ind] = x.cnt;
        return ret;
    }
};

1508. 子数组和排序后的区间和

class Solution {
public: 
    struct Data {
        Data(int i, int j, long long sum) : i(i), j(j), sum(sum) {}
        int i, j, sum;
    };
    struct CMP{
        bool operator()(const Data &a, const Data &b){
            return a.sum > b.sum;
        }
    };
    int rangeSum(vector<int>& nums, int n, int left, int right) {
        priority_queue<Data, vector<Data>, CMP> q;
        for(int i = 0; i < n; i++){
            q.push(Data{i, i, nums[i]});
        }
        int ans = 0, mod = 1e9 + 7;
        for(int i = 1;i <= right; i++){
            Data d = q.top();
            q.pop();
            if(i >= left) ans = (ans + d.sum) % mod;
            if(d.j + 1 < n) q.push(Data{d.i, d.j + 1, (d.sum + nums[d.j + 1]) % mod });
        }
        return ans;
    }
};

面试题 04.08. 首个共同祖先

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == nullptr) return nullptr; 
        if(root == p || root == q) return root;
        TreeNode *l = lowestCommonAncestor(root->left, p, q);
        TreeNode *r = lowestCommonAncestor(root->right, p, q);
        if(l != nullptr && r != nullptr) return root;
        if(l != nullptr) return l;
        return r;
    }
};

1302. 层数最深叶子节点的和

class Solution {
public:
    void getResult(TreeNode *root, int k, int &max_k, int &ans){
        if(root == nullptr) return;
        if(k == max_k) ans += root->val;
        if(k > max_k){
            max_k = k;
            ans = root->val;
        }
        getResult(root->left, k + 1, max_k, ans);
        getResult(root->right, k + 1, max_k, ans);
        return;
    }
    int deepestLeavesSum(TreeNode* root) {
        int max_k = 0, ans = 0;
        getResult(root, 1, max_k, ans);
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值