考研数据结构学习心得记录,代码水平不高,如有错误,我虚心改正!
二叉树是数据结构中非常重要的一部分,在学习过程中,我一直对于二叉树的实现存在诸多问题(自以为会做实际是眼高手低……),因此特对二叉树的建立以及前中后序三种遍历方法进行总结和练习,层序遍历等写完队列再写。
先给出二叉树的结构定义:
typedef struct Node
{
char elem;
BiTreeBode *lchild,*rchild;
} BiTreeNode,*BiTree;
这个地方略介绍下typedef的用法(作为小白的我之前不太理解为何“多此一举”……)。在这里typedef来命名一个结构时,可以省略该结构的标签(如上可省略Node),且将该结构的指针命名为BiTree,即BiTree等同于Node*。
首先是二叉树的建立。
经过查阅各博主论坛以及各种资料,鉴于考研试题的复习时间紧及代码难度要求略低,这里给出一种简单的二叉树建立方法,二叉树的前序递归建立。
代码如下:
//前序生成二叉树
bool PreCreateBiTree(BiTree &T)
{
char x;
cin>>x;
if(x=='#')
T=null;
else
{
T=(BiTreeNode*)malloc(sizeof(BiTreeNode));
if(T)
{
T->elem=x;
PreCreateBiTree(T->lchild);
PreCreateBiTree(T->rchild);
}
else
cout<<"生成节点错误!"<
这里要求输入前序序列并将空结点输入(以‘#‘代表空结点)。
建立二叉树之后,现给出前中后序的递归算法:
//前序递归遍历二叉树并打印
void PreOrder(BiTree &T)
{
if(T)
{
cout<elem<<' ';
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
//中序递归遍历二叉树并打印
void MidOrder(BiTree &T)
{
if(T)
{
MidOrder(T->lchild);
cout<elem<<' ';
MidOrder(T->rchild);
}
}
//后序递归遍历二叉树并打印
void PostOrder(BiTree &T)
{
if(T)
{
PostOrder(T->lchild);
PostOrder(T->rchild);
cout<elem<<' ';
}
}
递归算法十分简单,但代码性能不好(最重要的是考研试题应该不会考这么水的东西),因此给出三种遍历的非递归算法。
前中序算法的基本思路大致相同,后序算法略有不同,但也差别不大。
对于前中序算法,基本思路是:
而前中序的区别在于访问结点的位置,前序在入栈时访问,中序则在出栈时访问。
对于后序算法,在当前结点为空时,不能直接出栈(因为要先访问右孩子才能再访问根),而是判断栈顶元素的右孩子是否存在,若存在右孩子且右孩子未访问过,则进入右子树,否则才执行出栈。
在这里,对于右孩子的检验有两种方法(我见过的只有这两种,且比较简便):
这里我采用第二种方法实现后序遍历
//
//非递归算法
//前序遍历并打印的非递归算法
void PreOrderPro(BiTree &T)
{
Stack S; //声明栈并初始化
InitStack(S);
BiTree p=T; //建立p指向当前访问的结点
while(p||!IsEmpty(S)) //如果p和栈均空则跳出循环
{
if(p) //结点非空,入栈,打印并进入左孩子
{
cout<elem<<' ';
Push(S,p);
p=p->lchild;
}
else //结点为空,出栈并进入栈顶元素的右孩子
{
Pop(S,p);
p=p->rchild;
}
}
}
//中序遍历并打印的非递归算法
void MidOrderPro(BiTree &T)
{
Stack S; //声明栈并初始化
InitStack(S);
BiTree p=T; //建立p指向当前访问的结点
while(p||!IsEmpty(S)) //如果p和栈均空则跳出循环
{
if(p) //结点非空,入栈,并进入左孩子
{
Push(S,p);
p=p->lchild;
}
else //结点为空,出栈,打印栈顶元素并进入栈顶元素的右孩子
{
Pop(S,p); //并进入栈顶元素的右孩子
cout<elem<<' ';
p=p->rchild;
}
}
}
//后序遍历并打印的非递归算法
void PostOrderPro(BiTree &T)
{
Stack S; //声明栈并初始化
InitStack(S);
BiTree p=T; //建立p指向当前访问的结点
BiTree mark; //mark记录上一个打印过的结点
while(p||!IsEmpty(S)) //如果p和栈均空则跳出循环
{
if(p) //结点非空,入栈,并进入左孩子
{
Push(S,p);
p=p->lchild;
}
else
{
Top(S,p); //结点为空,取栈顶元素
if(p->rchild!=null&&p->rchild!=mark)//如果栈顶元素右孩子非空
p=p->rchild; //且右孩子不是上一个访问的结点
//则进入右孩子
else
{
cout<elem<<' '; //若右孩子空或已访问,则将其打印
mark=p; //并将mark置为该结点,出栈并将p置空
Pop(S,p);
p=null;
}
}
}
}