首页 > 其他分享 >归并分治

归并分治

时间:2024-09-26 08:51:10浏览次数:8  
标签:归并 right arr int 分治 mid vector left

归并排序

912. 排序数组

#include <iostream>
#include <vector>

using namespace std;

class Solution {
public:
    // 分治-治
    void merge(vector<int> &arr, int left, int mid, int right, vector<int> &temp) {
        // [left, mid]和[mid+1, right]两个有序数组
        int i = left;
        int j = mid + 1;
        int index = 0;
        while (i <= mid && j <= right) {
            if (arr[i] < arr[j])
                temp[index++] = arr[i++];
            else
                temp[index++] = arr[j++];
        }
        // 剩余元素直接放入temp
        while (i <= mid) temp[index++] = arr[i++];
        while (j <= right) temp[index++] = arr[j++];
        // 放回原数组
        index = 0;
        while (left <= right) arr[left++] = temp[index++];
    }

    // 分治-分
    // T(n) = 2 * T(n/2) + O(n)
    // a = 2, b = 2, c = 1, 根据 master 公式,时间复杂度为 O(n * logn)
    // 空间复杂度 O(n)
    void divide(vector<int> &arr, int left, int right, vector<int> &temp) {
        if (left >= right) return;
        int mid = left + ((right - left) >> 2);
        // 左边归并排序
        divide(arr, left, mid, temp);
        // 右边归并排序
        divide(arr, mid + 1, right, temp);
        // 合并两个有序序列
        merge(arr, left, mid, right, temp);
    }

    // 递归版归并排序
    void mergeSort(vector<int> &arr) {
        vector<int> temp(arr.size());
        divide(arr, 0, arr.size() - 1, temp);
    }

    vector<int> sortArray(vector<int> &nums) {
        mergeSort(nums);
        return nums;
    }
};
#include <iostream>
#include <vector>

using namespace std;

class Solution {
public:
    // 分治-治
    void merge(vector<int> &arr, int left, int mid, int right, vector<int> &temp) {
        // [left, mid]和[mid+1, right]两个有序数组
        int i = left;
        int j = mid + 1;
        int index = 0;
        while (i <= mid && j <= right) {
            if (arr[i] < arr[j])
                temp[index++] = arr[i++];
            else
                temp[index++] = arr[j++];
        }
        // 剩余元素直接放入temp
        while (i <= mid) temp[index++] = arr[i++];
        while (j <= right) temp[index++] = arr[j++];
        // 放回原数组
        index = 0;
        while (left <= right) arr[left++] = temp[index++];
    }


    // 非递归版归并排序
    void mergeSort(vector<int> &arr) {
        vector<int> temp(arr.size());
        int n = arr.size();
        // 执行 O(logn) 次
        for (int left, mid, right, step = 1; step < n; step <<= 1) {
            left = 0;
            // O(n)
            while (left < n) {
                mid = left + step - 1;
                // 没有右侧
                if (mid + 1 >= n) break;
                // 求右边界
                right = min(left + (step << 1) - 1, n - 1);
                merge(arr, left, mid, right, temp);
                // 找下一组
                left = right + 1;
            }
        }
    }

    vector<int> sortArray(vector<int> &nums) {
        mergeSort(nums);
        return nums;
    }
};

归并分治

  • 思考一个问题在大范围上的答案,是否等于:左部分答案 + 右部分答案 + 跨越左右产生的答案
  • 计算跨越左右产生的答案时,如果加上左右两侧都有序,能不能简化计算

计算数组的小和

#include <iostream>
#include <vector>

using namespace std;

vector<int> temp;

void merge(vector<int> &arr, int left, int mid, int right) {
    int i = left;
    int j = mid + 1;
    int index = 0;
    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) {
            temp[index++] = arr[i++];
        } else {
            temp[index++] = arr[j++];
        }
    }
    while (i <= mid) temp[index++] = arr[i++];
    while (j <= right) temp[index++] = arr[j++];
    index = 0;
    while (left <= right) arr[left++] = temp[index++];
}

// 返回小和,且把数组这一段变成有序
long divide(vector<int> &arr, int left, int right) {
    if (left >= right) return 0;
    int mid = left + ((right - left) >> 1);
    // 分别计算两侧的小和
    long leftSum = divide(arr, left, mid);
    long rightSum = divide(arr, mid + 1, right);
    // 两部分都已经有序
    long midSum = 0;
    for (int l = left, r = mid + 1, sum = 0; r <= right; r++) {
        // 找到第一个大于 arr[r] 的位置
        while (l <= mid && arr[l] <= arr[r]) {
            sum += arr[l++];
        }
        midSum += sum;
    }

    merge(arr, left, mid, right);
    return leftSum + midSum + rightSum;
}

int main() {
    int n;
    cin >> n;
    vector<int> arr(n);
    temp.resize(n);
    for (int i = 0; i < n; ++i)
        cin >> arr[i];
    cout << divide(arr, 0, n - 1);
}

493. 翻转对

#include <iostream>
#include <vector>

using namespace std;

class Solution {
public:
    vector<int> temp;

    void merge(vector<int> &arr, int left, int mid, int right) {
        int i = left;
        int j = mid + 1;
        int index = 0;
        while (i <= mid && j <= right) {
            if (arr[i] <= arr[j]) {
                temp[index++] = arr[i++];
            } else {
                temp[index++] = arr[j++];
            }
        }
        while (i <= mid) temp[index++] = arr[i++];
        while (j <= right) temp[index++] = arr[j++];
        index = 0;
        while (left <= right) arr[left++] = temp[index++];
    }

    int divide(vector<int> &nums, int left, int right) {
        if (left >= right) return 0;
        int mid = left + ((right - left) >> 1);
        int leftSum = divide(nums, left, mid);
        int rightSum = divide(nums, mid + 1, right);

        int midSum = 0;
        for (int l = left, r = mid + 1, sum = 0; l <= mid; ++l) {
            while (r <= right && ((long) nums[l] > (long) 2 * nums[r])) {
                sum++;
                r++;
            }
            midSum += sum;
        }

        merge(nums, left, mid, right);
        return leftSum + midSum + rightSum;
    }

    int reversePairs(vector<int> &nums) {
        temp.resize(nums.size());
        return divide(nums, 0, nums.size() - 1);
    }
};

标签:归并,right,arr,int,分治,mid,vector,left
From: https://www.cnblogs.com/sprinining/p/18432694

相关文章

  • 排序----归并排序(非递归版)
    如图代码为11归并的示例,用for循环来解决。每一次往前递归的前一小部分内部已经是有序的了。但是我们测试的时候会发现这样一个问题,begin和end的值会存在越界的问题,而且只有begin1不会越界,因为begin1是受for循环中i的控制的。所以当我们遇到begin越界了就不用管了,遇到end越......
  • 洛谷题单指南-分治与倍增-P3509 [POI2010] ZAB-Frog
    原题链接:https://www.luogu.com.cn/problem/P3509题意解读:n个点,每个点上有一只青蛙每次跳到距离自己第k近的点,m次之后跳到哪个点。解题思路:1、计算距离每个点第k近的点,存入ne[N]给定一个点i,距离i第k近的点一定在长度为k+1个点的窗口内,窗口包括i并且,第k近的点只能是左端点或者......
  • 【数据结构和算法实践-排序-归并排序】
    数据结构和算法实践-排序-归并排序题目MyThought代码示例JAVA-8题目排序MyThought然后再进行递归,递归要注意两个方面:一、自我调用二、终止条件:即函数边界注意点:树、递归*代码示例JAVA-8publicclassMergeSort{publicstaticvoidmergeSor......
  • 归并排序
    选择排序......定义归并排序(mergesort)是高效的基于比较的稳定排序算法。性质归并排序基于分治思想将数组分段排序后合并,时间复杂度在最优、最坏与平均情况下均为O(nlogn),空间复杂度为O(n)。归并排序可以只使用O(1)的辅助空间,但为便捷通常使用与原数组等长的辅助......
  • Datawhale Leecode基础算法篇 task02:递归算法and分治算法
    官方学习文档:datawhalechina往期task01:枚举算法链接:DatawhaleLeecode基础算法篇task01:枚举算法递归算法递归简介递归(Recursion):指的是一种通过重复将原问题分解为同类的子问题而解决的方法。在绝大数编程语言中,可以通过在函数中再次调用函数自身的方式来实现递归。举......
  • 算法-分治和逆序
    分治法(DivideandConquer)是一种重要的算法设计范式,它通过将复杂的问题分解成更小、更易于管理和解决的子问题,然后递归地解决这些子问题,最后将子问题的解合并以得到原问题的解。分治法通常用于排序、搜索、数学计算和优化等问题。分治法的核心思想可以概括为三个步骤:分......
  • 洛谷题单指南-分治与倍增-P2345 [USACO04OPEN] MooFest G
    原题链接:https://www.luogu.com.cn/problem/P2345题意解读:有n头牛,每头牛都有听力v、坐标x两个属性,要计算每两头牛的max{vi​,vj​}×∣xi​−xj​∣之和。解题思路:首先想到的肯定是枚举法,需要O(n^2)的复杂度有没有优化的方法?可以采用分治法!由于是计算两头牛之间的max{vi​,......
  • 算法学习-CDQ分治
    对于二维偏序,为普通的求逆序对,只需要先排序一遍,然后树状数组或双指针即可而三位偏序甚至更高,则需要用CDQ分治,简单来说,就是将树状数组和双指针结合操作步骤如下:1.开始将数组按第一维排序,保证满足x性质2.在归并中,将左右分别按y排序,因为开始以x排序,所以保证了正确性,保证贡献从左......
  • 【数据结构】线性表作业——归并排序
    【问题描述】有一个含n(n<=200000)个整数的无序序列,采用链表的二路归并排序实现递增排序【输入形式】一行字符串,包含多个整数,每个数之间用空格分开。【输出形式】递增排序的结果,每个数之间用空格分开。【样例输入】947625813【样例输出】12345......
  • 递归 & 分治
    递归定义递归(英语:Recursion),在数学和计算机科学中是指在函数的定义中使用函数自身的方法,在计算机科学中还额外指一种通过重复将问题分解为同类的子问题而解决问题的方法。引入递归的基本思想是某个函数直接或者间接地调用自身,这样原问题的求解就转换为了许多性质相同但是规......