learning tree
一、二叉查找树(二叉搜索树或二叉排序树)
定义:
二叉查找(搜索或排序)树(Binary Search Tree)。其定义为:二叉查找树或者是空树,或者是满足如下性质的二叉树:
①若它的左子树非空,则左子树上所有结点的值均小于(小于等于)根结点的值;
②若它的右子树非空,则右子树上所有结点的值均大于(大于等于)根结点的值;
③左、右子树本身又各是一棵二叉查找树。
按中序遍历该树所得到的中序序列是一个递增有序序列。
插入:
在二叉查找树中插入新结点,要保证插入后仍满足BST性质。其插入过程是:
(a)若二叉查找树T为空,则为待插入的关键字value申请一个新结点,并令其为根;
(b)若二叉查找树T不为空,则将key和根的关键字比较:
(i)若二者相等,则说明树中已有此关键字key,无须插入。
(ii)若value<T.key,则将key插入根的左子树中。
(iii)若value>T.key,则将它插入根的右子树中。
子树中的插入过程与上述的树中插入过程相同。如此进行下去,直到将value作为一个新的叶结点的关键字插入到二叉查找树中,或者直到发现树中已有此关键字为止。
注意:输入序列决定了二叉查找树的形态。二叉查找树的中序序列是一个有序序列。所以对于一个任意的关键字序列构造一棵二叉查找树,其实质是对
此关键字序列进行排序,使其变为有序序列。因此,人们又常常将二叉查找树称为二叉排序树。通常将这种排序称为树排序(Tree Sort),可以证明这种排
序的平均执行时间亦为O(nlgn)。对相同的输入实例,树排序的执行时间约为堆排序的2至3倍。因此在一般情况下,构造二叉排序树的目的并非为了排序,
而是用它来加速查找,这是因为在一个有序的集合上查找通常比在无序集合上查找更快,“查找树"的名称也由此而来。
删除:
从二叉查找树中删除一个结点,不能把以该结点为根的子树都删去,并且还要保证删除后所得的二叉树仍然满足BST性质。
一般步骤如下:
(a) 进行查找。查找时,令p指向当前访问到的结点,parent指向其双亲(其初值为NULL)。若树中找不到被删结点则返回,否则被删结点是p。
(b) 删去p。删去p时,应将p的子树(若有)仍连接在树上且保持BST性质不变。按p的孩子数目分三种情况进行处理。
(i)p是叶子(即它的孩子数为0),无须连接p的子树,只需将p的双亲parent中指向p的指针域置空即可。
(ii)p只有一个孩子child,只需将child和p的双亲直接连接后,即可删去p。注意,p既可能是parent的左孩子也可能是其右孩子,而child可能是p 的左孩子或右孩子,故共有4种状态。
(iii)p有两个孩子,先令q=p,将被删结点的地址保存在q中;然后找q的中序后继p,并在查找过程中仍用parent记住p的双亲位置。q的中序后继p 一定是q的右子树中最左下的结点,它无左子树。因此,可以将删去q的操作转换为删去的p的操作,即在释放结点p之前将其数据复制到q中,就 相当于删去了q。
查找:
在BST中查找一个值。如果树中的某个值与要查找的值相等,则返回这个结点,否则报告查找不成功。
进行查找时,先用目标值与树根的值相比较。如果相等,则找到;否则,如果目标值大于根的值,则继续在根的右子树中继续查找;如果目标值小于根的值,则继续在根的左子树中继续查找。
相同的关键字集合可能构成多棵不同的BST树。查找目标过程中,从根到目标结点之间的路径上的结点个数就是要进行比较的次数。树越平衡,要比较的次数越少。
如果二叉树接近平衡,则在树中查找一个结点时,需要进行的比较次数接近O(log n)。如果树退化为一个直链,则树的查找类似于顺序查找,其比较次数接近于O(n) ,这是树查找中的最坏时间复杂度。
一般不能预测要建立的二叉树的树型。实际上,如果关键字的排列是随机的,则建立的二叉树的树型将比较平衡,查找的效率接近二分查找。
实现:
1 tree.h
2 #ifndef _TREE_H
3 #define _TREE_H
4
5 typedef int ElemType;
6
7 typedef struct treenode
8 {
9 ElemType data;
10 struct treenode * left;
11 struct treenode * right;
12 }TREE;
13
14
15
16 TREE *MakeEmptyTree();
17
18 TREE *InsertTreeNode(ElemType e,TREE *t);
19
20 TREE *FindTreeNode(ElemType e,TREE *t);
21
22 TREE *FindMax(TREE *t);
23
24 TREE *FindMin(TREE *t);
25
26 TREE *DeleteTreeNode(ElemType e,TREE *t);
27
28 void DeleteTree(TREE **t);
29
30 #endif
31
32 tree.cpp
33 #include<stdio.h>
34 #include<stdlib.h>
35 #include"tree.h"
36
37
38 TREE *MakeEmptyTree()
39 {
40 return NULL;
41 }
42
43 TREE *InsertTreeNode(ElemType e,TREE *t)
44 {
45 if(t == NULL)
46 {
47 t = (TREE *)malloc(sizeof(TREE));
48 if(t == NULL) return NULL;
49 t->data = e;
50 t->left = t->right = NULL;
51 }else if(e < t->data)
52 t->left = InsertTreeNode(e,t->left);
53 else
54 t->right = InsertTreeNode(e,t->right);
55 return t;
56 }
57
58 TREE *FindTreeNode(ElemType e,TREE *t)
59 {
60 if(t == NULL) return NULL;
61 if(t->data == e)
62 return t;
63 else if(t->data < e)
64 return FindTreeNode(e,t->right);
65 else
66 return FindTreeNode(e,t->left);
67 }
68
69 TREE *FindMax(TREE *t)
70 {
71 if(t == NULL)
72 return NULL;
73 if(t->right == NULL)
74 return t;
75 return FindMax(t->right);
76 }
77
78 TREE *FindMin(TREE *t)
79 {
80 if(t == NULL)return NULL;
81 if(t->left == NULL)
82 return t;
83 return FindMin(t->left);
84 }
85
86 TREE *DeleteTreeNode(ElemType e,TREE *t)
87 {
88 TREE *p = NULL;
89 if(t == NULL)return NULL;
90 if(t->data > e)//数据比根小
91 t->left = DeleteTreeNode(e,t->left);//去左子树删
92 else if(t->data < e)
93 t->right = DeleteTreeNode(e,t->right);//去右子树删
94 //这个节点就是要删的
95 else if(t->left && t->right)//有两个子节点的节点
96 {
97 //找出右子树里最小的
98 p = FindMin(t->right);
99 t->data = p->data;
100 t->right = DeleteTreeNode(t->data,t->right);
101 }else//要删除的节点有一个子节点或就是叶子节点
102 {
103 p = t;
104 if(t->left == NULL)//右数有
105 t = t->right;
106 else if(t->right == NULL)
107 t = t->left;
108 free(p);
109 return t;
110 }
111 return t;
112
113 }
114
115 void DeleteTree(TREE **t)
116 {
117 if(*t == NULL)
118 return;
119 DeleteTree(&((*t)->left));
120 DeleteTree(&((*t)->right));
121 free(*t);
122 *t = NULL;
123 }
1 //非递归查找操作 2 TREE *FindTreeNode(ElemType e,TREE *t) 3 { 4 while( t!=NULL ) 5 { 6 if(t->data == e) 7 return t; 8 else if(t->data <e) 9 t=t->right; 10 else 11 t=t->left; 12 } 13 return t; 14 }