使用递归(并且不使用循环)展平嵌套数组

Posted

技术标签:

【中文标题】使用递归(并且不使用循环)展平嵌套数组【英文标题】:Flatten nested arrays using recursion (and without using loops) 【发布时间】:2019-07-04 08:21:24 【问题描述】:

我对问题的逻辑,使用以下内容作为输入。

var input = [['A','B'],1,2,3,['C','D']]
    使用 Array.isArray(input) 检查第一个元素是否为数组 如果第一个元素是数组,调用函数,第一个元素 ['A,'B'] 作为参数。 嵌套数组的第一个元素是“A”,它不是一个数组,因此将该元素推入结果数组,然后将该元素移出。重复函数调用。

当尝试使用递归展平嵌套数组时,我的函数输入变量不断被重新分配,从而阻止我使用原始数组再次调用该函数。如何防止原始输入变量被重新分配?

我知道这不是完整的解决方案,但是当我将第一个元素移出嵌套数组时,我被卡住了。

这个功能我是一步一步经历的,但一定有什么我遗漏的,另一双眼睛会有很大帮助。

我也一直在用我的chrome开发者工具,设置断点一步步监控功能。

//Defining original input variable
var input = [['A','B'],1,2,3,['C','D']]

function flat(array)
    var result = []
    var firstElement = array[0]

//CHECK IF FIRST ELEMENT IS ARRAY OR NOT
    if(Array.isArray(firstElement))
    return flat(firstElement)  
     


//IF ELEMENT NOT ARRAY, PUSH ELEMENT TO RESULT
    elseresult.push(firstElement)
    array.shift() //removing child element
    return (flat(array)) //call function on same array
    

if(array.length===0)return result


第一次迭代: firstElement = ['A','B'], Array.isArray(firstElement) 为真,因此调用 flat(firstElement)

第二次迭代: firstElement = 'A', Array.isArray(firstElement) 为假,所以我们 1. 下跳将这个元素推入结果 2. 使用 array.shift() 删除 'A' 3.调用flat(array),array现在是['B']

第三次迭代: firstElement = 'B', Array.isArray(firstElement) 为假 1. 跳下将这个元素推入结果,结果现在只有['B'],因为我在调用函数时重置了结果。 2. 使用array.shift()删除'B',数组现在是空的,->[] 3. 我怎样才能退出,并在原始输入数组上使用 flat()?

【问题讨论】:

为什么?只是为什么?这是一个纯粹的教学练习,在现实生活中你永远不需要这样的东西 @CristianTraìna 这是掌握递归的绝佳练习。 @CristianTraìna - 很高兴知道我不会在现实生活中使用它哈哈。我可以使用循环轻松完成此操作,但我正在尝试提高我的递归技能。它仍然摇摇欲坠。这只是练习题之一。 这是一个很好的练习。并且在现实生活中很有用,因为嵌套数组还可以包含更多嵌套数组,例如树。 【参考方案1】:

如果第一个元素是数组,您的代码不会考虑以下元素。下面的解决方案使用array.concat(...) 来组合递归的结果(沿着树向下),还可以组合处理列表其余部分的结果(在同一级别)。将问题可视化为一棵树,通常有助于 IMO 的递归:

 [] 1 2 3 []
 |         |
A []      C D
   |
  B C

所以这里可能更清楚一点,我们必须将递归的结果和向右走“一步”(再次递归)的结果连接起来,否则这将是一个循环迭代数组。

var input = [['A',['B', 'C']],1,2,3,['C','D']]

function flat(array) 
    var result = []
    if (array.length == 0) return result;
  
    if (Array.isArray(array[0])) 
        result = result.concat(flat(array[0]));    // Step down
     else 
        result.push(array[0]);
    
    result = result.concat(flat(array.slice(1)))   // Step right

    return result;


console.log(flat(input));
// ["A", "B", "C", 1, 2, 3, "C", "D"]

这有点类似于带有循环的版本:

function flat(array) 
    var result = []

    for (var i = 0; i < array.length; i++) 
        if (Array.isArray(array[i])) 
            result = result.concat(flat(array[i]));
         else 
            result.push(array[i]);
        
    
    return result;

编辑:出于调试目的,您可以跟踪深度以帮助了解发生的情况:

var input = [['A',['B', 'C']],1,2,3,['C','D']]
function flat(array, depth) 
    var result = []
    if (array.length == 0) return result;

    if (Array.isArray(array[0])) 
        result = result.concat(flat(array[0], depth + 1));
     else 
        result.push(array[0]);
    
    var res1 = flat(array.slice(1), depth);
    console.log("Depth: " + depth + " | Concatenating: [" + result + "]  with: [" + res1 + "]");
    result = result.concat(res1)

    return result;


console.log(flat(input, 0));

【讨论】:

有一个澄清问题,但首先,非常感谢,您的第一个解决方案是我试图编写的。 第一次迭代倒数第二行flat(array.slice(1)),这里array是原始输入var input = [['A',['B', 'C']],1,2,3,['C','D']]第二次迭代flat(array.slice(1))['A',['B','C']],然后我们切掉'A' 第三次迭代flat(array.slice(1))['B','C'],直到array.length===0 我们是如何爬出嵌套数组的,当我们调用flat(array.slice(1)) 时,@987654335 @实际上是指我们原来的输入? 它与返回“结果”值的调用堆栈有关吗?为冗长的后续行动道歉。我一直在为此使用 Pythontutor 可视化工具,我想我只需要更慢地了解它。 @MaxHsu “爬出”是由函数调用堆栈隐式进行的。拿一张纸,试着跟随周围的事情。 @MaxHsu 哦,.slice(1) 在调用 .shift() 后留下的数组与您的数组相同。 @MaxHsu 我添加了一个代码-sn-p,它输出更多信息。 array.slice(1) 从索引 1 返回 array(因此不包括第一个元素)。在第一次迭代中,这意味着 1,2,3,['C','D']] - 对应于我帖子中树中顶层的“其余部分”。所以最终,递归返回到左上角的元素并被返回。【参考方案2】:

如果您想避免循环,并且我正在考虑将 concating/spreading 数组作为循环,您需要将结果数组传递给您的函数。

const input = [['A', 'B'], 1, 2, 3, ['C', 'D']]

// Start the function with an empty result array.
function flat(array, result = []) 
  if (!array.length)
    return result

  // Extract first element.
  const first = array.shift()

  // Call the function with the array element and result array.
  if (Array.isArray(first))
    flat(first, result)
  // Or add the non array element.
  else
    result.push(first)

  // Call the function with the rest of the array and result array.
  flat(array, result)
  return result



console.log(flat(input))

【讨论】:

@destroyer - 感谢这个。我不愿意使用第二个论点,因为我坚持试图理解我的第一次尝试。但是,我能够更直观地掌握您的解决方案。欣赏这个解决方案!【参考方案3】:

如果您使用 javascript,这是我的答案

您可以使用下面一行代码来展平n级嵌套数组

let flattendArray = input.flat(Infinity);

或者使用reduce和concat来使用这种方法

function flatDeep(arr, d = 1) 
   return d > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val), [])
                : arr.slice();
;

Refer this link

【讨论】:

以上是关于使用递归(并且不使用循环)展平嵌套数组的主要内容,如果未能解决你的问题,请参考以下文章

以递归方式展平包含未知级别的嵌套数组和映射的嵌套映射

javascript 通过递归来展平嵌套数组Ex:[1,2,3,[4,5,[7,8,[10,11,[12,13,[[[[[[14]]]]]]]]]] ]]

用于递归展平结果的 JS 数组串联

如何使用 flatten_json 递归地展平嵌套的 JSON

如何在 Clojure 中递归展平任意嵌套的向量和映射?

递归展平列表[重复]