繁体   English   中英

在 Javascript 中遍历此链表时,这种 O(N) 方法是避免 while 循环的唯一方法吗?

[英]Is this O(N) approach the only way of avoiding a while loop when walking this linked list in Javascript?

我有一个数据结构,它本质上是一个存储在 state 中的链表。它表示对基数 object 的 stream 更改(补丁)。它通过键链接,而不是通过 object 引用链接,以允许我简单地序列化和反序列化state。

它看起来像这样:

const latest = 'id4' // They're actually UUIDs, so I can't sort on them (text here for clarity)
const changes = {
  id4: {patch: {}, previous: 'id3'},
  id3: {patch: {}, previous: 'id2'},
  id2: {patch: {}, previous: 'id1'},
  id1: {patch: {}, previous: undefined},
}

有时,用户选择运行昂贵的计算,结果返回到 state。我们没有对应于每个更改的结果,但只有一些。 所以结果可能看起来像:

const results = {
  id3: {performance: 83.6},
  id1: {performance: 49.6},
}

给定更改数组,我需要获得最接近更改列表顶端的结果,在本例中为results.id3

我已经编写了一个 while 循环来执行此操作,目前它非常可靠:

    let id = latest
    let referenceId = undefined
    while (!!id) {
      if (!!results[id]) {
        referenceId = id
        id = undefined
      } else {
        id = changes[id].previous
      }
    }

该方法是 O(N),但这是病态的情况:我希望有一个很长的更改列表,但结果更新相当频繁,这样您只需返回几步即可找到匹配的结果。

虽然循环可能很脆弱

遵循 Gene Krantz 的伟大工作(阅读他的书“失败不是一种选择”以了解为什么 NASA 从不使用递归:)我尽量避免在代码库中使用 while 循环。 他们往往容易犯无意的错误。

例如,在这里进行无限循环所需要做的就是执行delete changes.id1

因此,我想避免该漏洞,而不是检索任何结果,因为可以处理不返回性能值; 但是用户的应用程序挂起真的很糟糕!

我尝试过的其他方法

排序数组 O(N)

为了避免 while 循环,我考虑将changes object 排序到按链表排序的数组中,然后简单地循环遍历它。

问题是我必须先遍历整个更改列表才能按排序顺序获取数组,因为我没有存储排序键(这会违反链表的要点,因为你不能再做 O( 1)插入)。

将 id 压入数组并不是一项繁重的操作,但仍然是 O(N)。

问题

有没有一种方法可以在不使用 while 循环并且不使用 O(N) 方法将链表转换为普通数组的情况下遍历这个链表?

在写出问题时,我意识到与其完全避免 while 循环,不如添加一个执行计数和一个逃生舱口,这应该足以达到目的。

该解决方案使用Object.keys() ,它严格来说是 O(N) 所以在技术上不是问题的正确答案 - 但它非常快。

如果我需要更快,我可以将changes重组为map而不是一般的 object 并根据此答案访问changes.size

    let id = latest
    let referenceId = undefined
    const maxLoops = Object.keys(changes).length
    let loop = 0
    while (!!id && loop < maxLoops) {
      loop++
      if (!!results[id]) {
        referenceId = id
        id = undefined
      } else {
        id = changes[id].previous
      }
    }

由于最后只需要 append 并可能从末尾删除,因此所需的结构是stack 在 JavaScript 中,实现堆栈的最佳数据结构是数组——使用其pushpop功能。

那么你可以做这样的事情:

 const changes = []; function addChange(id, patch) { changes.push({id, patch}); } function findRecentMatch(changes, constraints) { for (let i = changes.length - 1; i >= 0; i--) { const {id} = changes[i]; if (constraints[id]) return id; } } // Demo addChange("id1", { data: 10 }); addChange("id2", { data: 20 }); addChange("id3", { data: 30 }); addChange("id4", { data: 40 }); const results = { id3: {performance: 83.6}, id1: {performance: 49.6}, } const referenceId = findRecentMatch(changes, results); console.log(referenceId); // id3

根据您想对该referenceId执行的操作,您可能希望findRecentMatch返回changes中的索引而不是更改 ID 本身。 这使您有可能仍然检索id ,但也可以剪辑changes列表以在该“版本”结束(即,就好像您弹出了到该点的所有条目,但随后在一个操作中)。

暂无
暂无

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

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