链接: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;
}
};