How to return the tree node by index when tree nodes have subtree size?

元气小坏坏 提交于 2021-02-10 18:23:59

问题


Say I have this piece of demo data:

{
  size: 100,
  type: 'container',
  list: [
    {
      size: 30,
      type: 'container',
      list: [
        {
          size: 10,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10]
        },
        {
          size: 15,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
        },
        {
          size: 25,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]
        }
      ]
    },
    {
      size: 50,
      type: 'container',
      list: [
        {
          size: 20,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
        },
        {
          size: 25,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]
        },
        {
          size: 25,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]
        }
        ,
        {
          size: 30,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30]
        }
      ]
    },
    {
      size: 20,
      type: 'container',
      list: [
        {
          size: 5,
          type: 'leaf',
          list: [1,2,3,4,5]
        },
        {
          size: 15,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
        }
      ]
    }
  ]
}

Note, I just filled the so-called "leaf" nodes with integers to show their array positions. But they could be filled with any JavaScript objects (Arrays, objects, strings, numbers, etc.). The leaf nodes can have max 32 items, but I don't think that really matters for this question. The container nodes can only have 32 direct children as well.

How do you say getLeafContaining(tree, index), where it will return you the leaf which has the item at the global index, as well as the relative index. I say "global index" because this is the index of the leaf node if you were to take all of the leaf nodes as sequential.

What I have done so far is this:

const getLeafContaining = (tree, index) => {
  if (index > tree.size - 1) {
    return { node: null, index: -1 }
  }

  let nodes = [tree]
  let startSize = 0
  a:
  while (true) {
    b:
    for (let i = 0, n = nodes.length; i < n; i++) {
      let node = nodes[i]
      let endSize = startSize + node.size
      if (startSize <= index && index <= endSize) {
        if (node.type == 'container') {
          nodes = node.list
          break b
        } else {
          let relativeIndex = index - startSize
          return { node, index: relativeIndex }
        }
      } else {
        startSize = endSize
      }
    }
  }
}

const tree = {
  size: 100,
  type: 'container',
  list: [
    {
      size: 30,
      type: 'container',
      list: [
        {
          size: 10,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10]
        },
        {
          size: 15,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
        },
        {
          size: 25,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]
        }
      ]
    },
    {
      size: 50,
      type: 'container',
      list: [
        {
          size: 20,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
        },
        {
          size: 25,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]
        },
        {
          size: 25,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]
        }
        ,
        {
          size: 30,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30]
        }
      ]
    },
    {
      size: 20,
      type: 'container',
      list: [
        {
          size: 5,
          type: 'leaf',
          list: [1,2,3,4,5]
        },
        {
          size: 15,
          type: 'leaf',
          list: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
        }
      ]
    }
  ]
}

console.log(getLeafContaining(tree, 22))

It seems to be correct but I can't tell. How to you robustly implement this?


回答1:


No, it's incorrect, index <= endSize should be index < endSize. In the worst case this would lead to an infinite loop on an empty node.

Also a recursive solution would have been much simpler than the iterative version:

const getLeafContaining = (tree, index) => {
  if (index < tree.size) {
    if (node.type == 'leaf') {
      return { node, index };
    } else if (node.type == 'container') {
      let before = 0
      for (const node of nodes) {
        const after = before + node.size;
        if (index < after) {
          return getLeafContaining(node, index - before);
        }
        before = after;
      }
    }
  }
  return { node: null, index: -1 }
}

An alternative to the accumulating before sum would be to decrement index:

      for (const node of nodes) {
        if (index < node.size) {
          return getLeafContaining(node, index);
        }
        index -= node.size;
      }


来源:https://stackoverflow.com/questions/65553816/how-to-return-the-tree-node-by-index-when-tree-nodes-have-subtree-size

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!