BinarySearchTree 二叉查找树

遍历

二叉查找树和二分查找算法的思路一样,但使用树形结构来实现。

二叉查找树上节点xx的左孩子节点为leftxleft_{x},右孩子节点为rightxright_{x}。二叉查找树上任意节点xx的左子树上所有节点都小于xx,右子树上所有节点都大于xx,即leftx<x<rightxleft_{x} \lt x \lt right_{x}。二叉查找树中不允许两个相等的节点(通过一些方法可以实现包含重复键值的二叉查找树,但为了方便本文中不考虑这种情况)。

拥有nn个节点且安排良好的(金字塔型的)二叉查找树可以在O(log2n)O(log_2 n)的时间复杂度内查找任意元素。在二叉查找树上查询一个节点的过程和二分查找完全一样,从根节点开始依次比较被查找的节点xx和当前节点ee,若x=ex = e则查找结束;若x<ex \lt e则继续在左子树中查找;若x>ex \gt e则继续在右子树中查找:

二叉树有四种遍历方式:

(1)(1) 先序遍历(PreOrderPreOrder),访问顺序总是xleftxrightxx \rightarrow left_{x} \rightarrow right_{x}

(2)(2) 后序遍历(PostOrderPostOrder),访问顺序总是leftxrightxxleft_{x} \rightarrow right_{x} \rightarrow x

(3)(3) 中序遍历(InOrderInOrder),访问顺序总是leftxxrightxleft_{x} \rightarrow x \rightarrow right_{x}

(4)(4) 层序遍历(LevelOrderLevelOrder),访问顺序类似BFS算法,一层一层的访问所有节点);

下图中节点的访问顺序按照数字从小到大:

插入

往二叉查找树插入节点xx时,首先在树上尝试搜索xx,搜索失败时会停下的节点ee就是合适插入的位置。若x<ex \lt e则将其作为ee的左孩子节点,若x>ex \gt e则将其作为ee的右孩子节点。为了方便我们不考虑重复插入xx的情况。

删除

从二叉查找树中删除节点xx时需要保证二叉查找树的属性(leftx<x<rightxleft_{x} \lt x \lt right_{x}),有三种情况:

(1)(1)xx为叶子节点,既没有左孩子节点也没有右孩子节点,直接删除;

(2)(2)xx只有一个孩子节点yy,则像链表一样将xx从其父节点和yy之间删除;

(3)(3)xx同时有左右孩子节点,按照中序遍历找出二叉树中比xx大的下一个节点nextnext(中序遍历下的后继节点),用其值代替xx,再继续用相同的规则递归的删除原始节点nextnext(实际上,中序遍历的后继节点不可能存在左孩子节点,要么无孩子节点,要么只有右孩子节点);

下图演示了上述的三种删除情况,其中红色节点是待删除节点:

(1)(1) 待删除节点33没有左右孩子节点,直接删除即可;

(2)(2) 待删除节点66只有右孩子节点77,用77代替66即可;

(3)(3) 待删除节点44拥有左右孩子节点2266,用44的中序遍历的下一个节点55代替它,然后用相同的规则递归删除原始的节点55(在这种情况下,节点55没有左右孩子节点,因此直接删除);

(4)(4) 待删除节点44拥有左右孩子节点2266,用44的中序遍历的下一个节点55代替它,然后用相同的规则递归删除原始的节点55(在这种情况下,节点55只有右孩子节点88,因此用88替换55即可);

随机的插入/删除会让二叉查找树退化为链表,如图所示是一个糟糕的二叉查找树,虽然它满足节点之间有序,但是查找的时间复杂度已经退化为了O(n)O(n)

源码

BinarySearchTree.h

BinarySearchTree.cpp

测试

BinarySearchTreeTest.cpp

Last updated