返回

用 shuffle 探究编码者的品味(面试问题)

前端

软件开发中的代码审查常常是一项艰巨的任务,尤其当你在与新的开发团队合作时。这时候,你很有可能看到一些让你疑惑的代码实现,例如,随机函数 shuffle 的使用。shuffle,顾名思义,就是将数组随机排序,常被开发人员用作实现随机功能。

我们来看一下 shuffle 可以如何体现出代码品味。

首先,思考一下这个问题:为什么要把 i 和 randomIndex 的声明放在最前端?

ES5 中的变量提升(ES6 中有无变量提升…)。

其次,我们看看这两个函数是怎样处理参数的:

function shuffle_1(array) {
  const n = array.length;
  for (let i = 0; i < n; i++) {
    const randomIndex = Math.floor(Math.random() * (n - i)) + i;
    const temp = array[i];
    array[i] = array[randomIndex];
    array[randomIndex] = temp;
  }
}

function shuffle_2(array) {
  const n = array.length;
  for (let i = 0; i < n; i++) {
    const randomIndex = Math.floor(Math.random() * n);
    const temp = array[i];
    array[i] = array[randomIndex];
    array[randomIndex] = temp;
  }
}

shuffle_1() 在每个循环中都计算 n - i,而 shuffle_2() 则只计算一次 n。这使得 shuffle_2() 在数组较大时具有更好的性能,因为 n - i 会随着循环次数的增加而变小。

最后,这两个函数是如何处理边界情况的:

function shuffle_3(array) {
  const n = array.length;
  for (let i = 0; i < n; i++) {
    const randomIndex = Math.floor(Math.random() * n);
    if (randomIndex === i) {
      continue;
    }
    const temp = array[i];
    array[i] = array[randomIndex];
    array[randomIndex] = temp;
  }
}

function shuffle_4(array) {
  const n = array.length;
  for (let i = 0; i < n; i++) {
    const randomIndex = Math.floor(Math.random() * n);
    if (randomIndex === i) {
      randomIndex = (randomIndex + 1) % n;
    }
    const temp = array[i];
    array[i] = array[randomIndex];
    array[randomIndex] = temp;
  }
}

shuffle_3() 在遇到 randomIndex 等于 i 的情况时,会跳过本次循环,继续进行下一次循环。shuffle_4() 则会在遇到 randomIndex 等于 i 的情况时,将 randomIndex 加 1 并取模 n,确保 randomIndex 不等于 i。

通过以上三个方面的比较,我们可以看到 shuffle_2() 和 shuffle_4() 在性能和边界情况处理方面都优于 shuffle_1() 和 shuffle_3()。因此,在实际开发中,我们应该选择 shuffle_2() 和 shuffle_4()。