本文章参考了《数据结构与算法分析——C语言描述》这本书。书中写的还是挺透彻的
首先介绍一下二叉树。什么是二叉树呢?
二叉树(binary tree)是一棵树,其每个节点最多能有两个儿子。由于整个特性,实现二叉树的方式是创建两个指针分别指向左儿子和右儿子。
二叉查找树是一种特殊的二叉树,其特点是:对于树的每个节点x,其左子树中所有关键字的值都小于x, 其右子树中所有关键字的值都大于x。其左右子树也都是二叉查找树,并且在二叉查找树中没有重复的键值。
首先是声明二叉查找树:
#ifndef _TREE_H_
typedef int ElementType;
typedef struct treeNode *Position;
typedef struct treeNode *SearchTree;
struct treeNode
{
ElementType Element;
SearchTree lch;
SearchTree rch;
};
SearchTree MakeEmpty(SearchTree T);
Position Find(SearchTree T, ElementType x);
Position FindMin(SearchTree T);
Position FindMax(SearchTree T);
SearchTree Insert(SearchTree T, ElementType x);
SearchTree Delete(SearchTree T, ElementType x);
void PrintTree(SearchTree T);
void Destroy(SearchTree T);
#endif // _TREE_H_
对于二叉查找树主要有以下几个操作:
1.Find查找操作
这个操作返回指向树T中具有关键字X的节点的指针,如果不存在就返回NULL
树的结构令这种操作变得十分简单,只需要令X与当前节点比较就可以了,如果X小于当前节点,就用递归的方式去当前节点的左子树寻找,如果x大于当前节点就去右子树查找。如果相等就返回当前节点的指针,如果找不到就返回NULL;
Position Find(SearchTree T, ElementType x)
{
if(T == NULL)
return NULL;
if(T->Element > x)
return Find(T->lch, x);
else if(T->Element < x)
return Find(T->rch, x);
else
return T;
}
2.寻找最小值FindMin操作与寻找最大值FindMax操作
根据二叉查找树的特点可以发现其最小值总是在最左边的子树,其最大值在最右边的子树。
// 递归实现
Position FindMin(SearchTree T)
{
if(T == NULL)
return NULL; // 考虑是空树的情况
if(T->lch == NULL)
return T;
else
return FindMin(T->lch);
}
// 非递归实现
Position FindMax(SearchTree T)
{
if(T != NULL)
while(T->rch != NULL)
T = T->rch;
return T;
}
3.插入Inset操作
插入操作也比较简单,像find一样在树中寻找插入的位置,将X插入该路径最后的位置,如果X一存在,则说明也不用做
SearchTree Insert(SearchTree T, ElementType x)
{
if(T == NULL)
{
T = (struct treeNode *)malloc(sizeof(struct treeNode));
T->Element = x;
T->lch = T->rch = NULL;
}
else if(x < T->Element)
T->lch = Insert(T->lch, x);
else if(x > T->Element)
T->rch = Insert(T->rch, x);
else if(x == T->Element)
cout << "数据已存在!\n";
return T;
}
4.删除Delete操作
删除是比较复杂的操作,有多种情况要考虑的
1>如果要删除的节点是叶子节点,则直接删除就可以了
2>如果删除的节点又一个儿子,就让其父节点绕过这个字节就可以了
3>比较麻烦的是要删除的节点又两个子节点。我所采用的策略是将其右节点最小的数据来代替该节点的数据,然后将那个被代替的数据给删除
SearchTree Delete(SearchTree T, ElementType x)
{
Position TemCell;
if(T == NULL)
cout << "没有该数据,删除失败!\n";
else if(x < T->Element)
T->lch = Delete(T->lch, x);
else if(x > T->Element)
T->rch = Delete(T->rch, x);
else if(T->lch && T->rch) // 有连个子节点的情况
{
TemCell = FindMin(T->rch);
T->Element = TemCell->Element;
T->rch = Delete(T->rch, T->Element);
}
else // 有一个子节点或者没有子节点的情况下
{
TemCell = T;
if(T->lch == NULL)
T = T->rch;
else if(T->rch == NULL)
T = T->lch;
free(TemCell);
cout << "数据删除成功!\n";
}
return T;
}
图示:
完整代码:
头文件代码:
// tree.h
// binary search tree
// 二叉搜索树,也称二叉查找树,有序二叉树
// 使二叉树成为二叉查找树的性质是:
// 对于树的每个节点x,其左子树中所有关键字的值都小于x;
// 其右子树中所有关键字的值都大于x。
// 由于树的递归定义,所以通常是递归的操作,
// 因为二叉查找树的平均深度为O(log N),所以一般不必担心栈空间被用尽
// 二叉查找树的声明
#ifndef _TREE_H_
typedef int ElementType;
typedef struct treeNode *Position;
typedef struct treeNode *SearchTree;
struct treeNode
{
ElementType Element;
SearchTree lch;
SearchTree rch;
};
SearchTree MakeEmpty(SearchTree T);
Position Find(SearchTree T, ElementType x);
Position FindMin(SearchTree T);
Position FindMax(SearchTree T);
SearchTree Insert(SearchTree T, ElementType x);
SearchTree Delete(SearchTree T, ElementType x);
void PrintTree(SearchTree T);
void Destroy(SearchTree T);
#endif // _TREE_H_
实现部分:
// ree.cpp
// 二叉查找树实现
#include <iostream>
#include <cstdio>
#include "tree.h"
using namespace std;
// 建立一棵空树
// 利用递归将所有子树的地址都释放,返回一个空指针
SearchTree MakeEmpty(SearchTree T)
{
if(T != NULL)
{
MakeEmpty(T->lch);
MakeEmpty(T->rch);
delete T;
}
return T;
}
// 二叉查找树的Find操作
// 比较x的值与当前节点的值,如果x小,就往左子树查找,如果x大就往右子树查找
// 如果没有找到就返回空,找到就返回x的地址
Position Find(SearchTree T, ElementType x)
{
if(T == NULL)
return NULL;
if(T->Element > x)
return Find(T->lch, x);
else if(T->Element < x)
return Find(T->rch, x);
else
return T;
}
// 二叉查找树的FindMin操作
// 根据二叉查找树的特性,最左边的子树就是其最小值
// 递归实现
Position FindMin(SearchTree T)
{
if(T == NULL)
return NULL; // 考虑是空树的情况
if(T->lch == NULL)
return T;
else
return FindMin(T->lch);
}
// 二叉查找树的FindMax操作
// 根据二叉查找树的特性,最右边的子树就是其最大值
// 非递归实现
Position FindMax(SearchTree T)
{
if(T != NULL)
while(T->rch != NULL)
T = T->rch;
return T;
}
// 二叉查找树的Inset操作
// 像Find一样遍历查找。如果找到x,则给出提示,否则将x插入到遍历的最后一点上
SearchTree Insert(SearchTree T, ElementType x)
{
if(T == NULL)
{
T = (struct treeNode *)malloc(sizeof(struct treeNode));
T->Element = x;
T->lch = T->rch = NULL;
}
else if(x < T->Element)
T->lch = Insert(T->lch, x);
else if(x > T->Element)
T->rch = Insert(T->rch, x);
else if(x == T->Element)
cout << "数据已存在!\n";
return T;
}
// 二叉查找树的Delete的实现
// 分三种情况进行考虑:1.如果节点是一片叶子,则直接删除就可以了
// 2.如果节点只有一个儿子,就绕过这个节点把这个节点删除就可以
// 3.如果几点有两个节点就比较复杂了,一般的策略是找到其右子树最小的数据
// 代替该节点的数据并递归的删除那个被代替的节点
SearchTree Delete(SearchTree T, ElementType x)
{
Position TemCell;
if(T == NULL)
cout << "没有该数据,删除失败!\n";
else if(x < T->Element)
T->lch = Delete(T->lch, x);
else if(x > T->Element)
T->rch = Delete(T->rch, x);
else if(T->lch && T->rch) // 有连个子节点的情况
{
TemCell = FindMin(T->rch);
T->Element = TemCell->Element;
T->rch = Delete(T->rch, T->Element);
}
else // 有一个子节点或者没有子节点的情况下
{
TemCell = T;
if(T->lch == NULL)
T = T->rch;
else if(T->rch == NULL)
T = T->lch;
free(TemCell);
cout << "数据删除成功!\n";
}
return T;
}
// 中序遍历
void PrintTree(SearchTree T)
{
if(T != NULL)
{
PrintTree(T->lch);
cout << T->Element << " ";
PrintTree(T->rch);
}
}
// 销毁二叉查找树
void Destroy(SearchTree T)
{
if(T->lch != NULL)
Destroy(T->lch);
if(T->rch != NULL)
Destroy(T->rch);
free(T);
}
测试程序:
// usetree.cpp 于tree.cpp一起编译
// 二叉查找树测试程序
#include <iostream>
#include "tree.h"
using namespace std;
int main()
{
SearchTree T = NULL;
Position p;
int cmd;
MakeEmpty(T);
do
{
int x;
cout << "菜单:\t1.插入数据\n"
<< "\t2.查找数据\n"
<< "\t3.查找最小值\n"
<< "\t4.查找最大值\n"
<< "\t5.删除数据\n"
<< "\t6.遍历二叉查找树\n"
<< "\t0.销毁二叉查找树并退出程序\n"
<< "输入命令:";
cin >> cmd;
switch(cmd)
{
case 1:
cout << "请输入插入的数据:";
cin >> x;
T = Insert(T, x);
break;
case 2:
cout << "请输入插入的数据:";
cin >> x;
p =Find(T, x);
if(p != NULL)
cout << "数据存在!\n";
else
cout << "数据未找到!\n";
break;
case 3:
p = FindMin(T);
if(p != NULL)
cout << "最小值为:" << p->Element;
else
cout << "当前为空树!";
cout << endl;
break;
case 4:
p = FindMax(T);
if(p != NULL)
cout << "最大值:" << p->Element;
else
cout << "当前为空树!";
cout << endl;
break;
case 5:
cout << "请输入要删除的数据:";
cin >> x;
T = Delete(T, x);
break;
case 6:
if(T != NULL)
PrintTree(T);
else
cout << "当前为空树!";
cout << endl;
break;
}
}while(cmd != 0);
Destroy(T);
return 0;
}