加载中...
1470-重新排列数组(Shuffle the Array)
发表于:2021-12-03 | 分类: 简单
字数统计: 1.4k | 阅读时长: 6分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/shuffle-the-array

英文原文

Given the array nums consisting of 2n elements in the form [x1,x2,...,xn,y1,y2,...,yn].

Return the array in the form [x1,y1,x2,y2,...,xn,yn].

 

Example 1:

Input: nums = [2,5,1,3,4,7], n = 3
Output: [2,3,5,4,1,7] 
Explanation: Since x1=2, x2=5, x3=1, y1=3, y2=4, y3=7 then the answer is [2,3,5,4,1,7].

Example 2:

Input: nums = [1,2,3,4,4,3,2,1], n = 4
Output: [1,4,2,3,3,2,4,1]

Example 3:

Input: nums = [1,1,2,2], n = 2
Output: [1,2,1,2]

 

Constraints:

  • 1 <= n <= 500
  • nums.length == 2n
  • 1 <= nums[i] <= 10^3

中文题目

给你一个数组 nums ,数组中有 2n 个元素,按 [x1,x2,...,xn,y1,y2,...,yn] 的格式排列。

请你将数组按 [x1,y1,x2,y2,...,xn,yn] 格式重新排列,返回重排后的数组。

 

示例 1:

输入:nums = [2,5,1,3,4,7], n = 3
输出:[2,3,5,4,1,7] 
解释:由于 x1=2, x2=5, x3=1, y1=3, y2=4, y3=7 ,所以答案为 [2,3,5,4,1,7]

示例 2:

输入:nums = [1,2,3,4,4,3,2,1], n = 4
输出:[1,4,2,3,3,2,4,1]

示例 3:

输入:nums = [1,1,2,2], n = 2
输出:[1,2,1,2]

 

提示:

  • 1 <= n <= 500
  • nums.length == 2n
  • 1 <= nums[i] <= 10^3

通过代码

高赞题解

这个问题空间复杂度是 O(n) 的解法太简单了,不多说了。说说空间复杂度为 O(1) 的做法,原地完成:)

实际上,有两个思路可以完成这一点。

以下解析可能结合代码看会更好理解。


思路一

因为题目限制了每一个元素 nums[i] 最大只有可能是 1000,这就意味着每一个元素只占据了 10 个 bit。(2^10 - 1 = 1023 > 1000)

而一个 int 有 32 个 bit,所以我们还可以使用剩下的 22 个 bit 做存储。实际上,每个 int,我们再借 10 个 bit 用就好了。

因此,在下面的代码中,每一个 nums[i] 的最低的十个 bit(0-9 位),我们用来存储原来 nums[i] 的数字;再往前的十个 bit(10-19 位),我们用来存储重新排列后正确的数字是什么。

在循环中,我们每次首先计算 nums[i] 对应的重新排列后的索引 j,之后,取 nums[i] 的低 10 位(nums[i] & 1023),即 nums[i] 的原始信息,把他放到 nums[j] 的高十位上。

最后,每个元素都取高 10 位的信息(e >> 10),即是答案。

我的参考代码(C++):

class Solution {
public:
    vector<int> shuffle(vector<int>& nums, int n) {

        for(int i = 0; i < 2 * n; i ++){
            int j = i < n ? 2 * i : 2 * (i - n) + 1;
            nums[j] |= (nums[i] & 1023) << 10;
        }
        for(int& e: nums) e >>= 10;
        return nums;
    }
};

思路二

利用题目中限制每一个元素 nums[i] 都大于 0。我们可以使用负数做标记。

标记什么?标记当前 nums[i] 存储的数字,是不是重新排列后的正确数字。如果是,存负数;如果不是,存正数(即原本的数字,还需处理)。

我们每次处理一个 nums[i],计算这个 nums[i] 应该放置的正确位置 j。但是,nums[j] 还没有排列好,所以我们暂时把 nums[j] 放到 nums[i] 的位置上来,并且记录上,此时 nums[i] 的元素本来的索引是 j。现在,我们就可以安心地把 nums[i] 放到 j 的位置了。同时,因为这已经是 nums[i] 正确的位置,取负数,即标记这个位置已经存放了正确的元素。

之后,我们继续处理当前的 nums[i],注意,此时这个新的 nums[i],本来的索引是 j。所以我们根据 j 算出它应该存放的位置,然后把这个位置的元素放到 nums[i] 中,取负做标记。

这个过程以此类推。这就是代码中 while 循环做的事情。

直到 nums[i] 的值也是负数,说明 i 的位置也已经是重新排列后的正确元素了,我们就可以看下一个位置了。

for 循环中,如果某一个元素已经是小于零了,说明这个位置已经是正确元素了,可以忽略。

这个算法虽然有两重循环,但是时间复杂度是 O(n) 的,因为每个元素最多会被重新排列一次,然后会被最外面的 for 循环访问一次。一旦重新排列过,for 的访问只是一次 if 判断而已。

当然,最后,数组中的所有元素还需要从负数转换回正数。

我的参考代码(C++):

class Solution {
public:
    vector<int> shuffle(vector<int>& nums, int n) {

        for(int i = 0; i < 2 * n; i ++)
            if(nums[i] > 0){
                
                // j 描述当前的 nums[i] 对应的索引,初始为 i
                int j = i; 

                while(nums[i] > 0){

                    // 计算 j 索引的元素,也就是现在的 nums[i],应该放置的索引
                    j = j < n ? 2 * j : 2 * (j - n) + 1; 

                    // 把 nums[i] 放置到 j 的位置,
                    // 同时,把 nums[j] 放到 i 的位置,在下一轮循环继续处理
                    swap(nums[i], nums[j]); 

                    // 使用负号标记上,现在 j 位置存储的元素已经是正确的元素了 
                    nums[j] = -nums[j]; 
                }
            }

        for(int& e: nums) e = -e;
        return nums;
    }
};

觉得有帮助请点赞哇!



## 统计信息
| 通过次数 | 提交次数 | AC比率 |
| :------: | :------: | :------: |
|    54672    |    64899    |   84.2%   |

## 提交历史
| 提交时间 | 提交结果 | 执行时间 |  内存消耗  | 语言 |
| :------: | :------: | :------: | :--------: | :--------: |
上一篇:
1471-数组中的 k 个最强值(The k Strongest Values in an Array)
下一篇:
1472-设计浏览器历史记录(Design Browser History)
本文目录
本文目录