Javascript:使用递归将多维数组展平到位

Posted

技术标签:

【中文标题】Javascript:使用递归将多维数组展平到位【英文标题】:Javascript: Flatten multidimensional array in place using recursion 【发布时间】:2015-12-06 02:56:08 【问题描述】:

我有以下代码可以展平多维数组

var x = [[[2, 3], 4], 5, [6, 7]];

function flatten(arr) 

  for (var i = 0; i < arr.length; i++) 
    if (arr[i].constructor === Array) 
      subArr = arr[i];
      // Shift the array down based on the space needed for the sub array
      for (var j = arr.length + subArr.length - 2; j > i + subArr.length - 1; j--) 
        arr[j] = arr[j - subArr.length + 1];
      
      // Insert sub array elements where they belong
      for (var k = 0; k < subArr.length; k++) 
        arr[i + k] = subArr[k]; 
      
      // Look at arr[i] again in case the first element in the subarray was another array;
      i--;
    
  


flatten(x);

这里是JSBin:http://jsbin.com/gazagemabe/edit?js,console

我想使用递归来做到这一点,但我最终被卡住了。我试图在不保留临时数组的情况下做到这一点,但是事情似乎不正常了。我觉得我缺少一些递归的核心原理。

我意识到我需要一个基本案例。我能想到的唯一情况是当前处于展平状态的数组没有子数组。但是由于我总是通过引用传递数组,所以我没有什么要返回的。

我的伪代码是

function flatten(arr) 

  loop through arr
    if arr[index] is an array
      increase the array length arr[index] length
      flatten(arr[index])
    else
      // unsure what to do here to modify the original array

【问题讨论】:

jsfiddle.net/50dnu97z ES6: babeljs.io/repl @zerkms - ES6 很漂亮,不是吗:p “我总是通过引用传递数组”---当你实现递归时,你应该忘记引用:你只接受和返回值。 @Nate 它取决于所使用的编程语言和数据结构。从 JS 开始 - 递归解决方案的效率会降低*。 | *在大多数情况下 【参考方案1】:

从内向外进行递归展平。首先在您找到的数组上调用flatten 函数,然后将该(现在是平面)数组的内容移动到父数组中。向后循环遍历数组,这样您就不必为插入的项目调整循环变量。

您知道插入的数组是平面数组,您可以使用splice 方法将数组替换为其项。

它是这样工作的:

start with
[[[2, 3], 4], 5, [6, 7]]

flatten [6,7] (which is already flat) and insert:
[[[2, 3], 4], 5, 6, 7]

flatten [[2, 3], 4] recursively calls flatten [2,3] and inserts in that array:
[[2, 3, 4], 5, 6, 7]
then it inserts [2, 3, 4]:
[2, 3, 4, 5, 6, 7]

代码:

function flatten(arr) 
  for (var i = arr.length - 1; i >= 0; i--) 
    if (arr[i].constructor === Array) 
      flatten(arr[i]);
      Array.prototype.splice.apply(arr, [i, 1].concat(arr[i]));
    
  


var x = [[[2, 3], 4], 5, [6, 7]];

flatten(x);

// Show result in snippet
document.write(JSON.stringify(x));

【讨论】:

Array.prototype.splice.apply(arr, [i, 1].concat(arr[i])); 很不错!【参考方案2】:

这是一个完全就地工作的纯递归版本,并且不构建中间数组。

// Deep-flatten an array starting at index `i`.
function flatten(a, i = 0)                                                                               

  if (i >= a.length)                                                                                      
    // Null case--we are off the end of the array.                                                        
    return;                                                                                               

  var elt = a[i];                                                                                         

  if (!Array.isArray(elt))                                                                                
    // Element is not an array. Proceed to next element.                                                  
    i++;                                                                                                  

  else                                                                                                    
    // Element is an array.                                                                               
    // Unroll non-empty arrays; delete empty arrays.                                                      

    if (elt.length)                                                                                      
      // Non-empty array.                                                                                 
      // Unroll it into head and tail.                                                                    

      // Shift elements starting at `i` towards end of array.
      // Have to do this recursively too--no loops!                                             
      (function shift(a, i, n = a.length)                                                                
        if (n > i ) a[n] = a[n-1], shift(a, i, --n);                                                      
      (a, i));                                                                                           

      // Replace elt with its head, and place tail in slot we opened up.                                  
      a[i] = elt.shift();                                                                                 
      a[i + 1] = elt;                                                                                     
                                                                                                         

  else                                                                                                    
    // Array is empty.                                                                                    
    // Delete the element and move remaining ones toward beginning of array. 
    // Again, have to do this recursively!                             
    (function unshift(a, i)                                                                              
      if (i < a.length) a[i] = a[i+1], unshift(a, ++i);                                                   
      else a.length--;                                                                                    
    (a, i));                                                                                             

  flatten(a, i);                                                                                          

                                                                                                         

var arr = [[[2, 3], 4], 5, [6, 7]];                                                                   
flatten(arr);                                                                                             
console.log(arr);                                                                                         

[2, 3, 4, 5, 6, 7]         

这个方案使用了一些 ES6 的零碎,比如默认的函数参数和Array.isArray。如果您没有可用的 ES6,请相应地进行调整。

【讨论】:

就地修改数据的函数使用不方便:不能组合。 确实,递归和就地算法之间存在一些矛盾。对于就地算法,递归在某种程度上仅限于作为循环的替代品,而不是作为组合部分结果的策略。 实际上与递归无关。任何不纯的函数都是不可组合的。

以上是关于Javascript:使用递归将多维数组展平到位的主要内容,如果未能解决你的问题,请参考以下文章

Javascript递归数组展平

如何在不使用 NumPy 复制的情况下展平多维数组的轴?

Javascript函数没有正确展平数组

在javascript中展平数组[重复]

如何在 PHP 中将多维数组“展平”为简单数组? [复制]

如何在 PHP 中将多维数组“展平”为简单数组? [复制]