[Leetcode]667.Beautiful Arrangement II

链接:LeetCode667

给定两个整数?n?和?k,你需要实现一个数组,这个数组包含从?1?到?n?的 n?个不同整数,同时满足以下条件:

① 如果这个数组是\([a1, a2, a3, ... , an]\),那么数组\([|a1 - a2|, |a2 - a3|, |a3 - a4|, ... , |an-1 - an|]\)中应该有且仅有?k 个不同整数;.
② 如果存在多种答案,你只需实现并返回其中任意一种.

示例 1:

输入: n = 3, k = 1
输出: \([1, 2, 3]\)
解释: \([1, 2, 3]\) 包含 3 个范围在 1-3 的不同整数, 并且 \([1, 1]\) 中有且仅有 1 个不同整数 : 1

这道题真实诠释了什么是思考一小时,解题一分钟。遇到这种题,明显无法通过数据结构和常用的算法技巧来解,我们需要通过多次尝试,找规律才能发现解法。我们用一个例子来分析,比如说当n=8,我们有数组:

1, 2, 3, 4, 5, 6, 7, 8

当我们这样有序排列的话,相邻两数的差的绝对值为1。我们想差的绝对值最大能为多少,应该是把1和8放到一起,为7。那么为了尽可能的产生不同的差的绝对值,我们在8后面需要放一个小数字,比如2,这样会产生差的绝对值6,同理,后面再跟一个大数,比如7,产生差的绝对值5,以此类推,我们得到下列数组:

1, 8, 2, 7, 3, 6, 4, 5

其差的绝对值为:7,6,5,4,3,2,1

共有7种,所以我们知道k最大为n-1,所以这样的排列一定会存在。我们的策略是,先按照这种最小最大数相邻的方法排列,没排一个,k自减1,当k减到1的时候,后面的排列方法只要按照生序的方法排列,就不会产生不同的差的绝对值,这种算法的时间复杂度是O(n),属于比较高效的那种。我们使用两个指针,初始时分别指向1和n,然后分别从i和j取数加入结果res,每取一个数字k自减1,直到k减到1的时候,开始按升序取后面的数字。
另外,还有一种解法是将数组不停翻转即可。代码如下:

python:

class Solution:
    def constructArray(self, n: int, k: int) -> List[int]:
        nums = list(range(1,n+1))
        for i in range(k):
            nums[i:] = nums[i:][::-1]
        return nums

C++:

class Solution {
public:
    vector<int> constructArray(int n,int k){
        vector<int> res;
        int i=1,j=n;
        while(i<=j){
            if(k>1) res.push_back(k--%2 ? i++ : j--);
            else res.push_back(i++);
        }
        return res;
    }


    vector<int> constructArray2(int n, int k) {
        vector<int> nums;
        for(int i=1;i<=n;++i){
            nums.push_back(i);
        }
        for(int i=0;i<k;++i){
            reverse(nums.begin()+i,nums.end());
        }
        return nums;
    }
};

参考:[LeetCode] Beautiful Arrangement II 优美排列之二