什么是二叉搜索树
一棵二叉搜索树是以一棵二叉树来组织的,如下图:
这样的一棵树可以使用一个链表数据结构来表示,其中每个结点就是一个对象。除了key和卫星数据之外,每个结点还包含属性left, right 和 parent, 它们分别指向左孩子,右孩子和双亲。比如:
typeof struct node{
int key;
// other data... 即卫星数据
struct node *lchild; //左孩子
struct node *rchild; //右孩子
struct node *parent; //双亲
};
二叉搜索树,对任何结点x,其左子树中的关键字最大不超过x.key, 其中右子树中的关键字最小不低于x.key。不同的二叉搜索树可以代表同一组值的集合。大部分搜索树操作的最坏运行时间与树的高度成正比。如上图中(a)一棵包含6个结点高度为2的二叉搜索树。(b)一棵包含相同关键字、高度为4的低效二叉搜索树.
二叉搜索树中的关键字总是以满足二叉搜索树性质的方式来存储:
这是什么意思呢,设x为二叉搜索树中一结点,若y是x左子树中一结点,则 y.key≤x.key ; 若y是x右子树中一结点,则 y.key≥x.key
二叉搜索树遍历
那么根据上面性质,我们可知,要想按序输出二叉搜索树中的所有关键字,需用中序遍历(inorder tree walk)算法.
inorder tree walk 伪代码如下:
INORDER-TREE-WALK(x) // x为根结点指针
if x != NIL
INORDER-TREE-WALK(x.left)
printf(x.key)
INORDER-TREE-WALK(x.right)
C/C++语言代码实现:
(1)非递归,用栈来实现,在我写的二叉树遍历里面有地址:
http://blog.youkuaiyun.com/jun2016425/article/details/72783252
(2)非递归,不用栈
一、中序遍历
步骤:
如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。
如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。
a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。
b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。当前节点更新为当前节点的右孩子。
重复以上1、2直到当前节点为空。
图示:
下图为每一步迭代的结果(从左至右,从上到下),cur代表当前节点,深色节点表示该节点已输出。
注意:本来想用下面这个(1)结点结构的,但是写到请求前驱后继的时候有点难写,就用了有双亲的(2)结点结构。正如UNIX编程艺术里面说的:数据结构才是编程的核心,宁愿让数据结构复杂一点,也不愿省的一点两点的数据结构空间,而增大算法的复杂度,因为相比于算法逻辑的复杂,数据结构的复杂更容易让人看懂. so KISS(Keep It Simple, Stupid)
(1)
typedef struct _binary_search_tree{
int key;
struct _binary_search_tree *left;
struct _binary_search_tree *right;
}binary_search_tree;
(2)
typedef struct _binary_search_tree{
int key;
struct _binary_search_tree *left;
struct _binary_search_tree *right;
struct _binary_search_tree *parent;
}binary_search_tree;
void inorderMorrisTraversal(binary_search_tree *root)
{
binary_search_tree *cur = root, *prev = NULL;
while (cur != NULL){
if ( cur->left == NULL ){
// 1.
printf("%d ", cur->key);
cur = cur->right;
}else{
// 查找当前结点的前驱,中序遍历中当前结点的前一个结点
prev = cur->left;
/* 查找当前结点的前驱 prev->right != NULL.
* 当前结点的左子树已经被遍历之后, prev->right != cur
* */
while ( prev->right != NULL && prev->right != cur )
prev =