二叉树的遍历操作代码
文件申明
主要包括数据域和左右指针
#include<iostream>
#include<string.h>
#include<stack>
#include<queue>
using namespace std;
#define ElemType int
typedef struct BiTNode{
ElemType data; //数据域
struct BiTNode *lchild;
struct BiTNode *rchild; //左右孩子指针
}BiTNode, *BiTree;
visit函数
void visit(BiTree T){
printf("%d",T->data);
}
二叉树的递归代码实现
先序遍历
//先序遍历
void PreOrder(BiTree T){
if(T!=NULL){ //序是指根节点在什么时候被访问
visit(T); //先访问根节点,然后再访问左右子树
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
中序遍历
//中序遍历
void InOrder(BiTree T){
if(T!=NULL){
InOrder(T->lchild); //先访问左子树,然后访问根节点,最后访问右子树
visit(T);
InOrder(T->rchild);
}
}
后续遍历
//后序遍历
void PostOrder(BiTree T){
if(T!=NULL){
PostOrder(T->lchild); //先访问左子树,然后访问右子树,最后访问根节点
PostOrder(T->rchild);
visit(T);
}
}
二叉树遍历的非递归代码实现
先序遍历
第一种思路,采用深度优先遍历的方式
//前序非递归遍历
//不断访问左孩子,如果左孩子已经访问完,则说明已经到叶子节点
//然后取叶子节点的根节点,访问其右孩子,然后再循环判断其右孩子的子树是否有左孩子
//如果为空就不断出栈,然后输出
void PreOrder_2(BiTree T){ //深度优先遍历实现
stack<BiTree> Stack;
BiTree p = T;
while(p || Stack.empty()){
if(p){
visit(p); //如果p节点非空,访问当前节点,并入栈
Stack.push(p);
p = p->lchild; //一直访问,直到左孩子为空
}
else{
p = Stack.top(); //如果为空,则出栈,然后直接转向根节点的右孩子,因为左孩子和根节点都已经访问完
Stack.pop();
p = p->rchild;
}
}
}
第二种方式,类似于层次遍历
void PreOrder_3(BiTree T){ //广度优先遍历实现
stack<BiTree> stack;
BiTree p = T;
stack.push(p);
while(p){ //右孩子先入栈,然后左孩子后入栈
p = stack.top(); //取栈顶元素
visit(p);
stack.pop();
if(p ->rchild != NULL)
stack.push(p->rchild);
if(p ->lchild != NULL)
stack.push(p->lchild);
}
}
中序遍历
//中序非递归遍历
//一直访问左孩子直到根节点
//出栈,然后访问栈顶元素,如果有右孩子就继续访问右孩子的左子树,没有右孩子就继续出栈遍历
void InOrder_2(BiTree T){
stack<BiTree> Stack;
BiTree p = T;
while(p || Stack.empty()){
if(p){
Stack.push(p); //如果p节点非空,访问当前节点,并入栈
p = p->lchild; //一直访问,直到左孩子为空
}
else{
p = Stack.top(); //这里先输出叶子节点,也就是左孩子节点
Stack.pop();
visit(p);
p = p->rchild;
}
}
}
后序遍历
第一种思路:采用辅助指针方式
//后序非递归遍历
//加入一个辅助指针判断是否是第一次访问
//整体思路如下:首先不断访问其左孩子节点,当访问到叶子节点时,需要判断栈顶元素右孩子节点是否被访问过,
//如果没有被访问过,则继续访问右子节点,如果访问过,则输出该根节点,然后并记录目前访问的节点
void PostOrder_2(BiTree T){
stack<BiTree> Stack;
BiTree p = T;
BiTree r = NULL;
while(p || Stack.empty()){
if(p){
Stack.push(p); //如果p节点非空,访问当前节点,并入栈
p = p->lchild; //一直访问,直到左孩子为空
}
else{
p = Stack.top(); //取出栈顶元素判断是否被访问过
if(p->rchild && p->rchild != r){ //未被访问过,则对其右子树进行访问
p = p->rchild;
}
else{ //已经访问过,则进行输出
Stack.pop(); //将根节点弹出
visit(p);
r = p; //下次遍历的时候如果是其右子树,那么刚好已经访问过了,直接进行输出
p = NULL; //节点访问完后,重置p指针
}
}
}
}
和第一种一样,添加一个标志位,作用类似于第一个的指针
typedef struct BiTNode{
ElemType data; //数据域
struct BiTNode *lchild;
struct BiTNode *rchild; //左右孩子指针
int flag;
}BiTNode, *BiTree;
//和2思路一致,在树的结构体中增加一个标志位,用于判断其右子树是否被访问过
void PostOrder_3(BiTree T){
stack<BiTree> Stack;
BiTree p = T;
BiTree r = NULL;
while(p || Stack.empty()){
if(p){
Stack.push(p); //如果p节点非空,访问当前节点,并入栈
p = p->lchild; //一直访问,直到左孩子为空
}
else{
p = Stack.top(); //取出栈顶元素判断是否被访问过
if(p->rchild && T->flag == 0){ //未被访问过,则对其右子树进行访问
p = p->rchild;
p->flag = 1;
}
else{ //已经访问过,则进行输出
Stack.pop(); //将根节点弹出
visit(p);
}
}
}
}
第三种实现方式,修改先序遍历,(左右根)—>(右左根)
然后用另外一个栈保存,遍历完之后再输出就得到(右左根)
//先对其进行先序遍历,不过需要修改一下访问次序,先访问其右子树后在访问左子树
//然后在访问的时候加入另外一个栈中,这样得到的序列为根右左,最后对其进行出栈,得到左右根
void PostOrder_4(BiTree T){
stack<BiTree> stack1;
stack<BiTree> stack2;
BiTree p = T;
stack1.push(p);
while(p){ //右孩子先入栈,然后左孩子后入栈
p = stack1.top(); //取栈顶元素
visit(p);
stack2.push(p); //将遍历得到的序列加入第二个栈中
stack1.pop();
if(p ->rchild != NULL)
stack1.push(p->lchild);
if(p ->lchild != NULL)
stack1.push(p->rchild);
}
while(stack2.empty()){ //再出栈
visit(stack2.top());
stack2.pop();
}
}
层次遍历
需要采用辅助队列方式实现
//层次遍历
//需要一个辅助队列实现,首先访问根节点,入队
//出队,访问其左右子树,并将其入队,再次访问队头
void LeverOrder(BiTree T){
queue<BiTree> Treeqe; //树队列
BiTree p;
Treeqe.push(T); //根节点入队
while(T){
p = Treeqe.front();
visit(p); //访问出队节点
Treeqe.pop();
if(p->lchild){ //左子树不为空,访问左子树
Treeqe.push(p->lchild);
}
if(p->rchild){ //右子树不为空,访问右子树
Treeqe.push(p->rchild);
}
}
}
中序二叉排序树基本操作
文件申明
#include<iostream>
using namespace std;
#define ElemType int
//线索二叉树,利用叶子结点的两个空指针找到前驱节点和后继节点
/* 增加两个标志域
ltag = 0 lchild指向结点的左孩子
ltag = 1 lchild指向结点的前驱节点
rtag = 0 rchild指向结点的右孩子
rtag = 1 rchild指向结点的后驱节点
*/
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;
}ThreadNode,*ThreadTree;
中序实现二叉排序树的线索化
//中序遍历实现二叉树线索化
//pre为p的前驱节点
//在中序遍历的过程中,如果p的左指针为空,则指向pre
//如果pre的右子树为空,则指向p
void InThread(ThreadTree &p, ThreadTree &pre){
if(p!=NULL){
InThread(p->lchild,pre); //递归,线索化左子树
if(p->lchild == NULL){ //左子树为空,建立线索前驱
p->lchild = pre;
p->ltag = 1;
}
if(pre!=NULL && pre->rchild == NULL){ //右子树为空,建立后继线索
pre->rchild = p;
pre->rtag = 1;
}
pre = p;
InThread(p->rchild,pre); //递归,线索化右子树
}
}
void CreateInThread(ThreadTree T){
ThreadTree pre = NULL;
if(T != NULL){
InThread(T,pre);
pre->rchild = NULL;
pre->rtag = 1;
}
}
求中序线索二叉树下的第一个节点
//求中序线索二叉树下的第一个节点
ThreadNode *FirstNode(ThreadNode *p){
if(p->rtag == 0) p = p->lchild; //最左下节点
return p;
}
求中序线索二叉树结点p在中序序列下的后继
//求中序线索二叉树结点p在中序序列下的后继
ThreadNode *NextNode(ThreadNode *p){
//如果右子树不为空,则下一个节点就是右子树的最左下节点
if(p->rtag == 0) return FirstNode(p->rchild);
//如果为空,直接返回线索后继
else{
return p->rchild;
}
}
求中序线索二叉树最后一个节点
//求中序线索二叉树最后一个节点
ThreadNode *LastNode(ThreadNode *p){
//最右下节点,为最后一个节点
if(p->rtag == 0){ //最右下节点
p = p->rchild;
}
return p;
}
求中序线索二叉树的结点p的前驱
//求中序线索二叉树的结点p的前驱
ThreadNode *FrontNode(ThreadNode *p){
//如果左子树不为空,则下一个节点就是左子树的最右下节点
if(p->lchild == 0) return LastNode(p->lchild);
//如果为空,直接返回线索前驱
else{
return p->lchild;
}
}
不含头结点的中序线索二叉树的中序遍历
//不含头结点的中序线索二叉树的中序遍历
void InOrder(ThreadNode *T){
for(ThreadNode *p=FirstNode(T); p!=NULL; p = NextNode(p))
printf("%d\n",p->data);
}