likes
comments
collection
share

如何生成漩涡型二维数组?看完你就知道了!

作者站长头像
站长
· 阅读数 77

大家好,我是渡一前端子辰老师。

今天来给大家分享的是一道非常有意思的算法笔试题。他精准地考察了你对二维数组“坐标”的认知和理解。

快来测试一下自己的逻辑能力吧

如何生成漩涡型二维数组?看完你就知道了!

如上图所示,这道题要求我们生成一个二维数组,这里边的数字是呈漩涡状一直到数组的中心。

然后让你写一个函数,告诉你行和列的数量,让你生成这个二维数组。函数原型如下:

function vortex(n, m) {
  // n 是行数,m 是列数
  // 返回一个 n 行 m 列的二维数组
}

貌似看上去不是很难,让我们去写写试试。

解题思路

要解决这个问题,我们可以分为两个步骤:

  • 第一步,生成一个全是0的二维数组
  • 第二步,在数组里按照漩涡形式填充数字

第一步:生成全是0的二维数组

下面的 vortex 函数就是我们要实现的函数,告诉你行和列的数量,让你生成这个二维数组。函数原型如下:

function vortex(n, m) {}
console.log(vortex(5, 6));

那么首先要完成结果的二维数组,首先要生成一个全是 0 的二维数组。

如何生成漩涡型二维数组?看完你就知道了!

于是就有了这样一个代码。

function vortex(n, m) {
  const nums = new Array(n).fill(0).map(() => []);
  return nums;
}
console.log("vortex >>>", vortex(5, 6));

通过 new Array() 创建一个数组,长度是 n,然后每一行填充一个数组。

如何生成漩涡型二维数组?看完你就知道了!

这样我们就到了一个五行的数组。

列的话就简单了,我们只要在现在的行里填充指定的列数,然后每一列填充为 0 就可以了。

function vortex(n, m) {
  const nums = new Array(n).fill(0).map(() => new Array(m).fill(0));
  return nums;
}
console.log("vortex >>>", vortex(5, 6));

如何生成漩涡型二维数组?看完你就知道了!

这样我们就得到了一个指定行数指定列数的全是 0 的二维数组。

第二步:按照漩涡形式填充数字

接下来就要依次以螺旋形式在数组里填充数字了。

我们可以使用两个变量 i 和 j 来表示当前位置在数组中的坐标,分别对应横坐标和纵坐标。

如何生成漩涡型二维数组?看完你就知道了!

最开始是在左上角利用坐标去找到数组中对应的一项,给它填一个数字就行了,还需要一个变量 count 来表示当前要填充的数字。

然后循环填充就行了。

function vortex(n, m) {
  const nums = new Array(n).fill(0).map(() => new Array(m).fill(0));
  // 初始化坐标和计数器
  let i = 0,
    j = 0,
    count = 1;
  while (true) {
    // 填充当前位置
    nums[j][i] = count++;
    // 填充完之后改动 i 和 j
    // ...
  }
  return nums;
}

填充完了我们就要改动 i 和 j,然后根据改动后的值继续填充,但是现在我们还不知道如何改动。

我们可以这样想,无论我们是什么方向,你改动 i 和 j 无非就是在 i 上加某个值,在 j 上加某个值,我们可以用两个变量 stepi 来表示 i 加的值,stepj 来表示 j 加的值。

function vortex(n, m) {
  const nums = new Array(n).fill(0).map(() => new Array(m).fill(0));
  let i = 0,
    j = 0,
    count = 1;
    // 初始化步长
  let stepi = 1,
    stepj = 0;
  while (1) {
    nums[j][i] = count++;
    // 根据步长改动 i 和 j
    i += stepi;
    j += stepj;
    // ...
  }
  return nums;
}

比如这里 stepi 为 1,stepj 为 0,就意味着每次变动,i 加上 1,j 不变。

如何生成漩涡型二维数组?看完你就知道了!

也就是说方向是上图这样由左至右的。

那么有些同学可能会说这么做的话如何转向呢?这个并不是我们首要考虑的问题,我们首要考虑的是什么时候转向。

第一种:当下一个位置为空时要转向。

如何生成漩涡型二维数组?看完你就知道了!

第二种:当下一个位置已经填充过时要转向。

如何生成漩涡型二维数组?看完你就知道了!

换句话说,只要下个位置不是 0,就要转向,无论超过了数组下标是 undefined,还是下个位置已经填充过。

那么我们可以写一个辅助函数来判断是否需要转向,同样也知道如何在循环里什么时候转向了。

function vortex(n, m) {
  // etc...
  // 是否需要转向
  function isTurn() {
    // 值为 undefined 或值不是 0 的时候
    return !nums[j] || nums[j][i] !== 0;
  }
  while (1) {
    nums[j][i] = count++;
    i += stepi;
    j += stepj;
    if (isTurn()) {
      // 转向
      // ...
    }
  }
  // etc...
}

那么我们如何转向?转向的第一步是不是要退回一步,因为已经前进了一步才知道下一步不能走,所以我们要先回来。

也就是说让 i 和 j 加上的值再减去就行,退回一步之后我们在进行转向。

function vortex(n, m) {
  // etc...
   while (true) {
     nums[j][i] = count++;
     i += stepi;
     j += stepj;
     if (isTurn()) {
       // 转向前先退回一步
       i -= stepi;
       j -= stepj;
       // 退回之后再进行转向
       // ...
     }
  // etc...
}

如何转向就特别有意思了,我们来找找规律。

如何生成漩涡型二维数组?看完你就知道了!

通过上图我们可以知道,改变方向其实就是控制 stepi 和 stepj 的值。

也就是代码是这样写的。

function vortex(n, m) {
  // etc...
  while (1) {
    nums[j][i] = count++;
    i += stepi;
    j += stepj;
    if (isTurn()) {
      i -= stepi;
      j -= stepj;
      // 如果说 stepj 为 0 那么一定是在横向填充
      if (stepj === 0) {
        // 横向动变成纵向动
        stepj = stepi;
        stepi = 0;
      } else {
        // 纵向动变横向动
        stepi = -stepj;
        stepj = 0;
      }
    }
  }
  // etc...
}

现在我们已经转向了,转向后我们就要再次向前走一步了,所以利用新的 stepi 和 stepj 来走一步。

function vortex(n, m) {
  // etc...
  while (1) {
    nums[j][i] = count++;
    i += stepi;
    j += stepj;
    if (isTurn()) {
      i -= stepi;
      j -= stepj;
      if (stepj === 0) {
        stepj = stepi;
        stepi = 0;
      } else {
        stepi = -stepj;
        stepj = 0;
      }
      // 利用新的 stepi 和 stepj 来走一步。
      i += stepi;
      j += stepj;
    }
  }
  // etc...
}

这就完成了转向和继续前进了,但是什么时候我们退出循环呢?

就是说当我们即使转向也没有路的时候就退出循环。

function vortex(n, m) {
  const nums = new Array(n).fill(0).map(() => new Array(m).fill(0));
  let i = 0,
    j = 0,
    stepi = 1,
    stepj = 0,
    count = 1;
  function isTurn() {
    return !nums[j] || nums[j][i] !== 0;
  }
  while (1) {
    nums[j][i] = count++;
    i += stepi;
    j += stepj;
    if (isTurn()) {
      i -= stepi;
      j -= stepj;
      if (stepj === 0) {
        stepj = stepi;
        stepi = 0;
      } else {
        stepi = -stepj;
        stepj = 0;
      }
      i += stepi;
      j += stepj;
    }
    // 转向后也没有路就退出循环
    if (isTurn()) {
      break;
    }
  }
  return nums;
}
console.log("vortex >>>", vortex(5, 6));

现在我们已经全部实现了,运行一下看看。

如何生成漩涡型二维数组?看完你就知道了!

可以看到输出的结果是没问题的。

总结

这个算法题虽然不难,但是考察了我们对数组和循环的掌握程度,以及对方向和坐标的理解能力。

希望这篇文章能够帮助你学习和巩固这些知识点。

虽然我们解出了这道题,但是我一直在想,这个东西能不能有数学的解,就是给你一个 i 和 j,有没有一个数学公式,可以直接算出某个位置的值。

有思路的同学可以在评论区留言。

本文来源自渡一官方公众号:Duing,欢迎关注获取最实时、最新、最全、最深入的技术讲解

转载自:https://juejin.cn/post/7248853134784561189
评论
请登录