如何使用递归在JavaScript中扁平化数组
什么是递归?
递归只是意味着一个调用自己的函数。你可能会立即问,如果一个函数继续调用自己,它会是一个无限循环吗?是的——你是对的!
为了解决这个问题,一旦我们完成任务,我们使用一些条件(很可能是if条件)来停止递归函数调用。这些条件被称为基本案例。
让我们从一个例子开始。假设我想打印从1到N的数字(包括)。通常,你会为它写一个for循环,对吗?类似的东西:
打印1到N的功能(迭代解决方案)
如果我想用递归编写代码来打印1到N怎么办?
要为上述内容编写递归函数,我们必须问以下两个问题:
- 我们的递归函数什么时候应该停止?答案:在达到N+1时,因为我们必须从1到N (包括) 打印。
- 我们的递归函数应该做的实际工作是什么?答案:将值打印到控制台。
因此,简而言之,继续打印值,直到我们达到N+1。
根据我们刚才讨论的第二个问题,我们的代码应该看起来像这样:
上面的代码也打印1到N(5),对吗?这条代码的实际工作是将值打印到控制台。
现在,与其手动调用相同的函数,不如让代码为我们做。类似的东西:
如果您仔细观察上述代码,第6行print1ToNo(currentValue + 1)
正在使用新值(无论当前值是多少,加1,即currentValue + 1)调用相同的函数。它一直在这样做,直到当前值超过N,因为那是我们告诉它返回的时候。现在,这就是递归的含义。
如何思考递归方式
现在,让我们回到我们的主要问题——我们需要扁平化一个阵列。假设我们只有一个层次的嵌套(当然,我们可以有多个嵌套,但现在我们将处理一个)。数组应该看起来像这样:
示例输入 - 1 级嵌套
我们将按索引浏览输入数组索引。
索引0,值=1
索引0包含一个数字(值=1)。它只是一个数字,而不是一个数组。我们需要扁平化数字吗?不!它们将成为输出阵列的一部分。也就是说,我们不需要对数字做任何特别的事情,我们只特别关注数组。
因此,我们的规则是,如果是一个数字,将其推送到输出数组,然后转到下一个索引(即此处的索引1)。
索引1,值=2
索引1还包含一个数字(值=2)。我们需要扁平化数字吗?不!它们将成为输出阵列的一部分。 因此,按照我们的规则,如果是数字,请将其推送到输出数组,然后转到下一个索引(此处索引2)。
索引2,值=[3,4]
现在,索引2是一个数组([3,4]), 而不是数字。所以现在我们必须想办法把它夷为平地。
如果我给你一个数组[3,4],并告诉你把它弄平呢?您将像我们之前一样开始按索引浏览数组元素索引。然后你可能会意识到3只是一个数字,所以把它推到输出数组,然后转到下一个索引。
Well in the next index, 4 is also just a number, so push it to the output array. And we're done! Well, why don't you do that same on index 2 ( [ 3, 4 ] )
of our input array, then?
你一定在想,好吧,这么说很容易!如何在代码中做到这一点!?这就是递归进入画面的地方。 每当我们遇到数组时,我们都会告诉递归函数将该数组作为新的输入,并为我们解决它。
将所有内容置于上下文中,如果只是一个数字,不要做任何事情,只需将该数字推送到我们的输出数组,然后转到下一个索引。
如果是数组,则将该数组作为新输入,并开始做我们之前所做的。(我们将使用递归来完成这一部分)
问题的解决方案
好吧,提醒一下,这是我们的问题:
您将获得一个数组,其中包含数字和嵌套数字数组。您的工作是返回一个新的数组,该数组以线性方式包含所有数字,而无需任何嵌套。请记住,筑巢可以是任何级别的深度。
以下是使用递归解决我们问题的办法:
解决方案 - 代码
如果您仔细查看上述代码片段中名为递归的函数,我们正在检查我们当前所在的数组元素是否是数组。名为**index
的变量用于表示我们在inputArray
中的当前索引。**
如果它不是数组,我们只需将该元素推送到我们的输出数组中,然后转到下一个索引。否则,我们将使用索引变量指向的数组开始一个新的函数调用(递归)。
这段代码适用于任何级别的嵌套,而不仅仅是1级的嵌套!为什么会这样?每当我们找到一个数组而不是一个数字时,我们都会发起一个新的递归调用,该数组作为递归调用的输入。
因此,无论我们有多少嵌套数组,递归都会持续下去,直到我们找到一个数字,这样我们就可以开始将其推送到输出数组!
这就是递归在幕后的工作原理(对于上一个示例):
事情是如何完成的!
结论
现在,您知道如何使用递归扁平化数组。当涉及到时间和空间的复杂性时,递归是一种不适合的方法。
转载自:https://juejin.cn/post/7136940699580104712