【leetcode】【2022/8/29】1470. 重新排列数组

问题描述:

  • 给定一个数组 nums,数组中有 2n 个元素,按 [x1,x2,...,xn,y1,y2,...,yn] 的格式排列。
    • 将数组按 [x1,y1,x2,y2,...,xn,yn] 格式重新排列,返回重排后的数组。
  • 满足 1 <= nums[i] <= 10^3

核心思路:

  • 该题虽然是简单题,可以直接用空间复杂度为 O(n) 的方法来解题,但是精彩的地方在于考虑以 O(1) 的空间复杂度实现原地排列。
  • 有两种方法实现原地解题,均来自一位网友的题解,链接放在本文的最后。
  • 方法一:利用位运算
    • 这个方法非常巧妙,因为数组中数值的取值范围为 [1, 1000],也就是说所有数值均低于 2^10 = 1024,也就是说所有数值均只有低 10 位是有意义的。
    • 而一个整型数值有 32 位,可以存储 210 位的数值,因此可以用低 10 位表示数组中原数值用中 10 位表示重排列后数值
    • 如此就好像是利用数值中剩余的位数组成了新的结果数组。【最后把所有数值右移 10 位即可得到结果】
  • 方法二:不断交换直到当前位置正确,满足后将数值置为负数,相当于标记为正确。
    • 这个方法是比较常规的类似于原地哈希思想,我一开始也往这个方向想,在当前位置希望通过不断交换数值直到位置满足,但没有想到能用负数来标记结果正确。
      • 所谓的原地哈希算法,就是将原数组作为哈希表,建立原数值对应数值的映射关系,通常是通过不断交换的操作使得数值放置在应该放置的位置上。【具体题目可看 leetcode 41 缺失的第一个正数和 leetcode 287 寻找重复数,这两题是原地哈希的典型题目】
      • 方法一其实也是原地哈希算法的体现,但是通常原地哈希算法都伴随着交换,所以方法二更常规。

代码实现:

  • 方法一代码实现:【贴自参考内容中链接的题解】
    class Solution
    {
    public:
        vector<int> shuffle(vector<int>& nums, int n)
        {
            for(int i = 0; i < 2*n; ++i)
            {
                int j = i < n ? i*2 : 2*(i-n) + 1; // 要推导出对应关系
                nums[j] |= (nums[i] & 1023) << 10; // index i to index j
            }
            for(int& num : nums)
                num >>= 10;
            return nums;
        }
    };
    
  • 方法二代码实现:【贴自参考内容中链接的题解】
    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;
        }
    };
    

参考内容:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!我是 ChitGPT AI,很高兴能够为您解答问题。 针对您提出的问题,我可以给出以下解答: 对于输入的正整数 target,可以通过双指针法求出所有和为 target 的连续正整数序列。具体地,我们设两个指针 small 和 big 分别表示连续正整数序列的起点和终点,初始时两个指针都指向 1,即序列为 {1}。然后如果序列的和小于 target,我们将 big 指针右移一位,扩展序列的长度;如果序列的和大于 target,我们将 small 指针右移一位,缩小序列的长度;如果序列的和等于 target,即找到了一个符合要求的连续正整数序列,将其加入结果列表中,并将 big 指针右移一位,继续寻找下一个连续序列。 具体实现过程中,我们需要注意以下几点: 1. 如果区间和小于 target,应该扩展序列的末尾,即将 big 指针右移一位; 2. 如果区间和大于 target,应该缩小序列的开头,即将 small 指针右移一位; 3. 如果 big 指针移动到了 target 的一半位置,就没有必要再继续往下寻找了,因为后面的数肯定不符合要求。 下面是完整代码实现,您可以参考: ```python def findContinuousSequence(target: int) -> List[List[int]]: res = [] small, big = 1, 2 while big <= target // 2 + 1: s = sum(range(small, big + 1)) if s == target: res.append(list(range(small, big + 1))) big += 1 elif s < target: big += 1 else: small += 1 return res ``` 希望我的回答能对您有所帮助,如果您还有其他问题,欢迎随时提出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值