休息了两天,状态恢复了一下,补充点基础知识。
二叉搜索树
搜索树数据结构支持许多动态集合操作,包括Search,minimum,maximum,predecessor(前驱),successor(后继),INSERT和DELETE等。因此我们使用一颗搜索树既可以作为一个字典又可以作为一个优先队列。且二叉搜索树上的基本操作所花费的时间与这棵树的高度成正比。二叉搜索树有两个很重要的变体,红黑树与B树,这个我们之后有机会再补一篇文章。
顾名思义,一棵二叉搜索树是以一棵二叉树来组织的。如图所示,这样的一棵树可以用一个链表的数据结构来表示,其中每个结点就是一个对象。除了结点的key之外,每个结点还包含属性left,right,p。分别用于指向这个结点的左孩子,右孩子与父节点。如果不存在,则对应的属性值为null。根结点是树种唯一父指针为null的结点。
关于二叉搜索树有这么一些特点。对任何结点x,其左子树中的关键字最大值不超过x.key,其右子树中的关键字的最小值不小于x.key。不同的二叉搜索树可以代表同一组值的集合。如上图所示。
中序遍历
二叉搜索树的这种性质允许我们通过一个简单的递归算法来按序(x.key的顺序)输出二叉搜索树的所有关键字。这种算法被称为中序遍历(inorder tree walk)。这样命名的原因是输出的子树的根的关键字位于根的左子树与右子树的关键字值之间。类似地,先序遍历,后序遍历输出的根的关键字在其左右子树的关键字值的前/后。一个中序遍历的伪算法如下:
INORDER-TREE-WALK(x) { if (x != Null) { INORDER-TREE-WALK(x.left); print x; INORDER-TREE-WALK(x.right); } }
对于之前图中的两棵二叉搜索树,他们中序遍历得到的结果是一样的,2,5,5,6,7,8.
查询二叉搜索树
前面有提到,二叉搜索树支持许多集合操作。我们来看看一下利用二叉搜索树完成这些操作的思路。
查找
假如我们要寻找关键字值为k的结点,如果这个结点存在就返回,否则就返回null。可以利用下面的方法来进行查找。
Search(x,k) //x为要进行查找的树的根结点,k为要查找的关键字值 { if(x == Null || x.key ==k) { return x; } if(k < x.key) { return Search(x.left, k); } else { return Search(x.right, k); } }
思路很简单,我们判断当前这个结点是否是我们要寻找的结点,如果不是则决定使用左子树或右子树来继续查找。但上面的方式其实还是一种递归,我们可以用一种while循环的方式来改写它。对于计算机来说,这种方式效率应该会更高一些。
Search(x, k) //同样x为树的根结点,k为要查找的关键字的值 { while(x != Null && x.key != k) { if(k < x.key) { x = x.left; } else { x = x.right; } } return x; }
最大值与最小值
基于二叉搜索树的性质,对任何结点x,其左子树中的关键字最大值不超过x.key,其右子树中的关键字的最小值不小于x.key。这样我们去寻找一棵二叉搜索树种的最大值或最小值可以简化操作为不断的寻找右/左孩子,直到对应的右/左子孩子的值为null。以最大值举例,这样找到的结点值一定不小于它的父节点,而这个父节点也一定不小于它父节点,依次直到根结点。根结点又一定不小于左子树中的任意结点。
后继与前驱
我们先来看看后序与前驱的定义。给定一棵二叉搜索树种的一个结点,如果按照中序遍历的次序查找它的后继,如果所有的关键字互不相同,则一个结点x的后继是大于x的.Key的最小关键字的结点。同样,结点x的前驱是小于x.Key的最大关键字的结点。二叉搜索树的结构允许我们通过没有任何关键字的比较来确定一个结点的后继。如果后继存在,这样的过程将返回结点x的后继,否则就返回Null。
SUCCESSOR(x) { if(x.right != null) { return TREE-MINIMUM(x.right); // 找到右子树中的最小值。 } var y = x.p; while(y != null && x == y.right) { x = y; y = y.p; } return y; }
我们以上面寻找后继的伟算法为例,看看找后继结点的思路。前驱结点的寻找方式与之大同小异。根据后继的定义,它的值一定不小于x.key。因此,如果结点x的右子树存在,那么x.Right中的最小值就是x的后继。因为根据二叉搜索树的定义,这个最小值一定满足要求。如果x的右子树不存在,我们需要向x的祖先中寻找x的后继了。(x的左子树一定不满足要求,故舍去)。我们先找到x的父节点y,判读一下x是y的左孩子还是右孩子。如果x是左孩子,那x的父结点y就是x的后继。因为y.Key >= x.Key。如果x是y的右孩子,说明y的值仍小于x,此时我们应继续寻找。直到遇到根结点,或当期迭代的x是父节点的左孩子。
另算法导论上有一个定理,即在一棵高度为h的二叉搜索树上,动态集合上的操作查找,最小值,最大值,寻找后继与前驱可以在O(h)的时间内完成。这里就不贴证明了。
原文地址:https://www.cnblogs.com/dogtwo0214/p/11963652.html