赞
踩
二叉树是由一个储存数据的变量和两个指向子树的指针构成的,定义在一个结构体中。其一般形式为:
- typedef struct BiTree
- {
- char data;
- struct BiTree *lchild;
- struct BiTree *rchild;
- }BiTree,*BiNode;
BiNode即结构体变量的指针,BiTree是结构体变量。在创建二叉树时,为每个结点动态分配内存时要用到这两个标识符,例如: BiNode root=(BiNode)malloc(sizeof(BiTree))。
二叉树一般使用递归的方式建立,递归调用函数来创建自己的左右子树。
- BiNode create()//先序建立二叉树
- {
- BiNode T;
- char a;
- scanf("%c",&a);
- if(a=='.') return NULL;//当输入.时表示这个结点为空,而且没有左右子树。
- else
- {
- T=(BiNode)malloc(sizeof(BiTree));//动态分配内存
- T->data=a;
- T->lchild=create();//创建左子树
- T->rchild=create();//创建右子树
- }
- return T;
- }
若想中序或后序创建,则只需改变函数中 T->data=a; T->lchild=create(); T->rchild=create(); 这三条语句的顺序,先给T->data=a在先的话是先序,在中间的话是中序,在最后的话是后序。
二叉树一般有4种遍历方法,即先序、中序、后序、层序。
先序的遍历顺序是根节点->左子树->右子树。
中序的遍历顺序是左子树->根节点->右子树。
后序的遍历顺序是右子树->根节点->左子树。
层序的遍历顺序是按层顺次遍历。
先序、中序、后序的代码基本相同
- void pre(BiNode root)//先序遍历二叉树
- {
- if(root)
- {
- printf("%c ",root->data);
- pre(root->lchild);
- pre(root->rchild);
- }
- }
-
- void mid(BiNode root)//中序遍历二叉树
- {
- if(root)
- {
- mid(root->lchild);
- printf("%c ",root->data);
- mid(root->rchild);
- }
- }
-
- void post(BiNode root)//后序遍历二叉树
- {
- if(root)
- {
- post(root->lchild);
- post(root->rchild);
- printf("%c ",root->data);
- }
- }
例如这个简单的二叉树:
当我们先序建立二叉树时,应输入:ABDF..GH..I..E..C..,这三种遍历的结果为:
而层序遍历较为复杂,可以用队列来实现,队列的特点是先进先出。我们先构造一个队列,先将根节点入队列,之后每个出队列的结点都要判断其左右子树是否存在,若存在则入队列。代码如下:
- void level(BiNode root)//二叉树的层次遍历,运用队列,每层的结点挨个先进先出。
- {
- BiNode queue[20],pTemp;
- int cur=0,pre=0;//cur表示当前入队列的结店,pre表示当前出队列的结点
- queue[cur++]=root;
- while(cur!=pre)
- {
- pTemp=queue[pre++];
- printf("%c ",pTemp->data);
- if(pTemp->lchild!=NULL) queue[cur++]=pTemp->lchild;
- if(pTemp->rchild!=NULL) queue[cur++]=pTemp->rchild;
- }
- }
层序遍历上面的二叉树的运行结果为:
二叉排序树的特点是:若一个结点的左子树非空,则左子树的值一定小于结点的值;若一个节点的右子树非空,则右子树的值一定大于结点的值。
例如这颗简单二叉排序树:
其创建过程也不复杂,对于每个数据,都判断它与根节点的大小关系,若大于根节点,则作为根节点的右子树,若小于根节点,则作为根节点的左子树。则二叉排序树的中序遍历一定是严格递增的。代码如下:
- BiNode BSTInsert(BiNode T,int key)
- {
- if(!T)//若为空,则创建这个结点
- {
- T=(BiNode)malloc(sizeof(BiTree));
- T->data=key;
- T->lchild=T->rchild=NULL;
- }
- else
- {
- if(T->data<key)
- T->rchild=BSTInsert(T->rchild,key);
- else if(T->data>key)
- T->lchild=BSTInsert(T->lchild,key);
- }
- return T;
- }
-
- BiNode Insert(BiNode T,int data[],int n)
- {
- int i;
- for(i=0;i<n;i++)
- T=BSTInsert(T,data[i]);
- return T;
- }
- int main()
- {
- int num[9]={8,3,10,13,14,1,6,7,4};
- BiNode T=NULL;
- T=Insert(T,num,9);
- printf("\n中序遍历:");
- mid(T);
- return 0;
- }
将一个数组{8,3,10,13,14,1,6,7,4}作为数据构造一棵二叉排序树,可以得到上图中的结果,对他进行中序遍历的结果为:
得到了正确的结果。也可以在二叉排序树构造成功后直接使用BSTInsert函数来插入元素,例如在上面的例子里插入5:
- int main()
- {
- int num[9]={8,3,10,13,14,1,6,7,4};
- BiNode T=NULL;
- T=Insert(T,num,9);
- printf("中序遍历:");
- mid(T);
- BSTInsert(T,5);
- printf("\n插入后:");
- mid(T);
- return 0;
- }
运行结果为:
根据二叉树的特点,我们可以轻松的找出二叉排序树中的最小、最大元素和特定元素。
寻找最小、最大元素的方法是相同的。根据二叉树的特点,若左右子树不为空,则根节点的值一定大于左子树的值、小于右子树的值,那么这棵树的根节点的最左子树和最右子树即为最小、最大元素。代码如下:
- BiNode Searchmin(BiNode T)//寻找二叉排序树中的最小元素
- {
- if(T==NULL)
- return NULL;
- if(T->lchild==NULL)
- return T;
- else return Searchmin(T->lchild);//不断搜索他的左子树
- }
-
- BiNode Searchmax(BiNode T)//寻找二叉排序树中的最大元素
- {
- if(T==NULL)
- return NULL;
- if(T->rchild==NULL)
- return T;
- else return Searchmax(T->rchild);//不断搜索他的右子树
- }
而查找特定元素也利用了排序树的特点。将结点的值与待查值做比较,若结点值大,则说明待查值在结点的左子树;若结点值小,则说明待查值在结点的右子树,然后递归或不递归地查询结点的左子树或右子树即可。递归查询的代码如下:
- BiNode Search_1(BiNode T,int key)//递归查找特定元素
- {
- if(T==NULL)
- return NULL;
- if(key>T->data)
- return Search_1(T->rchild,key);//查找右子树
- else if(key<T->data)
- return Search_1(T->lchild,key);//查找左子树
- else return T;//找到则返回指针
- }
在此基础上,我们可以省掉递归过程,用while循环来实现结点的遍历。非递归查找代码如下:
- BiNode Search_2(BiNode T,int key)//非递归查找特定元素
- {
- BiNode p=T;
- while(p)
- {
- if(p->data==key)//找到就返回结点
- return p;
- else p=(p->data>key)?p->lchild:p->rchild;//判断下一个要查找的是左子树还是右子树。
- }
- return NULL;//找不到则返回空
- }
对于一般的二叉树来说,删去树中的一个结点是没有意义的,因为它将使以被删除的结点为根的子树变成森林,破坏了整棵树的结构。但是,对于二叉排序树,删去树上的一个结点相当于删去有序序列中的一个记录,只要在删除某个结点后不改变二叉排序树的特性即可。
删除节点p的一般过程是:
1、在二叉排序树中找到要删除的那个结点p。
2、若p有左子树,则找到左子树的最右子树r,用r来代替p,将r的左子树作为r父亲的右子树。
3、若p没有左子树,则用p的右子树r代替p。
第一步我们可以用一个函数,先根据二叉排序树的特点找到要删除的结点。找到后,用另一个函数来实现第二步或第三步。
明确思路后,应考虑如何完成用r代替p这一操作,若移动r和p的指针,不仅麻烦,而且移来移去容易出错。仔细思考一下,我们删除的实质上是r,并不是p,则更好的办法是将r的值赋给p,然后删除掉r结点。代码如下:
- BiNode Delete(BiNode T,int key)//删除二叉排序树中的特定元素
- {
- if(T->lchild)//如果存在左子树
- {
- BiNode p=T->lchild;
- BiNode parent=T->lchild;
- while(p->rchild!=NULL)//找到左子树的最右子树
- {
- parent=p;
- p=p->rchild;
- }
- T->data=p->data;//将要删除的结点的值覆盖掉
- if(parent!=p)//如果找到的最右子树不是T的左子树,则把最他的左子树作为他的父结点的右子树
- parent->rchild=p->lchild;
- else T->lchild=p->lchild;//否则T的左子树指向p的左子树
- free(p);//清空p的内存
- return T;
- }
- else//如果不存在左子树,则直接返回自己的右子树。
- {
- BiNode p=T->rchild;
- free(T);
- return p;
- }
- }
-
- BiNode DeleteBST(BiNode T,int key)//二叉排序树的删除
- {
- if(T)
- {
- if(T->data==key)//若找到要删除的结点,则执行删除操作
- T=Delete(T,key);
- else if(T->data>key)
- T->lchild=DeleteBST(T->lchild,key);
- else T->rchild=DeleteBST(T->rchild,key);
- }
- return T;
- }
用{8,3,10,13,14,1,6,7,4}来测试一下这段代码:
完全符合预期。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。