码迷,mamicode.com
首页 > 编程语言 > 详细

二叉查找树的C++实现

时间:2015-05-03 15:56:24      阅读:211      评论:0      收藏:0      [点我收藏+]

标签:

原创作品,转载请注明出处:点我

 二叉查找树(Binary Search Tree,BST),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

中序遍历二叉排序树可得到一个关键字的有序序列,一个无序序列可以通过构造一棵二叉排序树变成一个有序序列,构造树的过程即为对无序序列进行排序的过程(其实构建BST的过程就是对数据进行快速排序的过程,也就是快排算法)。所以通常可以通过构建一棵二叉查找树来对数据进行排序,其时间复杂度为O(nlogn),对BST进行搜索,插入和删除的时间复杂度即为树的高度,O(logn).

先来看下本文实现的对BST进行的操作。

void create_tree(node **tree,int data);//生成二叉查找树
void mid_travel(node *tree);//中序遍历树
node* find_node(node *tree,int data);//查找
node* find_parent_node(node *tree,int data);//查找data节点的父节点
node* insert_node(node **tree,int data);//插入数据
node* get_min_parent_ptr(node *tree);//获得tree的最小值的父亲指针
node* get_max_patent_ptr(node *tree);//获取tree的最大值的父亲指针
bool delete_node(node **tree,int data);//删除指定的节点

其中,node的定义为:

typedef struct tree_node {
    int data;//数据域
    struct tree_node *r_child;//左孩子节点
    struct tree_node *l_child;//右孩子节点
}node ;

接着来看下BST的生成create_tree(),其中第一个参数是一个二级指针,指向BST的根节点,注意,这里一定要以二级指针的形式传入,不然后构造失败的,第二个参数是用来生成BST的数据。

 1 /*
 2  * 生成二叉树
 3  * tree:二级指针,指向生成树
 4  * data:生成树的数据
 5  * 注意:这里只能传入二级指针
 6  * author:rio_2607
 7  */
 8 void create_tree(node **tree,int data)
 9 {
10     if(NULL == *tree)
11     {
12         *tree = (node *)malloc(sizeof(node));
13         (*tree)->data = data;
14         (*tree)->l_child = NULL;
15         (*tree)->r_child = NULL;
16         return;
17     }
18     else if(data < (*tree)->data)//如果小于根节点的数据,则插入到根节点的左子树
19         create_tree(&((*tree)->l_child),data);
20     else if(data > (*tree)->data)//如果大于根节点的数据,则插入到根节点的右子树
21         create_tree(&((*tree)->r_child),data);
22     else
23     {
24         cout << "有重复数据" << endl;
25         return;
26     }
27 }

再来看下对BST进行查找,这很简单,首先拿待查找的数据跟根节点数据进行比较,如果小于根节点数据,则递归的对根节点的左子树进行查找,如果大于根节点的数据,则对右子树递归的进行查找,如果查找大叶子节点还没有查找到,则要查找的数据不存在,代码如下:

 1 /*
 2  * 查找data是否存在
 3  * 如果存在,返回对应的节点的指针否则返回NULL
 4  * author:rio_2607
 5  */
 6 node* find_node(node *tree, int data)
 8 {
 9     if(data == tree->data)
10         return tree;
11     else if(data < tree->data && tree->l_child != NULL)
12         //如果data小于根节点的数据,则对左子树进行查找
13         return find_node(tree->l_child,data);
14     else if(data > tree->data && tree->r_child != NULL)
15         //如果data大于根节点的数据,则对右子树进行查找
16         return find_node(tree->r_child,data);
17     return NULL;
18 }

再来看看插入节点:

 1 /*
 2  * 在合适的位置插入数据
 3  * tree:二级指针,指向二叉搜索树
 4  * data:要插入的数据
 5  * 返回被插入节点的指针
 6  * author:rio_2607
 7  */
 8 node* insert_node(node **tree,int data)
 9 {
10     node *temp = (node *)malloc(sizeof(node));
11     temp->data = data;
12     temp->l_child = NULL;
13     temp->r_child = NULL;
14 
15     if(NULL == *tree)//如果*tree为空,说明tree还没有数据,则直接插入
16     {
17         *tree = temp;
18         return *tree;
19     }
20     else if(data < (*tree)->data)//如果小于根节点的数据,则插入到他的左子树中
21          return insert_node(&((*tree)->l_child),data);
22     else if(data > (*tree)->data)//如果data大于根节点的数据,则插入到右子树中去
23          return insert_node(&((*tree)->r_child),data);
24     else
25     {
26         cout << "不能添加重复数据" << endl;
27         return NULL;
28     }
29 }

其实对于BST,生成,查找,插入都是很简单的递归操作,麻烦的是删除节点。那么应该怎么删除BST的节点呢?

由于BST的特殊性,BST有以下四个很明显的性质:

1、BST的右子树的最小值,没有左子树(如果有的话,就不会是最小值,因为它的所有的左子树上的值都小于根节点的值)

2、BST的左子树的最大值,没有右子树(如果有的话,就不会是最大值,因为它的所有的右子树上的值都大于根节点的值)

3、BST的左子树中的最大值一定位于该左子树中的最右边的节点上

4、BST中的右子树的最小值一定位于该右子树中的最左边的节点上

这四句话怎么理解呢?如下图所示:

技术分享

以跟节点为例,根节点的右子树的最小值为15,15这个节点位于右子树的最左端,没有左子树,左子树的最大值为12,位于左子树的最右边,没有右子树。

好了,明白了上面的几点的话就可以进行删除节点的操作了。删除节点的操作为:

1、找到要删除的节点p

2、先看下以节点P否存在右子树,如果存在右子树,就找到右子树中的最小值,把这个最小值赋值给要删除的节点,然后把这个找到的最小值节点删除,删除时有些收尾工作

3、如果以节点P没有右子树,那么就找到节点P的左子树的最大值,把这个最大值赋值给要删除的节点,然后把这个最大值节点给删除,删除时有些收尾工作

4、如果以节点P既没有左子树也没有右子树,说明节点P是一个叶子节点,直接删除即可,删除时有些收尾工作

以上图为例,假如要删除节点13,那么先找到节点13的右子树的最小值,这里是节点15,找到节点15之后,把这个节点的数据,也就是15赋值给要删除的节点13,然后把节点15的父节点,这里是19,的左孩子指针指向节点15的右孩子节点,这里是节点17,然后删除节点15,即可。如下图所示:

技术分享

因为这里既要用到右子树的最小值节点,记为m,也要用到最小值节点的父节点,记为p,所以我们直接找到最小值节点的父节点,这里是节点19,使用以下代码进行操作:

1 //假设要删除的节点为d
2 //最小值节点的父节点为p,最小值节点为m
3 //其中m = p->l_child
4 d->data = m->data
5 p->l_child = m->r_child
6 free(m)

在查找右子树的最小值的父节点的时候,会碰到一种特殊的情况,如下图:

技术分享

要删除的节点1,先要找到节点1的右子树的最小值节点,就是节点2,但是在这颗右子树中,节点2没有父节点(因为这是节点1的右子树,节点1不包含在这颗右子树当中),此时,最小值节点的父节点就是最小值节点本身,这种情况下,父节点p的l_child节点为空。所以在代码中,要对这种情况进行特殊处理:直接把父节点p的data域赋值给要删除的节点,然后把要删除的节点(这里是节点1)的右孩子指针指向父节点(这里是节点2)的右孩子节点(这里是节点5),然后删除节点2。

技术分享

以上图为例,假设我们要删除节点13,由于节点13没有右子树,所以我们要找到节点13的左子树(这里由节点10,节点12和节点11组成)的最大值,这里是节点12,找到之后,把最大值节点(节点12)的数据赋值给要删除的节点,这里是节点13,然后把最大直接点的父节点(这里是节点10)的右孩子指针指向最大值节点的左孩子节点(这里是节点11),然后删除最大直接点(节点12).如下图所示:

技术分享

同样的,在处理这种情况的时候,跟上面一样,也会碰到一种特殊的情况,如下图所示:

技术分享

要删除节点14,找到他的左子树的最大值,为节点10,但是在这个左子树(不包括节点14)中,节点10没有父节点,或者说节点10的父节点就是他本身,这种情况要单独处理,处理方法跟上面一样。

如果要删除的节点是叶子节点的话,这种情况就很简单了,但是,这一点要注意的是,在把叶子节点删除了之后,要把这个叶子节点的父节点对应的指针域(l_child或者r_child)置为NULL.具体操作看代码。

好了,现在可以上代码了。首先是get_min_parent_ptr()

 1 /*
 2  * 返回树中最小值节点的父节点的指针
 3  * 最小值一定是位于左子树的叶子节点
 4  * author:rio_2607
 5  */
 6 node* get_min_parent_ptr(node *tree)
 7 {
 8     if(NULL == tree)
 9     {
10         cout << "tree is empty" << endl;
11         return NULL;
12     }
13     //这种情况对应着父节点记为自身的情况
14     if(tree->l_child == NULL)
15         return tree;
16 
17     node *tree_temp = tree;
18     node *temp = tree_temp->l_child;
19     while(temp->l_child != NULL)
20     {
21         tree_temp = temp;
22         temp = temp->l_child;
23     }
24 
25     return tree_temp;
26 
27 }

接下来是get_max_patent_ptr()

 1 /*
 2  * 返回树中最大值节点的父节点的指针
 3  * 最大值一定是位于右子树的叶子节点
 4  * author:rio_2607
 5  */
 6 node* get_max_patent_ptr(node *tree)
 7 {
 8     if(NULL == tree)
 9     {
10         cout << "tree is empty" << endl;
11         return NULL;
12     }
13     //这种情况对应着父节点记为自身的情况
14     if(tree->r_child == NULL)
15         return tree;
16 
17     node *tree_temp = tree;
18     node *temp = tree_temp->r_child;
19     while(temp->r_child != NULL)
20     {
21         tree_temp = temp;
22         temp = temp->r_child;
23     }
24 
25     return tree_temp;
26 }

接下来是get_parent_ptr()

 1 /*
 2  * 找到指定节点的父节点
 3  * 返回父节点的指针,否则返回NULL
 4  * 一般只会查找叶子节点的父节点
 5  * author:rio_2607
 6  */
 7 node* find_parent_node(node *tree,int data)
 8 {
 9     if(data == tree->data)
10         return tree;
11     if(data < tree->data && tree->l_child != NULL)
12     {
13         if(data == tree->l_child->data)
14             return tree;
15         else
16             return find_parent_node(tree->l_child,data);
17     }
18     else if(data > tree->data && tree->r_child != NULL)
19     {
20         if(data == tree->r_child->data)
21             return tree;
22         else
23             return find_parent_node(tree->r_child,data);
24     }
25     return NULL;
26 }

接下来就是具体的删除节点的代码了delete_node():

 1 /*
 2  * 删除data节点
 3  * author:rio_2607
 4  */
 5 bool delete_node(node **tree,int value)
 6 {
 7     node *value_ptr = find_node(*tree,value);
 8     if(NULL == value_ptr)
 9     {
10         cout << value << " is not in the data" << endl;
11         return false;
12     }
13 
14     //如果要删除的数据是叶子节点,先找到这个叶子节点的父节点,把父节点的孩子节点指针清空,再删除叶子节点
15     if(NULL == value_ptr->l_child && NULL == value_ptr->r_child)
16     {
17         //叶子节点一定有父节点,找到父节点
18         node *parent_ptr = find_parent_node(*tree,value);
19         //判断叶子节点是父节点的左节点还有右节点
20         if(parent_ptr->l_child->data == value)
21             parent_ptr->l_child = NULL;
22         else if(parent_ptr->r_child->data == value)
23             parent_ptr->r_child = NULL;
24         free(value_ptr);
25         //注意,这里要把根节点的父节点的对应的指针域清空
26         return true;
27     }
28     //如果value_ptr的右子树存在,则找到右子树的最小值
29     if(value_ptr->r_child != NULL)
30     {
31         node *min_parent_ptr = get_min_parent_ptr(value_ptr->r_child);
32         if(min_parent_ptr != NULL && min_parent_ptr->l_child != NULL)
33         {
34             node *delete_ptr = min_parent_ptr->l_child;//右子树的最小值的节点指针
35             value_ptr->data = delete_ptr->data;//把要删除的节点的数据域的值赋值为右子树的最小值
36             //把最小值的节点的父节点的左子树指向最小值节点的右子树
37             //因为最小值节点没有左子树
38             min_parent_ptr->l_child = delete_ptr->r_child;
39             free(delete_ptr);//删除最小值节点
40             return true;
41         }
42         else if(min_parent_ptr != NULL && NULL == min_parent_ptr->l_child)
43         {
44             //如果min_parent_ptr->l_child为空,说明以value_ptr为根节点的树没有左子树
45             //则value_ptr的右子树的根节点就是最小值
46             value_ptr->data = min_parent_ptr->data;
47             value_ptr->r_child = min_parent_ptr->r_child;
48             free(min_parent_ptr);
49             return true;
50         }
51     }
52     //如果没有右子树有左子树,则找出左子树的最大值
53     else if(NULL == value_ptr->r_child && value_ptr->l_child != NULL)
54     {
55         node *max_parent_ptr = get_max_patent_ptr(value_ptr->l_child);
56         if(max_parent_ptr != NULL && max_parent_ptr->r_child != NULL)
57         {
58             node *delete_ptr = max_parent_ptr->r_child;//左子树的最大值节点指针
59             value_ptr->data = delete_ptr->data;//把要删除节点的数据域赋值为左子树的最大值
60             //把最大值节点的父节点的右子树赋值为最大值节点的左子树
61             //因为最大值节点没有右子树
62             max_parent_ptr->r_child = delete_ptr->l_child;
63             free(delete_ptr);
64             return true;
65         }
66         else if(max_parent_ptr != NULL && NULL == max_parent_ptr->r_child)
67         {
68             //如果max_parent_ptr->r_child为空,说明value_ptr没有右子树
69             //则value_ptr的左子树的根节点就是最大值
70             value_ptr->data = max_parent_ptr->data;
71             value_ptr->l_child = max_parent_ptr->l_child;
72             free(max_parent_ptr);
73             return true;
74         }
75     }
76 
77     return false;
78 }

 

二叉查找树的C++实现

标签:

原文地址:http://www.cnblogs.com/rio2607/p/4474038.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!