二叉树的先序遍历:NLR
中序遍历:LNR
后续遍历:LRN
N:访问结点本身
L:遍历该结点的左子树
R:遍历该结点的右子树
最近看《算法导论》,发现里面短短几页篇幅却包含着很多内容。要完全理解它可以试试不看书自己总结出来。如果可以不看书写出来才算真的"吃进去"了,否则都不算掌握了。大家可以检测下!借这篇文章来稍微总结下,便于以后查阅。
下面说说二叉查找树( Binary search tree)。
1、二叉查找树和二叉堆的区别(前者简称 bstree ,后者简称 heap)
区别一:性质不同。heap(此处默认说的是最小堆)的左右孩子节点都比父亲大,而bstree 的左孩子中的所有节点比父亲小,而右孩子中的所有节点比父亲大。
区别二:排序方式不同。heap 的根节点永远是最小节点,所以只需要依次弹出root节点,由此组成的序列即是;而 bstree 的中序遍历才得到的是排序结果。
区别三:堆有特殊性质,一般可用数组实现,index[parent] 等于 index[child]/2 。
2、二叉树操作中的难点
查找二叉树的前驱、后继,以及插入、删除操作!
------------------------------------------------------------------------------------------------------------------------------------
下面依次列出各种操作的伪码:
遍历
1、递归实现中序遍历
遍历整棵树可以调用 PRINT-BSTREE(root[T])。
if x ≠ NIL then //只能这么表示"不等于"了,"NIL"相当于"NULL指针"
PRINT-BSTREE(left[x])
print key[x]
PRINT-BSTREE(right[x])
2、非递归中序遍历(需要借助Stack来实现)
S <---- {NIL} //无法输入书中那种集合空,暂用"{NIL}"来替代
while TRUE do
if left[x] ≠ NIL then
x <--- left[x]
PUSH(S, x) //将该节点压入栈
else
print key[x] // 如果没有左孩子了,则输出该节点
if right[x] ≠ NIL then
x <---- right[x]
else
if S == {NIL} then
break // 循环退出条件:到达叶子节点,且栈为空,即已经输出最后那个节点
else x <---- POP(S) //到达叶子节点了,该节点没有孩子
查找
1、递归查找某个元素
如果要从根节点开始查找,则调用 FIND-RECURSION(root[T], k)。
if x ≠ NIL and key[x] ≠ k then
if k < key[x] then
return FIND-RECURSION(left[x], k)
else
return FIND-RECURSION(right[x], k)
return x //返回指向这个节点的指针,或者没找到,返回空
2、非递归查找某个元素
while x ≠ NIL and key[x] ≠ k do
if k < key[x] then
x <--- left[x]
else
x <--- right[x]
return x
3、查找树的最小值(在查找前驱和后继的时候有用)
y <--- NIL
while left[x] ≠ NIL do
y <---- x
x <---- left[x]
return y
4、查找树的最大值
y <--- NIL
while right[x] ≠ NIL do
y <---- x
x <---- right[x]
return y
5、查找某个元素的前驱
如果知道元素的值,比如 k,可以先用 p = FIND(x,k) 得到指向该节点的指针,然后再调用下面的函数 FIND-PREV(p)
if left[x] ≠ NIL then
return FIND-MAX(left[x]) //如果该节点有左孩子,则左子树中最大的节点就是它的前驱
else
y <--- p[x] //p[x]表示x节点的父节点
while y ≠ NIL and right[y] ≠ x do
x <--- y
y <--- p[y]
return y //如果x没有左孩子,则它的前驱肯定是它的某个父亲,满足这个条件:某个父亲P 的右子树包含了x(或者说x所在的子树) ,则P 就是x的前驱。
6、查找某个元素的后继(和前驱类似)
if right[x] ≠ NIL then
return FIND-MIN(right[x]) //如果该节点有右孩子,则右子树中最小的节点就是它的后继
else
y <--- p[x] //p[x]表示x节点的父节点
while y ≠ NIL and left[y] ≠ x do
x <--- y
y <--- p[y]
return y //如果x没有右孩子,则它的前驱肯定是它的某个父亲,满足这个条件:某个父亲P 的左子树包含了x(或者说x所在的子树) ,则P 就是x的后继。
插入
向二叉树中插入节点,先需要生成节点m,然后调用 INSERT(T, m)。
x <---- root[T]
y <--- NIL //用来记录要插入节点位置的父亲
while x ≠ NIL do //寻找要插入的位置
y <--- x
if key[m] < key[x] then
x <--- left[x]
else
x <--- right[x]
p[m] <--- y //对该节点的parent指针赋值
if y == NIL then
root[T] <--- m
else if key[m] < key[y] then
left[y] <--- m
else right[y] <--- m
删除
删除操作是最复杂的,该节点分三种情况:没有孩子,有一个孩子,有两个孩子。对于两个孩子的情况,我们采用"逻辑删除",也就是删除掉它的"后继"节点,把这个节点的值赋给要删除的节点!
先要通过 m = FIND(root[T],k) 来找到这个要删除节点的指针,而后调用 DELETE(T, m)来删除
x <--- NIL
y <--- NIL
if left[m]==NIL or right[m]==NIL then
x <--- m //如果至多只有一个孩子,则x指向该节点本身
else
x <--- FIND-NEXT(m) // 如果有两个孩子,则x指向m的后继节点,由于后继节点没有左孩子,所以可以将删除后继的操作和上面结合起来
if left[x] ≠ NIL then //x指向的是y节点的孩子节点(y最多有一个孩子),如果没有则y为NULL
y <--- left[x]
else
y <--- right[x]
if y ≠ NIL then //修改孩子节点的父节点
p[y] <--- p[x]
if p[y] == NIL then
root[T] <---- y //删除的刚好是根节点
else if left[p[x]] == x then
left[p[x]] <--- y
else
right[p[x]] <--- y
if x ≠ m then
key[m] <----- key[x] //把x节点的内容copy给m节点,以实现逻辑上删除m节点
delete x
C语言的源代码如下,仅做参考:
- #include<stdio.h>
- #include<stdlib.h>
- #include<assert.h>
- //二叉树节点
- typedef struct node{
- int data;
- struct node *left,*right,*parent;
- }bsnode;
- //遍历二叉树(中序)
- void print_bstree(bsnode *tree){
- if(tree!=NULL){
- print_bstree(tree->left);//递归
- printf("%d,",tree->data);
- print_bstree(tree->right);
- }
- }
- //查找元素(非递归)
- bsnode * find(bsnode *tree,int k){
- while(tree!=NULL && tree->data !=k){
- if(k < tree->data){
- tree = tree->left;
- }else{
- tree = tree->right;
- }
- }
- return tree;//返回指向data域等于k的节点的指针!
- }
- //查找元素(递归)
- bsnode * re_find(bsnode *tree,int k){
- if(tree==NULL || tree->data ==k){
- return tree;
- }else if(k < tree->data){
- return re_find(tree->left,k);
- }else{
- return re_find(tree->right,k);
- }
- }
- //求最小值(给定一个二叉查找树的根snode,求这颗树的最小节点)
- bsnode * find_min(bsnode *snode){
- bsnode *p=NULL;
- while(snode!=NULL){
- p=snode;
- snode=snode->left;
- }
- return p;//树的最左子树叶节点
- }
- //求最大值
- bsnode * find_max(bsnode *snode){
- bsnode *p=NULL;
- while(snode!=NULL){
- p=snode;
- snode=snode->right;
- }
- return p;
- }
- //查找前驱
- bsnode * find_prev(bsnode *m){
- assert(m!=NULL);
- if(m->left != NULL){
- return find_max(m->left);
- }else{
- //寻找m的祖先中满足这样关系的点:righ[p[m]] == m
- bsnode *p = m->parent;
- bsnode *child = m;
- while(p!=NULL && p->right != child){
- child=p;
- p=child->parent;
- }
- return p;
- }
- }
- //查找后继
- bsnode * find_next(bsnode *m){
- assert(m==NULL);
- if(m->right !=NULL){
- return find_min(m->right);
- }else{
- bsnode *p=m->parent;
- bsnode *child=m;
- while(p!=NULL && p->left != child){
- child=p;
- p=p->parent;
- }
- return p;
- }
- }
- //插入元素(递归)
- bsnode *re_insert(bsnode *tree,bsnode *newer){
- if(tree==NULL){
- tree=newer;
- }else{
- if(newer->data < tree->data){
- tree->left=re_insert(tree->left,newer);
- }else{
- tree->right=re_insert(tree->right,newer);
- }
- }
- return tree;
- }
- //插入元素(非递归)
- bsnode *insert(bsnode *tree,bsnode *newer){
- bsnode *root=tree;
- bsnode *p= NULL;
- while(root!=NULL){
- p=root;
- if(newer->data < root->data){//往左子树
- root=root->left;
- }else{
- root=root->right;
- }
- }//p指针就是要插入节点的父亲
- newer->parent = p;//修改parent指针,让其指向父亲
- if(p==NULL){//只有一个节点
- tree = newer;
- }else{
- if(newer->data < p->data){
- p->left=newer;
- }else{
- p->right=newer;
- }
- }
- return tree;
- }
- //删除元素
- bsnode * delete(bsnode *tree,int k){
- bsnode *delnode = find(tree,k);
- if(delnode ==NULL){
- printf("要删除的节点不存在!请检查\n");
- }else{
- bsnode *xnode = NULL;
- bsnode *ynode = NULL;
- if(delnode->left == NULL || delnode->right == NULL){
- xnode = delnode;//delnode有一个孩子或者没有,则xnode表示它本身
- }else{
- xnode = find_next(delnode);//delnode有两个孩子,xnode则表示它的后继节点
- }
- //得到xnode的孩子节点
- if(xnode->left != NULL){
- ynode = xnode->left;
- }else{
- ynode = xnode->right;
- }
- if(ynode != NULL){
- ynode->parent = xnode->parent;
- }
- if(xnode->parent == NULL){
- tree = ynode;
- }else{
- if(xnode->parent->left == xnode){
- xnode->parent->left = ynode;
- }else{
- xnode->parent->right = ynode;
- }
- }
- if(xnode != delnode){//两个孩子的情况,得将后继节点的值赋给delnode,以实现逻辑删除
- delnode->data = xnode->data;
- }
- //删除掉节点
- free(xnode);
- }
- return tree;
- }
- int main(){
- bsnode *tree=NULL;
- int data[]={10,2,11,3,8,6,1,7};
- int i;
- for(i=0;i<sizeof(data)/sizeof(int);i++){
- bsnode *newer = (bsnode *)malloc(sizeof(bsnode));
- newer->data=data[i];
- newer->left=newer->right=newer->parent=NULL;
- tree = insert(tree,newer);//插入
- }
- print_bstree(tree);//中序遍历
- printf("\n");
- tree = delete(tree,2);//删除节点
- print_bstree(tree);
- printf("\n");
- return 0;
- }
如果转载,请标明出处 blog.csdn.net/whuslei