我有一个堆(像二叉树一样实现:每个节点都有两个指向子节点的指针和一个指向父节点的指针)。
给定第k个元素的元素数量,我如何找到它(以BFS顺序)?我认为这可以在O(logn)时间内完成。。
(我假设“kth元素(以BFS顺序)”是指从输入的从上到下、从左到右扫描的角度来看的第k个元素。)
由于您知道二进制堆是一个完整的二叉树(可能在最后一级除外),所以您知道树的形状是一个具有一定高度的完美二叉树,对于某些k,它包含2k个节点),从左到右填充了一定数量的节点。当你写出图片中节点的编号时,这些树的一个非常漂亮的属性就出现了,其中一个索引值是:
1
2 3
4 5 6 7
8 9 10 11 12 13 14 15
请注意,每一层都从一个2的幂的节点开始。让我们假设,假设,你想查找数字13。两个不大于13的最大幂是8,所以我们知道13必须出现在该行中
8 9 10 11 12 13 14 15
我们现在可以使用这些知识对从13返回到树根的路径进行逆向工程。例如,我们知道13在这一行数字的后半部分,这意味着13属于根的右子树(如果它属于左子树,那么我们将在包含8、9、10和11的子树中。)这意味着我们可以从根向右走,抛出一半的数字来获得
12 13 14 15
我们现在位于树中的节点 3。那么我们是向左走还是向右走?好吧,13 在这些数字的前半部分,所以我们知道此时我们需要下降到节点 3 的左侧子树中。这将我们带到节点 6,现在我们只剩下数字的前半部分:
12 13
13在这些节点的右半部分,所以我们应该下降到右边,带我们去节点13。瞧!我们到了!
那么这个过程是如何运作的呢?嗯,我们可以使用一个非常非常可爱的技巧。让我们写出我们上面有的同一棵树,但用二进制:
0001
0010 0011
0100 0101 0110 0111
1000 1001 1010 1011 1100 1101 1110 1111
^^^^
我已经指出了13号节点的位置。我们的算法以如下方式工作:
让我们考虑一下这在二进制中意味着什么。查找包含节点的层等效于查找数字中设置的最高有效位。在具有二进制表示形式 1101 的 13 中,MSB 是 8 位。这意味着我们处于从 8 开始的图层中。
那么我们如何确定我们是在左子树还是右子树呢?要做到这一点,我们需要看看我们是在这一层的前半部分还是后半部分。现在有一个可爱的技巧——看看MSB之后的下一位。如果这位设置为0,我们在范围的前半部分,否则我们在范围的后半部分。因此,我们可以通过查看数字的下一位来确定我们在范围的哪一半!这意味着我们可以通过查看数字的下一位来确定要下降到哪个子树。
一旦我们这样做了,我们就可以重复这个过程。下一级我们做什么?如果下一位是零,我们向左走,如果下一位是1,我们向右走。看看这对13意味着什么:
1101
^^^
|||
||+--- Go right at the third node.
||
|+---- Go left at the second node.
|
+----- Go right at the first node.
换句话说,我们可以通过查看MSB之后的数字位来拼写出从树根到我们所讨论的节点的路径!
这总是有效的吗!当然!让我们试试数字7。它有二进制表示0111。MSB在4的位置。使用我们的算法,我们可以这样做:
0111
^^
||
|+--- Go right at the second node.
|
+---- Go right at the first node.
看我们的原图,这才是应该走的正道!
以下是此算法的一些粗略的 C/C 伪代码:
Node* NthNode(Node* root, int n) {
/* Find the largest power of two no greater than n. */
int bitIndex = 0;
while (true) {
/* See if the next power of two is greater than n. */
if (1 << (bitIndex + 1) > n) break;
bitIndex++;
}
/* Back off the bit index by one. We're going to use this to find the
* path down.
*/
bitIndex--;
/* Read off the directions to take from the bits of n. */
for (; bitIndex >= 0; bitIndex--) {
int mask = (1 << bitIndex);
if (n & mask)
root = root->right;
else
root = root->left;
}
return root;
}
我还没有测试这个代码!套用Don Knuth的话,我刚刚展示了它在概念上做了正确的事情。我可能在这里犯了一个错误。
那么这个代码有多快?好吧,第一个循环一直运行,直到找到大于n的二的一次幂,这需要O(logn)时间。循环的下一部分一次倒计数n个位,每一步做O(1)功。因此,整个算法总共需要O(log n)个时间。
希望这有帮助!
为了好玩/Java实践,我做了以下问题: 编写一个方法,该方法将整数的优先级队列作为输入,并输出最小整数。该方法不应更改传入的优先级队列的内部状态。您只能使用一个队列或堆栈作为额外数据。不允许使用其他数据结构<代码>k为1索引(表示最小值)。 获取元素很简单:只需删除次,因为它是优先级队列。我想我可以弹出,将元素放在堆栈上进行存储,然后在完成后将它们添加回队列。不过这不起作用,因为元素在优先级队列
这是一个面试问题。 在具有排序行和列的矩阵中找到Kth最小元素。 Kth最小元素是中的一个,例如,这是否正确?
本文向大家介绍交换数组的第k个元素-JavaScript,包括了交换数组的第k个元素-JavaScript的使用技巧和注意事项,需要的朋友参考一下 我们需要编写一个JavaScript函数,该函数接受一个Numbers和一个数字数组,例如k(k必须小于或等于数组的长度)。 并且我们的函数应该从数组末尾的第k个元素开始替换第k个元素。 示例 以下是代码- 输出结果 以下是控制台中的输出-
NowCoder 解题思路 利用二叉查找树中序遍历有序的特点。 // java private TreeNode ret; private int cnt = 0; public TreeNode KthNode(TreeNode pRoot, int k) { inOrder(pRoot, k); return ret; } private void inOrder(Tree
一、题目 给定一棵二叉搜索树,请找出其中的第k大的结点。 二、解题思路 如果按照中序遍历的顺序遍历一棵二叉搜索树,遍历序列的数值是递增排序的。只需要用中序遍历算法遍历一棵二叉搜索树,就很容易找出它的第k大结点。 三、解题代码 public class Test { private static class BinaryTreeNode { private int val;
我的问题是受到这个特殊的SO评论的启发,这个评论没有得到回答(我自己也面临这个问题): 我试图找到排序矩阵中第K个最小的元素: 给定一个nxn矩阵,其中每个行和列都按升序排序,返回矩阵中第k个最小的元素 请注意,它是排序顺序中的第k个最小元素,而不是第k个独立元素 输入:矩阵=[[1,5,9],[10,11,13],[12,13,15],[12,13,15],k=8 输出:13 说明:矩阵中的元素