leetcode-932.漂亮数组

分治法(递归实现)


题目详情

对于某些固定的 N,如果数组 A 是整数 1, 2, ..., N 组成的排列,使得:
对于每个 i < j,都不存在 k 满足i < k < j使得 A[k] * 2 = A[i] + A[j]
那么数组 A 是漂亮数组。
给定 N,返回任意漂亮数组 A(保证存在一个)。


示例1:

输入:4
输出:[2,1,4,3]

示例2:

输入:5
输出:[3,1,2,5,4]

思路:
以5为例画出分治法的"分治图":

图
解释

我的代码:

/* 结论:
 * <1> A 是漂亮数组,则 a * A + b 也是漂亮数组 
 * <2> A 为奇数漂亮数组,B 为偶数漂亮数组,A + B 为漂亮数组
 * <3>数组两两配对,左数组 * 2 - 1 一定是奇数组,右数组 * 2 一定为偶数组,合并一定为漂亮数组
 * 假设 [1] 是最小漂亮数组,按照上面规律递推得到的一定是漂亮数组。 
 * |1|1|1|1|1|1|1|1|
 * |1 2|1 2|1 2|1 2|
 * |1 3 2 4|1 3 2 4|
 * |1 5 3 7 2 6 4 8|
 */

class Solution 
{
public:
    vector<int> beautifulArray(int n) 
    {
        //递归出口
        if (n == 1) return {1};
    //递归实现奇数偶数的映射    
        int odd = (n + 1) / 2, even = n / 2;
        vector<int> left = beautifulArray(odd);
        vector<int> right = beautifulArray(even);
        vector<int> res;  //res存储每层"治"后的结果(奇在前偶在后)
    //治理的方法取决于是left还是right
    //left是奇数映射,right是偶数映射 所以反射需要做相对应的处理
        for (auto &c : left) res.push_back(2 * c - 1);
        for (auto &c : right) res.push_back(2 * c);
        return res;
    }
};

通过图中可以看出:我们分治的过程有很多重复的处理,比如上面例子图中第二层和第三层都有对2的分治,这里我们可以利用hash进行减少重复加速处理:

class Solution 
{
public:
    unordered_map<int, vector<int>> mp;
    vector<int> beautifulArray(int n) 
    {
        
        if (n == 1) return {1};
      
        if (mp.count(n)) return mp[n]; //如果已经处理过n的情况直接利用即可
        int odd = (n + 1) / 2, even = n / 2;
        vector<int> left = beautifulArray(odd);
        vector<int> right = beautifulArray(even);
        vector<int> res;  

        for (auto &c : left) res.push_back(2 * c - 1);
        for (auto &c : right) res.push_back(2 * c);
        mp[n] = res;
        return res;
    }
};

涉及知识点:

1.分治法

顾名思义,分治问题由“分”(divide)和“治”(conquer)两部分组成,通过把原问题分为子问题,再将子问题进行处理合并,从而实现对原问题的求解。例如归并排序就是典型的分治问题,其中“分”即为把大数组平均分成两个小数组,通过递归实现,最终我们会得到多个长度为 1 的子数组;“治”即为把已经排好序的两个小数组合成为一个排好序的大数组,从长度为 1 的子数组开始,最终合成一个大数组。

posted @ 2022-07-19 17:24  ggaoda  阅读(7)  评论(0编辑  收藏  举报  来源