给定其扁平化的数组索引,如何在数组树中找到节点的路径?

问题描述

以下是this answer一个相关问题的摘要

const data = [
 1,2,3,4,5,6,7,8,9,1,0
]

function divide(data,size) {
  const result = []

  for (let i = 0; i < data.length; i += size) {
    const chunk = data.slice(i,i + size);
    result.push(chunk)
  }

  if (result.length > size) {
    return divide(result,size)
  }

  return result;
}

const result = divide(data,5);
console.log(result)

data数组只是一个整数数组。但是,当您通过divide运行它时,它会生成一个嵌套数组树,其中每个数组最大为size。在“已编译”数组的树形版本中,如何说“给我42号商品”?例如,我想在这里使用这个 2 值,即数字42(索引41),如果这是树的样子,则为:

[                                                                                       ]
 [                   ],[                   ],[                   ]
  [ ],[ ],[ ]   [ ],[ ]
   1   6   1   6   1     6   1   6   1   6     1   6   1   6   1     6   1   6   1   6
   2   7   2   7   2     7   2   7  (2)  7     2   7   2   7   2     7   2   7   2   7
   3   8   3   8   3     8   3   8   3   8     3   8   3   8   3     8   3   8   3   8
   4   9   4   9   4     9   4   9   4   9     4   9   4   9   4     9   4   9   4   9
   5   0   5   0   5     0   5   0   5   0     5   0   5   0   5     0   5   0   5   0

它的路径是[0,1]。给定数组中的索引41,如何快速最佳地获取此路径?给定上面的divide函数以及将数组任意分块为不同大小的容器,一般方程是什么?


根据@Quade的回答,这是迄今为止我最好的开始:

let i = 42
let s = 5
let d = 3
let x = 0
let v = new Array(d)
while (d) {
  if (i < s ** d) {
    v[x] = 0
  } else {
    v[x] = Math.floor(i / (s ** d))
  }
  d--
  x++
}

解决方法

基本上,路径是以基数size表示的索引,其中基数是除法的最大数组大小。例如,以5为底的41是131。因此[1、3、1]。我认为您错误地为此前缀加上了0(只需在代码段中尝试console.log(result[1][3][1]))即可。

现在,对于较低的索引,您将需要预填充一个或多个零,以便给定树的路径长度始终相同。您需要的位数取决于树的深度,该深度由数据中的最大索引确定。在您的示例中,数据的大小为100,因此最大索引为99。以5为底的99仍为3位数字。因此,在这种情况下,任何路径都应包含3位数字。

关于您的实现的评论

当数据大小小于块大小时,divide函数当前无法产生正确的结果。在这种情况下,它应该只返回原始数组,但是您的代码仍然将其包装在另一个数组中。

解决方法是在开始时进行大小检查:

if (data.length <= size) return data;

实施

// Get path. Note that the actual tree is not needed; just the data size and chunk size
function getPath(chunkSize,dataSize,index) {
    if (index >= dataSize) throw new Error("index out of range");
    // Take logarithm,base chunkSize,from the highest possible index (i.e. dataSize - 1)
    let depth = Math.floor(Math.log(dataSize - 1) / Math.log(chunkSize)) + 1;
    let path = [];
    for (let i = 0; i < depth; i++) {
        // get each "digit" of the index when represented in base-chunkSize
        path.push(index % chunkSize);
        index = Math.floor(index / chunkSize);
    }
    return path.reverse();
}

let path = getPath(5,100,41);
console.log("path",path);

// Create the tree and extract that number at that index:
const data = [
 1,2,3,4,5,6,7,8,9,1,0
]

// I moved the base case detection first,so it works correctly 
//   for trees that are just the original array.
function divide(data,size) {
    if (data.length <= size) return data;
    const result = [];
    for (let i = 0; i < data.length; i += size) {
        result.push(data.slice(i,i + size))
    }
    return divide(result,size);
}

const tree = divide(data,5);

// Now get the value using the path we got
let drill = tree;
while (path.length) {
    drill = drill[path.shift()];
}
console.log("value at path",drill);

,

我将给出一个运行该示例的算法,然后让您编写一般形式。设max = 5和element = 42nd

这是一种回想,在每个递归步骤中都向下移动了一层。您的每个步骤都位于42所属的高级数组中。

从顶层数组开始。它可以包含125 = 5x5x5的元素数量(以概括max ^ number_of_sub_layers个)。 42小于125,因此包含在此数组中。您有结果[0]的前0个

现在要知道下一步将进入哪个子层,您可以计算42 // 25(25 = 5x5是每个子数组可以包含的元素数)。您得到42 // 25 = 1,所以42在第二个数组(索引为1)中,现在为[0,1]。

现在,您对第二个数组执行相同的操作。但是数字为42%25 = 17,因为第一个数组中包含25个元素。您计算17 // 5(5是每个子数组包含的元素数)。您得到17 // 5 = 3,因此42将在该数组的第四个子数组中(索引3)。所以现在你有[0,1,3]

然后,当您到达最后一层的数组时,该元素将处于17%5 =第二个位置。

更新

由于我对javascript不太熟悉,这是我在python中的处理方式。代码可以更简洁,但是可以完成工作。

result = []
nblayers = 3 # can be easily computed given your input array data,I let you do it

def compute(array,size,element,depth):
    """
    size plays the role of 5 in the example
    element plays the role of 42
    depth starts with 0 in the top layer array and increments by 1 each level. 
    """
    nbElements = size ** (nblayers - depth) # max Number of elements that the array can contain
    nbChildElements = size ** (nblayers - depth - 1) # max number of elements each sub array can contain
    if element > nbElements: # element index is not in the list 
        return False

    if depth == nblayers - 1: # if we are at the leaf array
        if element < len(array):
            result.append(element)
            return True
        else: 
            return False # this can happen only for the last subarray,because only it can contain fewer elements

    else:
        childArray = element // nbChildElements # child array in which this element will appear
        result.append(childArray)
        return compute(array[childArray],(element%nbChildElements),depth+1)



#here is you call the function with your inputs
compute(data,42,0)

更新2 关于Python运算符和函数:

  • **:就是力量
  • []:是一个空列表
  • len(list):返回列表的长度
  • //:整数除法。例如:5 // 3 = 1
  • %:是模运算。例如:5%3 = 2
  • list.append(element):将element添加到列表list
  • array [index]:返回index的索引array处的元素
,

您可以将所需的索引转换为带有基数的字符串,并以最大长度的最大值填充字符串。

此方法适用于基础,直到10

const
    getPath = (value,max,base) => value
        .toString(base)
        .padStart(max.toString(base).length,0)
        .split('');

console.log(getPath(41,99,5))

要获得更大的基数,直到36,您都需要添加一个映射以获得数字。

const
    getPath = (value,base) => Array.from(
        value.toString(base).padStart(max.toString(base).length,0),v => parseInt(v,base)
    );

console.log(getPath(255,255,16))