您的位置:首页 > 理论基础 > 数据结构算法

二叉树 的 存储与基本操作实现

2017-11-04 12:20 471 查看
数据结构课程学了二叉树,觉得自己大概理解了,但是实现还是总有困难,尤其是结构体指针这块,吃了不少亏

(一)存储与遍历(递归)

#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<queue>
#include<stack>
using namespace std;

typedef struct BiTNode{			//定义结构体,包含数据域、左右子节点指针
int data;
BiTNode *lchild,*rchild;
}BiTNode,*BiTree;				//定义一个BiTNode类型的结构体变量与结构体指针

//建立二叉树
void createBiTree(BiTree* t){	//此处必须为指针类型
int ch;
scanf("%d",&ch);
if(ch==-1){								//节点值输入为"-1"时,表示该节点没有左(右)叶节点
*t=NULL;
}
else{
*t=(BiTree)malloc(sizeof(BiTNode));		//开辟一块BiTree指针类型的空间
(*t)->data=ch;
printf("请输入%d的左子节点值:",ch);
createBiTree(&(*t)->lchild);		//对地址进行操作,注意此格式
printf("请输入%d的右子节点值:",ch);
createBiTree(&(*t)->rchild);
}
}

//先序遍历(递归实现)
void PreOrderTraverse(BiTree t){
if(t==NULL)
return;
else{
printf("%d",t->data);
PreOrderTraverse(t->lchild);
PreOrderTraverse(t->rchild);
}
}

//中序遍历(递归实现)
void InOrderTraverse(BiTree t){
if(t==NULL)
return;
else{
InOrderTraverse(t->lchild);
printf("%d",t->data);
InOrderTraverse(t->rchild);
}
}

//后序遍历(非递归)
void PostOrderTraverse(BiTree t){
if(t==NULL)
return;
else{
PostOrderTraverse(t->lchild);
PostOrderTraverse(t->rchild);
printf("%d",t->data);
}
}

//求树深度
int deepth(BiTree t){
int deep=0;
int ldeepth,rdeepth;
if(t){
ldeepth=deepth(t->lchild);
rdeepth=deepth(t->rchild);
deep=ldeepth>=rdeepth?ldeepth+1:rdeepth+1;
}
return deep;
}

//交换左右子树(递归实现)
void swapt(BiTree t){
BiTree temp;
if(t==NULL)
return;
else{
temp=t->lchild;
t->lchild=t->rchild;
t->rchild=temp;
swapt(t->lchild);
swapt(t->rchild);
}
return;
}

int main(){
BiTree t;
BiTree *p=(BiTree*)malloc(sizeof(BiTNode));
printf("请输入根节点的值:");
createBiTree(&t);
printf("先序遍历该二叉树为:\n");
PreOrderTraverse(t);
printf("\n");
printf("中序遍历输出为:\n");
InOrderTraverse(t);
printf("\n");
printf("后序遍历输出为:\n");
PostOrderTraverse(t);
printf("\n");
printf("树的深度为:%d\n",deepth(t));
printf("先序遍历输出交换左右子树后的二叉树:\n");
swapt(t);
PreOrderTraverse(t);
printf("\n");

return 0;
}


运行结果如下



(二)遍历、交换左右子树非递归实现

从难度上,中序遍历的非递归是最简单的,前序遍历居中,后序遍历最难

前序遍历

//先序遍历(非递归)
void PreOrderTraverse(BiTree root){
if(root==NULL)
return;
BiTree p=root;
stack<BiTree> s;
while(!s.empty()||p){
//遍打印边遍历,并存入栈中,以后需要借助这些根节点进入右子树
while(p){
printf("%d ",p->data);
s.push(p);
p=p->lchild;
}
//当p为非空时,说明根和左子树都遍历完了,该进入右子树了
if(!s.empty()){
p=s.top();
s.pop();
p=p->rchild;
}
}
}


中序遍历

//中序遍历
void InOrderTraverse(BiTree root)
{
//空树
if(root==NULL)
return;
//树非空
BiTree p=root;
stack<BiTree> s;
while (!s.empty()||p){
//一直遍历到左子树最下边,边遍历边保存根节点到栈中
while(p){
s.push(p);
p = p->lchild;
}
//当p为空时,说明已经到达左子树最下边,这时需要出栈了
if (!s.empty()){
p = s.top();
s.pop();
printf("%d ",p->data);
//进入右子树,开始新的一轮左子树遍历(这是递归的自我实现)
p=p->rchild;
}
}
}


后序遍历

//后序遍历非递归
void PostOrderTraverse(BiTree root){
if(root==NULL)
return;
stack<BiTree> s;
//pCur:当前访问节点,pLastVisit:上次访问节点
BiTree pCur,pLastVisit;
pCur=root;
pLastVisit =NULL;
//先把pCur移动到左子树的最下边
while(pCur){
s.push(pCur);
pCur=pCur->lchild;
}
while(!s.empty()){
//走到这里,pCur都是空,并已经遍历到左子树底端(看成扩充二叉树,则空,也是某科数的左孩子)
pCur=s.top();
s.pop();
//一个根节点被访问的前提是:无右子树或右子树已被访问过
if(pCur->rchild==NULL||pCur->rchild==pLastVisit){
printf("%d ",pCur->data);
//修改最近被访问的节点
pLastVisit=pCur;
}
//
else{
//根节点再次入栈
s.push(pCur);
//进入右子树,且可以肯定右子树一定不为空
pCur=pCur->rchild;
while(pCur){
s.push(pCur);
pCur=pCur->lchild;
}
}
}
printf("\n");
}


完整测试代码如下:

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<stack>
using namespace std;

typedef struct BiTNode{ //定义结构体,包含数据域、左右子节点指针
int data;
BiTNode *lchild,*rchild;
}BiTNode,*BiTree; //定义一个BiTNode类型的结构体变量与结构体指针

//建立二叉树
void createBiTree(BiTree* t){ //此处必须为指针类型
int ch;
scanf("%d",&ch);
if(ch==-1){ //节点值输入为"-1"时,表示该节点没有左(右)叶节点
*t=NULL;
}
else{
*t=(BiTree)malloc(sizeof(BiTNode)); //开辟一块BiTree指针类型的空间
(*t)->data=ch;
printf("请输入%d的左子节点值:",ch);
createBiTree(&(*t)->lchild); //对地址进行操作,注意此格式
printf("请输入%d的右子节点值:",ch);
createBiTree(&(*t)->rchild);
}
}

//先序遍历(非递归) void PreOrderTraverse(BiTree root){ if(root==NULL) return; BiTree p=root; stack<BiTree> s; while(!s.empty()||p){ //遍打印边遍历,并存入栈中,以后需要借助这些根节点进入右子树 while(p){ printf("%d ",p->data); s.push(p); p=p->lchild; } //当p为非空时,说明根和左子树都遍历完了,该进入右子树了 if(!s.empty()){ p=s.top(); s.pop(); p=p->rchild; } } }

//中序遍历 void InOrderTraverse(BiTree root) { //空树 if(root==NULL) return; //树非空 BiTree p=root; stack<BiTree> s; while (!s.empty()||p){ //一直遍历到左子树最下边,边遍历边保存根节点到栈中 while(p){ s.push(p); p = p->lchild; } //当p为空时,说明已经到达左子树最下边,这时需要出栈了 if (!s.empty()){ p = s.top(); s.pop(); printf("%d ",p->data); //进入右子树,开始新的一轮左子树遍历(这是递归的自我实现) p=p->rchild; } } }

//后序遍历非递归
void PostOrderTraverse(BiTree root){
if(root==NULL)
return;
stack<BiTree> s;
//pCur:当前访问节点,pLastVisit:上次访问节点
BiTree pCur,pLastVisit;
pCur=root;
pLastVisit =NULL;
//先把pCur移动到左子树的最下边
while(pCur){
s.push(pCur);
pCur=pCur->lchild;
}
while(!s.empty()){
//走到这里,pCur都是空,并已经遍历到左子树底端(看成扩充二叉树,则空,也是某科数的左孩子)
pCur=s.top();
s.pop();
//一个根节点被访问的前提是:无右子树或右子树已被访问过
if(pCur->rchild==NULL||pCur->rchild==pLastVisit){
printf("%d ",pCur->data);
//修改最近被访问的节点
pLastVisit=pCur;
}
//
else{
//根节点再次入栈
s.push(pCur);
//进入右子树,且可以肯定右子树一定不为空
pCur=pCur->rchild;
while(pCur){
s.push(pCur);
pCur=pCur-&
4000
gt;lchild;
}
}
}
printf("\n");
}

int main(){
// freopen("input11.txt","r",stdin);
BiTree t;
BiTree *p=(BiTree*)malloc(sizeof(BiTNode));
printf("请输入根节点的值:");
createBiTree(&t);
printf("\n");
printf("先序遍历该二叉树为:\n");
PreOrderTraverse(t);
printf("\n");
printf("中序遍历输出为:\n");
InOrderTraverse(t);
printf("\n");
printf("后序遍历输出为:\n");
PostOrderTraverse(t);
return 0;
}

交换左右子树:

void Exchange(BiTree bt){
queue<BiTree> q;
BiTree p;
if(bt!=NULL){
q.push(bt);
}
while(!q.empty()){
BiTree p=q.front();
q.pop();		//出队
if(p->lchild!=NULL){
q.push(p->lchild);
}
if(p->rchild!=NULL){
q.push(p->rchild);
}
BiTree temp;
temp=p->lchild;
p->lchild=p->rchild;
p->rchild=temp;
}
return;
}


执行效果图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息