我在试着寻找二叉搜索树的定义我发现到处都是不同的定义。
有人说,对于任何给定的子树,左子键都小于或等于根键。
有人说,对于任何给定的子树,右子键大于或等于根键。
我以前的大学数据结构书说“每个元素都有一个键,没有两个元素有相同的键。”
bst有一个通用的定义吗?特别是关于如何处理具有相同键的多个实例的树。
编辑:也许我不清楚,我看到的定义是
1)左<=根<右
2)左<根<=右
3)左<根<右,这样就不存在重复的键。
我在试着寻找二叉搜索树的定义我发现到处都是不同的定义。
有人说,对于任何给定的子树,左子键都小于或等于根键。
有人说,对于任何给定的子树,右子键大于或等于根键。
我以前的大学数据结构书说“每个元素都有一个键,没有两个元素有相同的键。”
bst有一个通用的定义吗?特别是关于如何处理具有相同键的多个实例的树。
编辑:也许我不清楚,我看到的定义是
1)左<=根<右
2)左<根<=右
3)左<根<右,这样就不存在重复的键。
当前回答
所有这三个定义都是可以接受和正确的。它们定义了BST的不同变体。
你的大学数据结构的书没有说明它的定义不是唯一可能的。
当然,允许复制会增加复杂性。如果你使用定义"left <= root < right",你有一个这样的树:
3
/ \
2 4
然后在这个树中添加一个重复的“3”键将导致:
3
/ \
2 4
\
3
注意,副本不是连续的级别。
当允许像上面那样的BST表示中的副本时,这是一个大问题:副本可以被任意数量的层分隔,因此检查副本的存在并不像检查节点的直接子节点那么简单。
避免此问题的一个选项是不从结构上表示重复项(作为单独的节点),而是使用一个计数器来计算键出现的次数。前面的例子会有一个这样的树:
3(1)
/ \
2(1) 4(1)
在插入重复的"3"键后,它将变成:
3(2)
/ \
2(1) 4(1)
这简化了查找、删除和插入操作,但牺牲了一些额外的字节和计数器操作。
其他回答
在BST中,节点左侧降序的所有值都小于(或等于,稍后将讨论)节点本身。类似地,节点右侧降序的所有值都大于(或等于)该节点值(a)。
一些bst可能会选择允许重复值,因此上面有“或等于”限定符。下面的例子可以说明:
14
/ \
13 22
/ / \
1 16 29
/ \
28 29
这显示了一个允许重复的BST (b) -您可以看到,要查找一个值,您可以从根节点开始,根据您的搜索值是否小于或大于节点值,沿着左或右子树向下查找。
这可以用类似这样的东西递归完成:
def hasVal (node, srchval):
if node == NULL:
return false
if node.val == srchval:
return true
if node.val > srchval:
return hasVal (node.left, srchval)
return hasVal (node.right, srchval)
用:
foundIt = hasVal (rootNode, valToLookFor)
重复增加了一些复杂性,因为在找到值之后,您可能需要继续搜索具有相同值的其他节点。显然,这对hasVal来说并不重要,因为它不关心有多少个,只关心是否至少存在一个。然而,对于countVal之类的东西,这很重要,因为它需要知道有多少个。
(a)如果你调整了搜索特定键的方式,你可以按照相反的方向对它们进行排序。BST只需要保持某种排序顺序,无论是升序还是降序(甚至是一些奇怪的多层排序方法,如所有奇数升序,然后所有偶数降序)都无关紧要。
(b)有趣的是,如果你的排序键使用存储在一个节点上的整个值(这样包含相同键的节点就没有其他额外的信息来区分它们),向每个节点添加一个计数可以提高性能,而不是允许重复的节点。
这样做的主要好处是,添加或删除副本只会修改计数,而不是插入或删除新节点(这一操作可能需要重新平衡树)。
因此,要添加一个项目,首先要检查它是否已经存在。如果是,增加计数并退出。如果不是,则需要插入一个新节点,其计数为1,然后重新平衡。
要删除一个项,您找到它,然后递减计数-只有当结果计数为零时,您才从树中删除实际节点并重新平衡。
考虑到节点更少,搜索也会更快,但这可能不会产生太大影响。
例如,下面两棵树(左边的非计数树和右边的计数树)是等价的(在计数树中,i.c表示i项的c个副本):
__14__ ___22.2___
/ \ / \
14 22 7.1 29.1
/ \ / \ / \ / \
1 14 22 29 1.1 14.3 28.1 30.1
\ / \
7 28 30
从左侧树中移除叶节点22将涉及重新平衡(因为它现在有两个高度差)所产生的22-29-28-30子树,如下所示(这是一个选项,还有其他选项也满足“高度差必须为0或1”规则):
\ \
22 29
\ / \
29 --> 28 30
/ \ /
28 30 22
在右边的树上执行相同的操作,只需将根节点从22.2修改为22.1(不需要重新平衡)。
你说的三件事都是真的。
密钥是唯一的 左边是比这个小的键 右边是比这个大的键
我想你可以把你的树倒过来,把较小的键放在右边,但实际上“左”和“右”的概念只是:一个可视化的概念,帮助我们思考一个没有真正的左或右的数据结构,所以这并不重要。
我只是想为罗伯特·保尔森的回答补充一些信息。
假设节点包含键和数据。因此具有相同键的节点可能包含不同的数据。 (因此搜索必须找到具有相同键的所有节点)
左<= cur <右
左< cur <=右
左<= cur <= right
左< cur <右&& cur包含具有相同键的兄弟节点。
左< cur <右,这样就不存在重复的键。
1 & 2. works fine if the tree does not have any rotation-related functions to prevent skewness. But this form doesn't work with AVL tree or Red-Black tree, because rotation will break the principal. And even if search() finds the node with the key, it must traverse down to the leaf node for the nodes with duplicate key. Making time complexity for search = theta(logN) 3. will work well with any form of BST with rotation-related functions. But the search will take O(n), ruining the purpose of using BST. Say we have the tree as below, with 3) principal.
12
/ \
10 20
/ \ /
9 11 12
/ \
10 12
如果我们在这棵树上搜索(12),即使我们在根结点上找到了12,我们也必须同时搜索左子结点和右子结点来寻找重复的键。 这需要O(n)个时间。 4. 是我个人的最爱。兄弟节点是指具有相同键的节点。 我们可以把上面的树变成下面的树。
12 - 12 - 12
/ \
10 - 10 20
/ \
9 11
现在任何搜索都需要O(logN)因为我们不需要遍历重复的子键。 这个原理也适用于AVL和RB树。
Duplicate Keys • What happens if there's more than one data item with the same key? – This presents a slight problem in red-black trees. – It's important that nodes with the same key are distributed on both sides of other nodes with the same key. – That is, if keys arrive in the order 50, 50, 50, • you want the second 50 to go to the right of the first one, and the third 50 to go to the left of the first one. • Otherwise, the tree becomes unbalanced. • This could be handled by some kind of randomizing process in the insertion algorithm. – However, the search process then becomes more complicated if all items with the same key must be found. • It's simpler to outlaw items with the same key. – In this discussion we'll assume duplicates aren't allowed
可以为树的每个节点创建包含重复键的链表,并将数据存储在列表中。
1.)左<=根<右 2.)左<根<=右 3.)左<根<右,这样就不存在重复的键。
我可能要去翻出我的算法书籍,但我的头脑中(3)是标准形式。
(1)或(2)只在你开始允许重复的节点并且你把重复的节点放在树本身(而不是包含列表的节点)时才会出现。