繁体   English   中英

使用循环递归地展平数组的时间复杂度是多少?

[英]What is the time complexity of flattening an array recursively utilizing a loop?

我在网上对这个解决方案做了一些研究,我看到了很多相互矛盾的信息,有些说它是指数的,有些说它是线性的。 我想知道在循环内利用递归展平深层嵌套结构的最坏情况的时间复杂度是多少?

这是我的代码:

const flattenDeep = (array) => {
  const flat = [];
  for (let element of array) {
    Array.isArray(element)
      ? flat.push(...flattenDeep(element))
      : flat.push(element);
  }
  return flat;
}

不,您显示的代码既没有指数时间复杂度也没有线性时间复杂度。

在我们确定任何算法的复杂性之前,我们需要决定如何测量输入的大小。 对于展平数组的特殊情况,存在许多选项。 我们可以统计输入中arrays的个数,数组元素的个数(所有数组长度之和),平均数组长度,所有arrays中非数组元素的个数,一个数组元素是数组的可能性, arrays等元素的平均数量

我认为衡量这个问题的算法最有意义的是整个输入中数组元素的数量——我们称之为e以及这些元素的平均深度——我们称之为d

现在有两种解决这个问题的标准方法。 你展示的算法

const flattenDeep = (array) => { const flat = []; for (let element of array) { Array.isArray(element)? flat.push(...flattenDeep(element)): flat.push(element); } return flat; }

确实具有O(e * d)的时间复杂度。 这是天真的方法,也在较短的代码中展示

const flattenDeep = x => Array.isArray(x) ? x.flatMap(flattenDeep) : [x];

或者在稍长的循环中

const flattenDeep = (array) => {
  const flat = [];
  for (const element of array) {
    if (Array.isArray(element)) {
      flat.push(...flattenDeep(element))
    } else {
      flat.push(element);
    }
  }
  return flat;
}

它们都有一个问题,即它们或多或少是明确的嵌套循环,其中内部循环遍历递归调用的结果。 请注意,调用flat.push(...flattenDeep(element))中的传播语法基本上等于

for (const val of flattenDeep(element)) flat.push(val);

这非常糟糕,考虑一下最坏情况下输入[[[…[[[1,2,3,…,n]]]…]]]会发生什么。

第二种标准方法是直接将非数组元素放入最终结果数组 - 无需创建、返回和迭代任何临时 arrays:

function flattenDeep(array) {
  const flat = [];
  function recurse(val) {
    if (Array.isArray(val)) {
      for (const el of val) {
        recurse(el);
      }
    } else {
      flat.push(val);
    }
  }
  recurse(array);
  return flat;
}

这是一个更好的解决方案,它的线性时间复杂度为O(e) - d不再是一个因素。

  • 可以进行常规结构的探索,但是,对于使用寻址的模型,有必要控制探索,否则它可能会无限递归..即:

    变量 a = [ 1, 2, 3 ]; var t = [ ]; t.push(a); t.push(t);

    控制台日志(t);

无限循环示例

  • 行动:检查你的数组值是否已经探索过。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM