基本概念
树 是一些节点的集合
没有儿子的节点是树叶
节点的深度是这个节点到根节点的惟一的路径的长
节点的高度是这个节点到树叶的最长路径的长
一个节点可以用它的第一个儿子和下一个兄弟来作为连接其他节点的中介
因为每个节点的儿子的个数是未知的 不好采用直接与儿子相连的方法
代码如下
struct treenode
{
int X;//假设数据类型是int
treenode* firstchild;
treenode* nextbrother;
};
对于树的遍历大致分为三种
先序遍历 这种遍历方式是先处理这个节点 而后在处理这个节点子节点
后序遍历 先处理某个节点的子节点 而后再处理这个节点
中序遍历 遍历顺序为左子树 根 右子树
--------------------------------------------------------
二叉树
二叉树是一种 树上每个节点都不能有多于两个儿子的树
实现代码
struct Treenode
{
int X;//假设数据类型是int
Treenode* left;
Treenode* right;
};
二叉查找树
二叉查找树是二叉树的一种特殊类型 对于二叉查找树中的每一个节点X 它的左子树中的所有的关键字的值小于X 而右子树所有的关键字的值大于X
树节点的实现与二叉树一致
而查找 插入 删除 可以这样实现
找一个元素
递归
Treenode* findx(int x, Treenode* node)
{
if (node==NULL)//找不到
return NULL;
if (x<node->X){
return findx(x,node->left); //x比这个点的值小 往左边查找
}
else if (x>node->X){
return findx(x,node->right);
}
return node; //是这个点本身
}
非递归
Treenode* findx2(int x,Treenode* node)
{
Treenode *p=node;
for (;;){
if (p->X==x||p==NULL)
break;
else if (x<p->X)
p=p->left;
else if (x>p->X)
p=p->right;
}
return p;
}
找最小值 最大值类似 只是往右找
Treenode* findmin(Treenode* T)
{
if (T->left==NULL||T==NULL)
return T;
return findmin(T->left);
}
插入
插入是创建一个新的满足条件的位置树叶 并把值存入
Treenode* Insert(int x,Treenode* T)
{
if(T==NULL){//找到空的位置
T=(Treenode*)malloc(sizeof(Treenode));
if (T==NULL){
return T;
}
else {
T->X=x;
T->left=NULL;
T->right=NULL;
}
}
else if (T->X>x){
T->left=Insert(x,T->left);//左子树被更新
}
else if (T->X<x){
T->right=Insert(x,T->right);
}
return T;
}
删除
首先要找到要删除的那个节点
对于这个节点有两种情况
删除的那个点有两个儿子
删除的那个点有一个或没有的儿子
对于第一种的情况 可以把要删除的那个点的右子树的最小值的点或者用左子树的最大值的点代替被删除点的位置(二叉查找树的性质说明这个结论是成立的 )
并且这两个点只有一个儿子 或者没有儿子 这样比较好处理
把右子树最小值的那个节点放在被删除那个节点的位置 再进行二次删除 这次是删除这个最小点的位置
同理 左子树是把最大值的那个点放在要被删除的点的位置 再进行二次删除
具体是
struct Treenode* Delete(int x,Treenode* t)
{
Treenode* node=findx(x,t);//找到这个点的位置
if (node==NULL)
cout<<"not found"<<endl;
else if (node->left&&node->right){//有两个儿子
Treenode* ch;
ch=findmin(node->right);
node->x=ch->x;
node->right=Delete(node->x,node->right);
/*
ch=findmax(node->left);
node->x=ch->x;
node->left=Delete(node->x,node->left);
*/
}
else{//一个或者没有儿子
if (node->left==NULL)
node=node->right;
else
node=node->left;
}
return t;
}
-------------------------------------------------
AVL树 (带有平衡条件的二叉树)
每个节点左子树和右子树的高度最多差1的二叉查找树
所以 在定义的时候 要比二叉树多一个记录高度的变量
struct AVLTree
{
int x;
int height;
AVLTree* left;
AVLTree* right;
};
在新增的点(树叶)另某个节点不满足高度差最多差1的时候 有两种旋转可调节这棵树重新满足AVL树的特性
单旋转
增加的点在需调节点的右子树的右子树上
增加的点再需调节点的左子树的左子树上
双旋转
增加的点在需调节点的右子树的左子树上
增加的点再需调节点的左子树的右子树上
单旋转 - 左
AVLTree* singleleft(AVLTree* k2)
{
AVLTree* k1;
k1=k2->left;
k2->left=k1->right;
k1->right=k2;
k2->height=max(k2->left->height,k2->right->height)+1;
k1->height=max(k1->left->height,k1->right->height)+1;
return k1;//返回旋转完的这个位置的点
}
双旋转 - 左
方式1 直接交换
AVLTree* doubleleft(AVLTree* k3)
{
AVLTree* k1,k2;//k2是要代替k3的点
k1=k3->left;
k2=k1->right;
k3->left=k2->right;
k1->right=k2->left;
k2->left=k1;
k2->right=k3;
k1->height=max(k1->left->height,k1->right->height)+1;
k3->height=max(k3->left->height,k3->right->height)+1;
k2->height=max(k2->left->height,k2->right->height)+1;
return k2;
}
方式2 做两次单旋转
AVLTree* doubleleft(AVLTree* k3)
{
k3->left=singleright(k3->left);
return singleleft(k3);
}
-------------------------------------------------------
嗯 如有错误 欢迎指出 谢谢
具体参考《数据结构与算法分析——C语言描述》