在循环中数组解构无法执行(node环境)

新手上路,请多包涵

直接上代码:
在 node 环境中,交换1能够执行,交换2会陷入死循环
并且按照百度上的操作,进行添加括号,或者是添加分号都无法通过交换2正常执行代码
这是为什么?

function minNum (nums) {
  let result = nums.length + 1
  for ( let i = 0; i < nums.length; i++) {
    // console.log(nums[i] !== nums[nums[i] - 1]);
    const judge = nums[i] >= 1 && (nums[i] <= nums.length) && (nums[i] !== nums[nums[i] - 1])
    if (judge) {
      // 交换1
      const temp = nums[nums[i] - 1];
      nums[nums[i] - 1] = nums[i];
      nums[i] = temp;
      // 交换2
      // [nums[i], nums[nums[i] - 1]] = [nums[nums[i] - 1], nums[i]]    
      // console.log('柳树');
      i -= 1
    }
  }
  console.log(nums);
  for (let i = 0; i < nums.length; i++) {
    if (i+1 < nums[i]) {
       return i + 1
    }
  }
  return result
}
const res = minNum([7, 2, 6, 1, 8, 9, 11, 12])
console.log(res);

(ps:这是一道面试题,求数组中未出现的最小正整数,要求时间复杂度为O(n))

阅读 1.3k
1 个回答

这跟循环无关,是解构交换的问题。一般情况下解构交换是没有问题的,但是从这里发生的现象来看

[nums[i], nums[nums[i] - 1]] = [nums[nums[i] - 1], nums[i]] 这句话的执行顺序貌似是(在 i == 0 时)

  1. nums[i] = nums[nums[i] - 1],即 nums[i] = nums[7 - 1] = 11
  2. nums[nums[i] - 1] = nums[i],按理说应该是 nums[7 - 1] = nums[0] = 7,但它算成了 nums[11 - 1] = nums[0] = 7,即 nums[10] = 7,结果得到了 [ 11, 2, 6, 1, 8, 9, 11, 12, <2 empty items>, 7 ]

为什么会这样,确实有点想不通,所以用 Proxy 写了个代理来跟踪了一下:

let nums = new Proxy([7, 2, 6, 1, 8, 9, 11, 12], {
    get: function (target, key, receiver) {
        const value =  Reflect.get(target, key, receiver);
        console.log(`get ${key} with ${value}`);
        return value;
    },
    set(target, key, value, receiver) {
        console.log(`set ${key} to ${value}`);
        return Reflect.set(target, key, value, receiver);
    }
});

let i = 0;
[nums[i], nums[nums[i] - 1]] = [nums[nums[i] - 1], nums[i]];

得到这样一个结果:

get 0 with 7
get 6 with 11
get 0 with 7
set 0 to 11
get 0 with 11
set 10 to 7

注意到倒数第 2 行是重新对索引 0 取了值的。

再尝试,如果直接用固定索引交换,是这样

[nums[0], nums[6]] = [nums[6], nums[0]];
// get 6 with 11
// get 0 with 7
// set 0 to 11
// set 6 to 7

并没有重新取值。

所以猜测:由于计算过程中检测到 nums[0] 发生了变化,所以重新进行了一次 get,造成了上面的结果。至于为什么会这样,可能需要从 ECMAScript-262 Specification 中去找答案了。


补充,把交换的两个对象交换了一下位置

[nums[nums[i] - 1], nums[i]] = [nums[i], nums[nums[i] - 1]];

输出

get 0 with 7
get 0 with 7
get 6 with 11
get 0 with 7
set 6 to 7
set 0 to 11

set 6 之前也重新 get 了一次。所以应该不是因为 num[0] 发生了变化才重新取值的,而是在对 nums[nums[i] - 1] 赋值之前一定会计算一下 nums[i]

这么说来,之前是想复杂了。实事就是,先计算等号右边的值,然后对等号左边的变量依次赋值。如果需要计算(比如计算索引)就依次先计算,再赋值

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题