下面是一个迭代算法,按顺序遍历二进制搜索树(第一个left child
,然后是parent
,最后是right child
)而不使用堆栈:
(想法:整个想法是找到一棵树的最左边的孩子,每次都找到手头节点的后继者并打印它的值,直到没有剩下的节点为止。)
void In-Order-Traverse(Node root){
Min-Tree(root); //finding left-most child
Node current = root;
while (current != null){
print-on-screen(current.key);
current = Successor(current);
}
return;
}
Node Min-Tree(Node root){ // find the leftmost child
Node current = root;
while (current.leftChild != null)
current = current.leftChild;
return current;
}
Node Successor(Node root){
if (root.rightChild != null) // if root has a right child ,find the leftmost child of the right sub-tree
return Min-Tree(root.rightChild);
else{
current = root;
while (current.parent != null && current.parent.leftChild != current)
current = current.parent;
return current.parrent;
}
}
据称,假设BST中有Theta(n)
个节点,此算法的时间复杂度为n
,这肯定是正确的。但是我无法说服自己,因为我猜测某些节点的遍历次数超过了常数,这取决于子树中节点的数量,并总结所有这些访问次数不会导致{{1 }}
关于如何证明它的任何想法或直觉?
答案 0 :(得分:4)
使用边而不是节点更容易推理。让我们根据Successor
函数的代码进行推理。
案例1(then
分支)
对于有正确孩子的所有节点,我们将访问右子树一次(“右转”边缘),然后始终使用Min-Tree
函数访问左子树(“左转”边缘)。我们可以证明这样的遍历将创建一条边缘是唯一的路径 - 边缘将不会在任何其他具有右子节点的任何遍历中重复,因为遍历确保您永远不会访问任何“右转”边缘树上的其他节点。 (通过施工证明)。
案例2(else
分支)
对于没有右子(else
分支)的所有节点,我们将通过跟随“右转”边缘访问祖先,直到你必须做出“左转”边缘或遇到根的“二叉树。同样,生成的路径中的边缘是唯一的 - 在没有右子节点的任何其他节点的任何其他遍历中永远不会重复。这是因为:
else
分支中排除)。当然,起始节点没有合适的孩子。(这里的证据非常挥手,但我认为它可以通过矛盾正式证明。)
由于边缘是唯一的,因此仅在情况1(或仅情况2)中遍历的边的总数将是O(n)(因为树中的边数等于顶点数 - 1) 。因此,在将两个案例相加后,有序遍历将为O(n)。
请注意,我只知道每个边缘最多访问一次 - 我不知道是否从证明中访问了所有边缘,但是边缘的数量受到顶点数量的限制,这恰好是正确的。
我们可以很容易地看到它也是Omega(n)(每个节点被访问一次),因此我们可以得出结论它是Theta(n)。
答案 1 :(得分:2)
给定程序在Θ(N)时间内运行。 Θ(N)并不意味着每个节点只被访问一次。请记住,有一个恒定的因素。因此,Θ(N)实际上可以受到 5N 或 10N 或甚至 1000N 的限制。因此,它并没有准确计算访问节点的次数。
二进制搜索树的有序迭代遍历的时间复杂度可以分析如下,
考虑具有N个节点的树,
让执行时间由复杂度函数T(N)
表示。
让左子树和右子树分别包含X和N-X-1个节点,
然后是时间复杂度T(N) = T(X) + T(N-X-1) + c
,
现在考虑BST
,
案例1:完全平衡的BST
,即两个子树都有相同数量的节点。例如,考虑下面显示的BST,
10
/ \
5 14
/ \ / \
1 6 11 16
对于这样的树,复杂度函数是
T(N) = 2 T(⌊N/2⌋) + c
在这种情况下,主定理给出了Θ(N)的复杂度。
案例2:完全不平衡的BST,即左子树或右子树为空。其中X = 0.例如,考虑下面显示的BST
,
10
/
9
/
8
/
7
现在T(N) = T(0) + T(N-1) + c
,
T(N) = T(N-1) + c
T(N) = T(N-2) + c + c
T(N) = T(N-3) + c + c + c
.
.
.
T(N) = T(0) + N c
由于T(N)= K,其中K是常数,
T(N)= K + N c
T(N)=Θ(N)。
因此,所有案例的复杂性均为Θ(N)
。
答案 2 :(得分:0)
我也无法想象这将是怎样的O(n)。要单独找到有序后继,即Node Successor(Node root)
将是O(log n),因为它取决于树的高度。对n个节点中的每个节点执行此操作将产生O(n log n)。
答案 3 :(得分:0)
我们专注于边缘而不是节点。 (为了更好的直觉看看这张图片:http://i.stack.imgur.com/WlK5O.png)
我们声称在这个算法中,每个边缘最多访问两次,(实际上它只访问了两次);
第一次向下移动,第二次向上移动。 要访问边缘两次以上,我们必须再次向下遍历该边缘:向下,向上, 向下 ,....
我们证明不可能再次向下访问边缘。
让我们假设我们第二次向下遍历edge (u , v)
,这意味着u
的一个祖先有一个继承者u
的继承人
这是不可能的:
我们知道当我们向上移动边缘时,我们正在寻找左转边缘来寻找后继者,所以虽然u
位于继任者的左侧,但是这个继任者的继承者是在它的右侧,通过移动到继任者的右侧(找到它的继任者)再次到达u
因此再次edge (u,v)
是不可能的。 (为了找到继任者,我们要么向右移动,要么向上移动,而不是向左移动)