Algorithm 查找二进制堆的最后一个元素
引述: 完全可以接受使用 传统的二叉树数据结构 实现二进制堆。有 查找相邻对象的问题 上最后一级的元素 添加元素时的二进制堆 这是可以解决的 算法上 有没有关于这种算法如何工作的想法 我找不到关于这个问题的任何信息,因为大多数二进制堆都是使用数组实现的 谢谢你的帮助Algorithm 查找二进制堆的最后一个元素,algorithm,data-structures,binary-tree,binary-heap,Algorithm,Data Structures,Binary Tree,Binary Heap,引述: 完全可以接受使用 传统的二叉树数据结构 实现二进制堆。有 查找相邻对象的问题 上最后一级的元素 添加元素时的二进制堆 这是可以解决的 算法上 有没有关于这种算法如何工作的想法 我找不到关于这个问题的任何信息,因为大多数二进制堆都是使用数组实现的 谢谢你的帮助 最近,我注册了一个OpenID帐户,无法编辑我的初始帖子或评论答案。这就是为什么我通过这个答案作出回应。对不起 引用米奇小麦的话: @伊塞:你的问题是“我怎么找到你?” 二进制堆的最后一个元素“ 是的。 或者更准确地说,我的问题
最近,我注册了一个OpenID帐户,无法编辑我的初始帖子或评论答案。这就是为什么我通过这个答案作出回应。对不起
引用米奇小麦的话: @伊塞:你的问题是“我怎么找到你?” 二进制堆的最后一个元素“ 是的。 或者更准确地说,我的问题是:“如何找到非基于数组的二进制堆的最后一个元素?” 引用灭火: 你在什么环境下工作 问这个问题?(即,是否存在 一些你想解决的具体问题 解决吗?) 如上所述,我想知道一种“查找非基于数组的二进制堆的最后一个元素”的好方法,这是插入和删除节点所必需的 引用罗伊的话: 对我来说,这似乎是最可以理解的 只需使用普通的二叉树 结构(使用pRoot和节点 定义为[数据,pLeftChild, pRightChild]),并添加两个额外的 指针(pInsertionNode和 质体节点)。pInsertionNode和 pLastNode都将在 插入和删除子程序 在数据更新时保持它们的最新状态 内部结构发生变化。这 允许O(1)访问两个插入 结构的点和最后一个节点 是的,这应该行得通。如果我没有弄错的话,当插入节点和插入节点的位置由于删除/插入而更改为另一子树时,查找插入节点和最后一个节点可能有点棘手。但我会试试看 引用扎克·斯克里维纳的话: 先做深度测试怎么样 搜索 是的,这将是一个很好的方法。我也要试试 我仍然想知道,是否有一种方法可以“计算”最后一个节点和插入点的位置。具有N个节点的二进制堆的高度可以通过取大于N的2的最小幂的log(基数2)来计算。也许也可以计算最深级别上的节点数。然后,可以确定如何遍历堆以到达插入点或节点进行删除。执行一次访问,先访问左子级,再访问右子级,以确定树的高度。此后,您遇到的第一个深度较短的叶子,或缺少子节点的父节点,将指示在“冒泡”之前应将新节点放置在何处
上面的深度优先搜索(DFS)方法并不假设您知道树中的节点总数。如果此信息可用,则我们可以利用完整二叉树的属性快速“放大”到所需位置: 设N为树中的节点总数,H为树的高度 (N,H)的一些值是(1,0),(2,1),(3,1),(4,2),…,(7,2),(8,3)。 与这两者相关的一般公式是H=ceil[log2(N+1)]-1。 现在,仅给定N,我们希望以最少的步骤从根遍历到新节点的位置,即不进行任何“回溯”。 我们首先计算高度H=ceil[log2(N+1)]-1的完美二叉树中M的节点总数,即M=2^(H+1)-1 如果N=M,则我们的树是完美的,新节点应添加到新级别。这意味着我们可以简单地执行DFS(左前右),直到到达第一个叶;新节点将成为此叶的左子节点。故事结束了 但是,如果NM,则树的最后一级仍有空缺,新节点应添加到最左侧的空缺位置。 已经处于树的最后一级的节点数仅为(N-2^H+1)。 这意味着新节点在最后一级从左侧取点X=(N-2^H+2) 现在,要从根部到达那里,您需要在每个关卡进行正确的转弯(L vs R),这样您就可以在最后一关卡的点X处结束。在实践中,您可以通过在每个关卡进行少量计算来确定转弯。然而,我认为下表显示了总体情况和相关模式,而没有陷入算法中(您可能认识到这是一种均匀分布的形式):
0 X 0基本上,引用的语句指的是解决数据元素在堆中插入和删除的位置问题。为了维护二进制堆的“shape属性”,堆的最低级别必须始终从左到右填充,不留下空节点。要保持二进制堆的平均O(1)插入和删除时间,您必须能够确定下一次插入的位置以及用于删除根节点的最低级别上的最后一个节点的位置,两者都是在固定时间内
对于存储在数组中的二进制堆(如Wikipedia条目中所述,具有隐式压缩数据结构),这很简单。只需在数组末尾插入最新的数据成员,然后将其“冒泡”到位(遵循堆规则)。或者用最后一个e替换根
0 0 0 0 0 X 0 0 <--- represents the last level in our tree, X marks the spot!
^
L L L L R R R R <--- at level 0, proceed to the R child
L L R R L L R R <--- at level 1, proceed to the L child
L R L R L R L R <--- at level 2, proceed to the R child
^ (which is the position of the new node)
this column tells us
if we should proceed to the L or R child at each level
define Node = [T data, *pParent, *pLeft, *pRight]
void insert(T data)
{
do_insertion( data ); // do insertion, update count of data items in tree
# assume: pInsert points node location of the tree that where insertion just took place
# (aka, either shuffle only data during the insertion or keep pInsert updated during the bubble process)
int N = this->CountOfDataItems + 1; # note: CountOfDataItems will always be > 0 (and pRoot != null) after an insertion
p = new Node( <null>, null, null, null); // new empty node for the next insertion
# update pInsert (three cases to handle)
if ( int(log2(N)) == log2(N) )
{# #1 - N is an exact power of two
# O(log2(N))
# tree is currently a full complete binary tree ("perfect")
# ... must start a new lower level
# traverse from pRoot down tree thru each pLeft until empty pLeft is found for insertion
pInsert = pRoot;
while (pInsert->pLeft != null) { pInsert = pInsert->pLeft; } # log2(N) iterations
p->pParent = pInsert;
pInsert->pLeft = p;
}
else if ( isEven(N) )
{# #2 - N is even (and NOT a power of 2)
# O(1)
p->pParent = pInsert->pParent;
pInsert->pParent->pRight = p;
}
else
{# #3 - N is odd
# O(1)
p->pParent = pInsert->pParent->pParent->pRight;
pInsert->pParent->pParent->pRight->pLeft = p;
}
pInsert = p;
// update pLastNode
// ... [similar process]
}
void Insert(Node root,Node n)
{
Node parent = findRequiredParentToInsertNewNode (root);
if(parent.left == null)
parent.left = n;
else
parent.right = n;
}
void findRequiredParentToInsertNewNode(Node root){
Node last = findLastNode(root);
//Case 1
if(2*Math.Pow(levelNumber) == NodeCount){
while(root.left != null)
root=root.left;
return root;
}
//Case 2
else if(Even(N)){
Node n =findParentOfLastNode(root ,findParentOfLastNode(root ,last));
return n.right;
}
//Case 3
else if(Odd(N)){
Node n =findParentOfLastNode(root ,last);
return n;
}
}
Node findLastNode(Node root)
{
if (root.left == nil)
return root
Queue q = new Queue();
q.enqueue(root);
Node n = null;
while(!q.isEmpty()){
n = q.dequeue();
if ( n.left != null )
q.enqueue(n.left);
if ( n.right != null )
q.enqueue(n.right);
}
return n;
}
Node findParentOfLastNode(Node root ,Node lastNode)
{
if(root == null)
return root;
if( root.left == lastNode || root.right == lastNode )
return root;
Node n1= findParentOfLastNode(root.left,lastNode);
Node n2= findParentOfLastNode(root.left,lastNode);
return n1 != null ? n1 : n2;
}
11 % 2 = 1 --> right (the quotient is 5, and push right into stack)
5 % 2 = 1 --> right (the quotient is 2, and push right into stack)
2 % 2 = 0 --> left (the quotient is 1, and push left into stack. End)