学习了一段时间二叉树,现在终于有时间把之前所学的内容总结总结,本篇所涉及的仅仅是二叉树一些基本的操作,如有遗漏,望多多指教。
函数总结
一.遍历类:
1.递归(前序遍历,中序遍历,后序遍历)
2.非递归(前序遍历,中序遍历,后序遍历)
3.层序遍历
4.线索化二叉树的遍历
二.线索化
1.线索化方法
2.线索化二叉树创立
三.查找类
1.树节点的查找
2.节点的删除
四.其它类
1.判断是否为完全二叉树
2.树的清空
3.树的复制
二叉树的创建
typedef struct tree{
ElemType data;
struct tree *lchild,*rchild;
int ltag,rtag;
}tree,*Tree;
1.1递归的三种遍历是最基础也是最简单的,这里不多赘述,如果对递归不懂,建议弄懂递归再回看,效果会好很多
前序遍历
void priorOrder(tree* T)//先序遍历二叉树
{
if(T)
{
cout<<T->data<<endl;
priorOrder(T->lchild);
priorOrder(T->rchild);
}
}
中序遍历
void midOrder(tree *T)//中序遍历二叉树
{
if(T)
{
midOrder(T->lchild);
cout<<T->data<<endl;
midOrder(T->rchild);
}
}
后序遍历
void behindOrder(tree* T)//后序遍历二叉树
{
if(T)
{
behindOrder(T->lchild);
behindOrder(T->rchild);
cout<<T->data<<endl;
}
}
1.2 非递归的遍历主要是用栈进行遍历操作,先让左孩子进栈直到左子树为空,再对右子树进行同样操作(对左子树进行入栈,为空时往右走),利用代码一步一步对着自己构建的树进行遍历;
非递归前序遍历
void preFOrder(tree *T)//非递归的前序遍历;
{
stack<Tree> q;
tree *p = T;
while(p || !q.empty())
{
if(p != NULL){
q.push(p);
cout<<p -> data<<endl;
p = p -> lchild;
}
else{
p = q.top();
q.pop();
p = p -> rchild;
}
}
}
非递归中序遍历(和前序很像,注意输出的位置)
void midFOrder(tree *T)//非递归中序遍历;
{
stack<Tree> q;
Tree p = T;
while(p || !q.empty())
{
if(p != NULL){
q.push(p);
p = p -> lchild;
}
else{
p = q.top();
cout<<p -> data<<endl;
q.pop();
p = p -> rchild;
}
}
}
非递归后序(emmm,,,不会,去别家看看吧)
1.3 层序遍历需要用到队列,根据队列先进先出的性质,先存入根节点,然后左右孩子分别进入队列,然后pop()第一个节点,再将队列第一个节点的左右孩子入队列,然后pop,这样循环往复,就能让每一层每一个孩子进入队列,具体操作看代码
void cengOrder(tree *T) //层序遍历二叉树
{
if(T)
{
q.push(*T);
}
while(!q.empty())
{
cout<<q.front().data;
if (q.front().lchild != NULL) //如果有左孩子,lchild入队列
{
q.push(*q.front().lchild);
}
if (q.front().rchild != NULL) //如果有右孩子,rchild入队列
{
q.push(*q.front().rchild);
}
q.pop();
if(!q.empty()){
cout << " → ";
}
}
}
1.4 线索化的遍历,以中序化的二叉树为例,先让指针指向最左边的孩子,然后查找线索,有线索就一直遍历下去,没有线索的时候指针指向当前节点的右孩子,然后把这个节点当作根节点重复第一步,直到遍历结束
void midZhongTree(tree *root)//中序遍历线索化二叉树
{
tree *p = root -> lchild;//遍历根据标志为0还是为1;
while(p != root)
{
while(p -> ltag == 0){
p = p -> lchild;
}
cout<<p -> data<<endl;
while(p -> rtag == 1&&p != root){
p = p -> rchild;
cout<<p -> data<<endl;
}
p = p -> rchild;
}
}
2.1 以中序线索化为例,先利用中序遍历写出你要线索化的那棵树的中序遍历,找到每一个节点的前驱和后继,中序化遍历树的时候,如果左指针为空,将左指针指向这个节点的前驱,并将ltag赋值为1 ,如果右指针为空,将右指针指向这个节点的后继,并将rtag赋值为1,如果孩子指针不为空,只需将l/r tag赋值为0即可,节点的遍历顺序为中序遍历
void midTreadTree(tree *p)//线索化方法
{
if(p != NULL){
midTreadTree(p -> lchild);//遍历左孩子
if(p -> lchild == NULL){
p -> ltag = 1;//为空,令标志=1;
p ->lchild = pre;//pre永远是指向上一个节点的指针
}
else {
p -> ltag = 0;//不为空令标志= 0;
}
if(pre -> rchild == NULL){
pre -> rtag = 1;
pre -> rchild = p;
}
else {
pre -> rtag = 0;
}
pre = p;
midTreadTree(p->rchild);//p指向当前节点,此时pre==p,p向后移动一位
}
}
2.2 树的创建就很简单了,这里要注意,为了使线索化后的指针围成一个圈,在根节点的上方还加了一个节点,目的是是最边的孩子有了前驱,最后边的孩子有了后继。当然没有也可以。
struct tree *creatTreadTree(tree *T)//创建线索化树
{
tree *root;//创建一个头节点,此节点的右指针指向根节点
root = new tree;
root -> ltag = 0;
root -> rtag = 1;
root -> rchild = T;
if(T == NULL)
root -> rchild = root;
else
{
root -> lchild = T;
pre = root;
midTreadTree(T);
pre -> rchild = root;//让最后一个叶子节点指向头节点
pre -> rtag = 0;
root -> rchild = pre;
}
return root;
}
3.1 利用递归进行遍历直到找到自己想要找到的数据
struct tree *searchTree(tree *T,char x) //树的查找
{
struct tree *temp1,*temp2;
if(T == NULL)
return NULL;
if(T->data == x)
return T;
else{
temp1 = searchTree(T->lchild,x);
temp2 = searchTree(T->rchild,x);
if(temp1 != NULL)
return temp1;
if(temp2 != NULL)
return temp2;
}
}
2.2 删除建立查找的基础上,找到这个节点,free它,然后让指向它的父类指针为空即可。
void deleteTree(tree *T)//删除节点
{
char x,xx,flag;
tree *temp,*temp2,*temp3;
temp2 = T;
temp = NULL;
cout<<"输入你要删除的节点和其父节点: ";
cin>>x>>xx;
cout<<"此节点为: 左孩子(L) 右孩子(R) :";
cin>>flag;
string ch1 = "正在树中删除这个节点及其根节点";
for(int i = 0; i<ch1.size(); i ++){
Sleep(80);
cout<<ch1[i];
}
cout<<endl;
temp = searchTree(temp2,x);
temp3 = searchTree(temp2,xx);
if(temp == NULL)
cout<<"没有这个数据"<<endl;
else{
delete temp;
if(flag == 'L')
temp3 -> lchild = NULL;
else if(flag == 'R')
temp3 -> rchild = NULL;
else
cout<<"输入有误 "<<endl;
}
priorOrder(T);
}
4.1 要判断一颗二叉树是否为完全二叉树,只需判断它的最后一层从左至右在遇到第一个空指针之前后面没有元素,都是空指针。这里就要使用到层序遍历,注意最后一层的空指针同样被放入了队列之中。
void judgeWanquanTree(tree *T)//判断完全二叉树
{
queue<struct tree*>q;
tree *p;
int i = 0;
if(T == NULL)
cout <<"该树为空树 "<<endl;
else
q.push(T);
while(p = q.front())
{
q.push(p -> lchild);
q.push(p -> rchild);
q.pop();
}
while(!q.empty())
{
if(q.front()){
i = 1;
break;
}
q.pop();
}
string ch = "正在判断是否为二叉树....................";
for(int j = 0;j < ch.size();j ++){
cout<<ch[j];
Sleep(80);
}
cout<<endl;
if(i == 0)cout<<"是完全二叉树"<<endl;
if(i == 1)cout<<"不是完全二叉树"<<endl;
}
4.2
进行后序遍历,free掉遍历的每一个节点
struct tree *freeTree(tree *T)//树的清空
{
if(T){
freeTree(T->lchild);
freeTree(T->rchild);
free(T);
T = NULL;
}
return T;
}
4.3 复制一棵树,和创建差不多,不同的是不用输入
struct tree *copyTree(struct tree *T)//二叉树的复制
{
if(!T)
return NULL;
Tree pp = new tree;
pp->data = T->data;
pp->lchild = copyTree(T->lchild);
pp->rchild = copyTree(T->rchild);
return pp;
}
完整代码
#include<stdio.h>
#include<queue>
#include<stack>
#include<iostream>
#include<windows.h>
using namespace std;
typedef char ElemType;
typedef struct tree{
ElemType data;
struct tree *lchild,*rchild;
int ltag,rtag;
}tree,*Tree;
int level = 0;
int depth(tree*);
void create(tree* &T);
int numNode(tree *T);
struct tree *copyTree(tree *T);//复制一棵树
struct tree *freeTree(tree *T);//清空一棵树
struct tree *searchTree(tree *T);//查找节点
void deleteTree(tree *temp);//删除节点
struct tree *creatTreadTree(tree *T);//创建线索二叉树
void midTreadTree(tree *p);//中序线索化
void midZhongTree(tree *root);//中序遍历线索二叉树
void judgeWanquanTree(tree *T);//判断完全二叉树
void preFOrder(tree *T);//非递归的前序遍历;
void midFOrder(tree *T);//非递归中序遍历;
void behindFOrder(tree *T);//非递归后序遍历;
queue<tree>q;//声明结构体队列
tree *pre;//前驱节点
void create(tree* &T)//创建二叉树
{
ElemType c;
cin>>c;
if(c == '/') return;
T = new tree;
T->data = c;
T->lchild = NULL;
T->rchild = NULL;
create(T->lchild);
create(T->rchild);
}
void priorOrder(tree* T)//先序遍历二叉树
{
if(T)
{
cout<<T->data<<endl;
priorOrder(T->lchild);
priorOrder(T->rchild);
}
//else cout<<"/* EMPTY */"<<endl;
}
void preFOrder(tree *T)//非递归的前序遍历;
{
stack<Tree> q;
tree *p = T;
while(p || !q.empty())
{
if(p != NULL){
q.push(p);
cout<<p -> data<<endl;
p = p -> lchild;
}
else{
p = q.top();
q.pop();
p = p -> rchild;
}
}
}
void behindOrder(tree* T)//后序遍历二叉树
{
if(T)
{
behindOrder(T->lchild);
behindOrder(T->rchild);
cout<<T->data<<endl;
}
//else cout<<"/* EMPTY */"<<endl;
}
void midOrder(tree *T)//中序遍历二叉树
{
if(T)
{
midOrder(T->lchild);
cout<<T->data<<endl;
midOrder(T->rchild);
}
//else cout<<"/* EMPTY */"<<endl;
}
void midFOrder(tree *T)//非递归中序遍历;
{
stack<Tree> q;
Tree p = T;
while(p || !q.empty())
{
if(p != NULL){
q.push(p);
p = p -> lchild;
}
else{
p = q.top();
cout<<p -> data<<endl;
q.pop();
p = p -> rchild;
}
}
}
void cengOrder(tree *T) //层序遍历二叉树
{
if(T)
{
q.push(*T);
}
while(!q.empty())
{
cout<<q.front().data;
if (q.front().lchild != NULL) //如果有左孩子,lchild入队列
{
q.push(*q.front().lchild);
}
if (q.front().rchild != NULL) //如果有右孩子,rchild入队列
{
q.push(*q.front().rchild);
}
q.pop();
if(!q.empty()){
cout << " → ";
}
}
}
int numNode(tree* T)//计算节点个数
{
int count=0;
if(T)
{
++count;
count += numNode(T->lchild);
count += numNode(T->rchild);
}
return count;
}
int depth(tree* T)//计算树的深度
{
int leftLen,rightLen;
if(T == NULL)return 0;
else {
leftLen = depth(T->lchild)+1;
rightLen = depth(T->rchild)+1;
}
if(leftLen>rightLen)return leftLen;
else return rightLen;
}
int numLeaf(tree *T)//计算叶子节点的个数
{
int num=0;
if(T)
{
if(T->lchild == NULL&&T->rchild == NULL){
++ num;
}
num += numLeaf(T->lchild);
num += numLeaf(T->rchild);
}
return num;
}
struct tree *copyTree(struct tree *T)//二叉树的复制
{
if(!T)
return NULL;
Tree pp = new tree;
pp->data = T->data;
pp->lchild = copyTree(T->lchild);
pp->rchild = copyTree(T->rchild);
return pp;
}
struct tree *freeTree(tree *T)//树的清空
{
if(T){
freeTree(T->lchild);
freeTree(T->rchild);
free(T);
T = NULL;
}
return T;
}
struct tree *searchTree(tree *T,char x) //树的查找
{
struct tree *temp1,*temp2;
/*if(T == NULL)
return NULL;
if(T->data == x)
return T;
else{
if(temp1 == searchTree(T->lchild,x))
return temp1;
if(temp2 == searchTree(T->rchild,x))
return temp2;
}*/
if(T == NULL)
return NULL;
if(T->data == x)
return T;
else{
temp1 = searchTree(T->lchild,x);
temp2 = searchTree(T->rchild,x);
if(temp1 != NULL)
return temp1;
if(temp2 != NULL)
return temp2;
}
}
void midTreadTree(tree *p)//线索化方法
{
if(p != NULL){
midTreadTree(p -> lchild);//遍历左孩子
if(p -> lchild == NULL){
p -> ltag = 1;//为空,令标志=1;
p ->lchild = pre;//pre永远是指向上一个节点的指针
}
else {
p -> ltag = 0;//不为空令标志= 0;
}
if(pre -> rchild == NULL){
pre -> rtag = 1;
pre -> rchild = p;
}
else {
pre -> rtag = 0;
}
pre = p;
midTreadTree(p->rchild);//p指向当前节点,此时pre==p,p向后移动一位
}
}
struct tree *creatTreadTree(tree *T)//创建线索化树
{
tree *root;//创建一个头节点,此节点的右指针指向根节点
root = new tree;
root -> ltag = 0;
root -> rtag = 1;
root -> rchild = T;
if(T == NULL)
root -> rchild = root;
else
{
root -> lchild = T;
pre = root;
midTreadTree(T);
pre -> rchild = root;//让最后一个叶子节点指向头节点
pre -> rtag = 0;
root -> rchild = pre;
}
return root;
}
void midZhongTree(tree *root)//中序遍历线索化二叉树
{
tree *p = root -> lchild;//遍历根据标志为0还是为1;
while(p != root)
{
while(p -> ltag == 0){
p = p -> lchild;
}
cout<<p -> data<<endl;
while(p -> rtag == 1&&p != root){
p = p -> rchild;
cout<<p -> data<<endl;
}
p = p -> rchild;
}
}
void judgeWanquanTree(tree *T)//判断完全二叉树
{
queue<struct tree*>q;
tree *p;
int i = 0;
if(T == NULL)
cout <<"该树为空树 "<<endl;
else
q.push(T);
while(p = q.front())
{
q.push(p -> lchild);
q.push(p -> rchild);
q.pop();
}
while(!q.empty())
{
if(q.front()){
i = 1;
break;
}
q.pop();
}
string ch = "正在判断是否为二叉树....................";
for(int j = 0;j < ch.size();j ++){
cout<<ch[j];
Sleep(80);
}
cout<<endl;
if(i == 0)cout<<"是完全二叉树"<<endl;
if(i == 1)cout<<"不是完全二叉树"<<endl;
}
void deleteTree(tree *T)//删除节点
{
char x,xx,flag;
tree *temp,*temp2,*temp3;
temp2 = T;
temp = NULL;
cout<<"输入你要删除的节点和其父节点: ";
cin>>x>>xx;
cout<<"此节点为: 左孩子(L) 右孩子(R) :";
cin>>flag;
string ch1 = "正在树中删除这个节点及其根节点";
for(int i = 0; i<ch1.size(); i ++){
Sleep(80);
cout<<ch1[i];
}
cout<<endl;
temp = searchTree(temp2,x);
temp3 = searchTree(temp2,xx);
if(temp == NULL)
cout<<"没有这个数据"<<endl;
else{
delete temp;
if(flag == 'L')
temp3 -> lchild = NULL;
else if(flag == 'R')
temp3 -> rchild = NULL;
else
cout<<"输入有误 "<<endl;
}
priorOrder(T);
}
int main()
{
struct tree *T=new tree;
struct tree *temp,*temp1,*temp2,*root/*指向线索化二叉树的指针*/;
char flag;
ElemType x;
temp = temp1 = temp2 = NULL;
T = NULL;
cout<<"输入你要创建的二叉树:"<<endl;
create(T);
cout<<"节点的个数是:"<<numNode(T)<<endl;
cout<<"树的节点深度:"<<depth(T)<<endl;
cout<<"树的叶子节点数:"<<numLeaf(T)<<endl;
cout<<"先序遍历:"<<endl; priorOrder(T);
cout<<endl;
cout<<"后续遍历:"<<endl; behindOrder(T);
cout<<endl;
cout<<"中序遍历:"<<endl; midOrder(T);
cout<<endl;
cout<<"层序遍历:"<<endl; cengOrder(T);
cout<<endl;
cout<<"非递归前序遍历: "<<endl;
preFOrder(T);
cout<<"非递归中序遍历: "<<endl;
midFOrder(T);
system("pause");
system("cls");
//judgeWanquanTree(T);//判断是否为完全二叉树
/******************************************************/
/*cout<<"输入你要查找的数据: ";
cin>>x;
string ch1 = "正在树中查找这个数据";
for(int i = 0; i<ch1.size(); i ++){
Sleep(80);
cout<<ch1[i];
}
cout<<endl;
temp2 = searchTree(T,x);
if(temp2 == NULL)
cout<<"没有这个数据"<<endl;
else cout<<"查找的数据是: "<<temp2->data<<endl;
cout<<endl;
system("pause");
/******************************************************/
/*string ch = "正在复制这颗树!...........";
for(int i = 0; i<ch.size(); i ++){
Sleep(80);
cout<<ch[i];
}
cout<<endl;
temp = copyTree(T);
cout<<"复制成功"<<endl;
cout<<"先序遍历结果:"<<endl;
priorOrder(temp);
system("pause");
/******************************************************/
/*cout<<"是否清空树:是(Y),否(N): ";
cin>>flag;
if(flag == 'Y')
temp1 = freeTree(T);
priorOrder(temp1);
/******************************************************/
string ch2 = "正在线索化二叉树...........";
for(int i = 0;i < ch2.size();i ++ ){
cout<<ch2[i];
Sleep(80);
}
cout<<endl;
string ch3 = "线索化结束...........";
for(int i = 0;i < ch3.size();i ++ ){
cout<<ch3[i];
Sleep(80);
}
cout<<endl;
root = creatTreadTree(T);
cout<<"遍历中序化二叉树"<<endl;
midZhongTree(root);
system("pause");
/******************************************************/
/*deleteTree(T);//删除节点
/******************************************************/
return 0;
}
//abc//d//f//