原贴:https://www.cnblogs.com/liuamin/p/6269950.html
一、总体源代码:
#include<iostream>
#include<stdlib.h>
using namespace std;
typedef char ElemType;
//二叉树的二叉链表结构,也就是二叉树的存储结构,1个数据域,2个指针域(分别指向左右孩子)
typedef struct BiTNode
{
ElemType data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
//二叉树的建立,按前序遍历的方式建立二叉树,当然也可以以中序或后序的方式建立二叉树
void CreateBiTree(BiTree *T)
{
ElemType ch;
cin >> ch;
if (ch == '#')
*T = NULL; //保证是叶结点
else
{
*T = (BiTree)malloc(sizeof(BiTNode));
//if (!*T)
//exit(OVERFLOW); //内存分配失败则退出。
(*T)->data = ch;//生成结点
CreateBiTree(&((*T)->lchild));//构造左子树
CreateBiTree(&(*T)->rchild);//构造右子树
}
}
//表示对遍历到的结点数据进行的处理操作,此处操作是将树结点前序遍历输出
void operation1(ElemType ch)
{
cout << ch << " ";
}
//此处在输出的基础上,并输出层数
void operation2(ElemType ch, int level)
{
cout << ch << "在第" << level << "层" << endl;
}
//递归方式前序遍历二叉树
void PreOrderTraverse(BiTree T, int level)
{
if (T == NULL)
return;
/*此处表示对遍历的树结点进行的操作,根据你自己的要求进行操作,这里只是输出了结点的数据*/
//operation1(T->data);
operation2(T->data, level); //输出了层数
PreOrderTraverse(T->lchild, level + 1);
PreOrderTraverse(T->rchild, level + 1);
}
//递归方式中序遍历二叉树
void InOrderTraverse(BiTree T,int level)
{
if(T==NULL)
return;
InOrderTraverse(T->lchild,level+1);
//operation1(T->data);
operation2(T->data, level); //输出了层数
InOrderTraverse(T->rchild,level+1);
}
//递归方式后序遍历二叉树
void PostOrderTraverse(BiTree T,int level)
{
if(T==NULL)
return;
PostOrderTraverse(T->lchild,level+1);
PostOrderTraverse(T->rchild,level+1);
//operation1(T->data);
operation2(T->data, level); //输出了层数
}
int main()
{
int level = 1; //表示层数
BiTree T = NULL;
cout << "请以前序遍历的方式输入扩展二叉树:"; //类似输入AB#D##C##
CreateBiTree(&T);// 建立二叉树,没有树,怎么遍历
cout << "递归前序遍历输出为:" << endl;
PreOrderTraverse(T, level);//进行前序遍历,其中operation1()和operation2()函数表示对遍历的结点数据进行的处理操作
cout << endl;
cout << "递归中序遍历输出为:" << endl;
InOrderTraverse(T, level);
cout << endl;
cout << "递归后序遍历输出为:" << endl;
PostOrderTraverse(T, level);
cout << endl;
return 0;
}
二、树的结构:
typedef struct BiTNode
{
ElemType data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
心得:
1、树的结构其实是一个结构体的嵌套,每一个节点的左右孩子都是父节点的两个指针域,这样子在用链表实现时就避免了链接的过程;
2、在定义树结构时,可以直接在struct前+typedef,在结构尾部可以写多个参数,不带*的是替换的节点名称(默认是不变的,为了替换节点指针好像必须写上),带*的是替换的节点指针名称,这样定义的结构在程序中依然存在;
三、树的建立部分:
//二叉树的建立,按前序遍历的方式建立二叉树,当然也可以以中序或后序的方式建立二叉树
void CreateBiTree(BiTree *T)
{
ElemType ch;
cin >> ch;
if (ch == '#')
*T = NULL; //保证是叶结点
else
{
*T = (BiTree)malloc(sizeof(BiTNode));
//if (!*T)
//exit(OVERFLOW); //内存分配失败则退出。
(*T)->data = ch;//生成结点
CreateBiTree(&((*T)->lchild));//构造左子树
CreateBiTree(&(*T)->rchild);//构造右子树
}
}
心得:
整个流程(前序建立):
1.定义参数 BiTree *T,这里的BiTree是结构体指针,所以T是指针的指针,之所以要这样定义是为了后面遍历时方便,取指针为*T,取左孩子是(*T)->lchild,将左孩子传参则是 &((*T)->lchild )因为&的优先级较->低,所以可以省略一个括号:
&(*T)->lchild ;
2.ET(ElemType) ch,并对ch赋值
3.如果ch==‘#’(这里默认#是空节点),对*T赋空,意味着本节点不存入树中
4.如果非空,则(开辟与赋值)该节点,统称(生成)该节点
5.以递归方式进行左右孩子的传参,参数格式1中已写到;
四、二叉树的遍历
1、递归版:
//递归方式前序遍历二叉树
void PreOrderTraverse(BiTree T, int level)
{
if (T == NULL)
return;
/*此处表示对遍历的树结点进行的操作,根据你自己的要求进行操作,这里只是输出了结点的数据*/
//operation1(T->data);
operation2(T->data, level); //输出了层数
PreOrderTraverse(T->lchild, level + 1);
PreOrderTraverse(T->rchild, level + 1);
}
//此处在输出的基础上,并输出层数
void operation2(ElemType ch, int level)
{
cout << ch << "在第" << level << "层" << endl;
}
非递归的写法很简单,前序和中序还有后续的区别就是输出语句的位置不同
(前)左递归(中)右递归(后)
在递归首部要有判断来测空
if (T == NULL)
return;
2、非递归版:堆栈实现版
(先序与中序):
树结构:abd##fe###cg#h##i##
自程:
代码:
void xianxu(BiTree T)
{
stack<BiTree>s;
while(T||!s.empty())//栈不空
{
while(T)
{
s.push(T);
cout<<T->data<<" ";
T=T->lchild;
}
if(!s.empty())
{
T=s.top();
s.pop();
T=T->rchild;
}
}
}
void zhongxu(BiTree T)
{
stack<BiTree>s;
while(T||!s.empty())//栈不空
{
while(T)
{
s.push(T);
// cout<<T->data<<" "; 前序遍历时输出语句在这
T=T->lchild;
}
if(!s.empty())
{
T=s.top();
s.pop();
cout<<T->data<<" ";
T=T->rchild;
}
}
}
重点:
0、整个遍历的流程:
遍历顺序默认是前序,遍历时将节点指针压入栈中;
先遍历根节点,再以根节点的左子树为根节点依次遍历,当左子树遍历完毕时,弹出当前栈顶元素,并将指针指向该元素的右子树,然后继续左子树遍历,如此循环,直到遍历结束整棵树(标志:栈为空且指针指向Null)
1、前序与中序的区别就是输出语句的位置问题,在这种写法中,每一个节点其实是在程序中出现三次的,第一次是遍历时压入栈中,第二次是弹出该节点时,第三次是将指针指向该节点的右子树时。在压入栈时输出访问节点就是前序遍历,在弹出时访问该节点就是中序遍历,而后序遍历是无法通过改变输出语句完成的,因为在第二次时,已经将该节点从栈中弹出,无法再次访问,所以完成后序遍历需要其他的手段;
后序遍历:
typedef struct TreeNode * BinTree;
struct TreeNode {
ElementType Data;
BinTree Left;
BinTree Right;
int flag; //结点的结构中加了一个flag,用于标记是否被访问过
};
void PostorderTraversal (BinTree BT)
{
BinTree T = BT;
Stack S = CreatStack();
while (T || !IsEmpty(S))
{
while ( T && T -> flag == 0 )
//在结点里加了一个标记flag用于判断是否访问过,0表示未访问过,1表示访问过。若已经访问过,再次遇到就不再进入这个循环
{
Push (S, T);
T -> flag = 1;
T = T -> left;
}
T = Pop(S);
if ( T -> right && T -> right -> flag == 0 ) //若有右儿子且未访问过,再次压入栈
Push ( S , T );
else
printf ( "%d" , T -> Data );
T = T -> right;
}
}
重点:后序遍历的操作之一就是标记法,在创造节点时多增加一个flag属性,并赋0,对每个节点进行多次入栈判断,如果无右儿子则不会重复进栈,有右儿子则再次进栈,因为后续遍历的根节点是最后访问,所以需要再次进栈等待访问。
总结:不管怎么说磕磕绊绊的也算把二叉树的第一节给滚过来了,开心(*^▽^*)!