二叉树链式存储和先序,中序,后序,层次遍历代码实现和讲解

一.二叉树的链式存储(以递归的形式表示出来)

1.结构体(此处用字符型作为二叉树的数据域类型)

//定义树的结构体
typedef struct BiTNode
{
  char data;
  struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

2.定义串的结构体

//定义串的结构体
typedef struct
{
  char ch[Maxsize];
  int length;
}SString;

3.串执行的操作

//串赋值操作
void StrAssign(SString &T,char *chars)
{
	T.length = strlen(chars);
	T.ch[0] = T.length;
	for(int i = 1; i <= Maxsize; ++i)
	{
		T.ch[i] = *(chars++);
	}
}

//串遍历输出操作
void StrPrint(SString &T)
{
  int i;
  for(i = 1; i <= T.length; ++i)
  {
	  printf("%c",T.ch[i]);
  }
  printf("\n");
}

4.初始化二叉树和创建二叉树

创建二叉树类似于前序遍历,通过 '#' 为空,然后用先序遍历来表示出二叉树,将结果作为字符串用于在程序中表示出来

二叉树链式存储和先序,中序,后序,层次遍历代码实现和讲解_第1张图片

以前序遍历表示出来为:AB##CD##E## 

//初始化二叉树
void InitBiTree(BiTree &T)
{
	T = NULL;
	printf("初始化成功!\n");
}

//创建二叉树(类似于前序遍历来创建二叉树)
void CreatBiTree(BiTree &T,SString &S)
{

  char s = S.ch[h++];//定义一个变量s用来存储字符串的元素,然后作为判断是否为#;S.ch[h++]先将 
                       S.ch[h]的值赋值给s,然后S.ch[h]就为字符串的下一个元素;如刚才是 
                       S.ch[1],此时就是S.ch[2]
  if(s == '#')//如果元素为#,则说明该结点元素为空!表明该结点为一个空结点
	  T = NULL;//因为指向的结点为空结点,自然指针指向空,然后栈会弹出改行代码,继续执行if(s 
                 == '#')代码
  else
  {
    T = (BiTree)malloc(sizeof(BiTNode));//如果不是#,就会动态分配内存作为二叉树的结点,此时 
                                          指针T指向该结点
 	if(T == NULL)
	{
	  printf("动态内存分配失败,结束程序!\n");
	  exit(-1);
	}
	else
	{
		T->data = s;//将字符串中的元素赋值给结点的数据域
		CreatBiTree(T->lchild,S);//如刚才的例子,因为函数间用的是地址的形式,所以此时S指向ch 
                                   为:S.ch[2],然后进行递归
		CreatBiTree(T->rchild,S);//原理一致
	}
  }
}

5.遍历输出(以王道视频讲解总结)

二叉树链式存储和先序,中序,后序,层次遍历代码实现和讲解_第2张图片

前序遍历的过程:脑补出空结点,从根节点出发,画一条路,如果左边还有没走的路,优先往左边走,走到路的尽头(空结点)就往回走;如果左边没路了,就往右边走;如果左右都没路了,则往上面走。先序遍历就是第一次路过时访问结点。结果:ABDGECF 

中序遍历的过程:和上面一致,只不过是第二次路过时访问结点。结果:DGBEAFC

后序遍历的过程:也是和上面一致,只不过是第三次路过时访问结点。结果:GDEBFCA

遍历代码:

//前序遍历
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);//访问根节点
  }
}

6.层次遍历

算法思想:要想进行层次遍历,需要借助一个队列。首先二叉树根节点入队,然后出队,访问出队结点,若它有左子树,则将左子树根节点入队;若它有右子树,则将右子树的根节点入队。完成入队后出队,访问出队结点的左右子树...反复如此,直至队列为空。

队列结构体

//定义队列的结构体
typedef struct LinkNode
{
  BiTNode *data;//为了节省空间,并不是把树的结点全部都压进去,而是压入树的指针,树的指针类型是BiTNode,所以数据域也是BiTNode类型
  struct LinkNode *next;
}LinkNode;

typedef struct
{
  LinkNode *front,*rear;
}LinkQueue;

队列的操作

//初始化队列
void InitQueue(LinkQueue &Q)
{
	Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
	Q.front->next = NULL;
}

//入队
void EnQueue(LinkQueue &Q, BiTree &s)
{
  LinkNode *p = (LinkNode*)malloc(sizeof(LinkNode));
  p->data = s;
  p->next = NULL;
  Q.rear->next = p;
  Q.rear = p;
}

//出队
bool DeQueue(LinkQueue &Q,BiTree &s)
{
  if(Q.front == Q.rear)//这是判断队列为空的条件
		return false;
  LinkNode *p= Q.front->next;//定义一个指针指向Q.front->next的这个地址的结点
  s = p->data;//因为层次遍历输出,入列的是BiTNode类型的地址,所以将地址存于s中
  Q.front->next = p->next;//使p指向结点的后继节点挂在头结点的后面
  if(p == Q.rear)//因为此时Q.front->next已经改变,所以要表示头结点后面就只有一个结点这种特殊 
                   情况只能用p == Q.rear表示,因为最开始就令p = Q.front,该条件没有改变
	  Q.rear = Q.front;
  free(p);
  return true;
}

//判断队列为空
bool IsEmpty(LinkQueue &Q)
{
	if(Q.front == Q.rear)
		return true;
	else
		return false;
}

层次遍历

//层次遍历
void LevelOrder(LinkQueue &Q, BiTree &T)
{
  BiTree p = T;//定义一个指针,指向树的根节点
  EnQueue(Q,p); //先将指向根节点的指针压入队列中
  while(!IsEmpty(Q))//判断空,作为循环结束条件
  {
	DeQueue(Q,p);//出队
	Visit(p);//访问出队指针指向的结点
	if(p->lchild != NULL)//判断刚出队指针指向的结点是否有左孩子
		EnQueue(Q,p->lchild);//如果有就将指向其左孩子结点的指针入队
	if(p->rchild != NULL)//判断刚出队指针指向的结点是否有右孩子
		EnQueue(Q,p->rchild);//如果有就将指向其右孩子结点的指针入队
  }
}

运行结果

二叉树链式存储和先序,中序,后序,层次遍历代码实现和讲解_第3张图片

完整代码如下:

#include
#include
#include
#include
#define Maxsize 11
int h = 1;
//定义树的结构体
typedef struct BiTNode
{
  char data;
  struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

//定义队列的结构体
typedef struct LinkNode
{
  BiTNode *data;//为了节省空间,并不是把树的结点全部都压进去,而是压入树的指针,树的指针类型是BiTNode,所以数据域也是BiTNode类型
  struct LinkNode *next;
}LinkNode;

typedef struct
{
  LinkNode *front,*rear;
}LinkQueue;

//定义串的结构体
typedef struct
{
  char ch[Maxsize];
  int length;
}SString;

//队列的函数说明
void InitQueue(LinkQueue &Q);
void EnQueue(LinkQueue &Q, BiTree &s);
bool DeQueue(LinkQueue &Q, BiTree &s);
bool IsEmpty(LinkQueue &Q);

//二叉树的函数说明
void InitBiTree(BiTree &T);
void CreatBiTree(BiTree &T,SString &S);
void Visit(BiTree T);
void PreOrder(BiTree T);
void InOrder(BiTree T);
void PostOrder(BiTree T);
void LevelOrder(LinkQueue &Q, BiTree &T);

//字符串的函数说明
void StrAssign(SString &T,char *chars);
void StrPrint(SString &T);

int main(void)
{
  printf("定义一个队列Q\n");
  LinkQueue Q;
  printf("初始化队列Q\n");
  InitQueue(Q);
  printf("定义一个字符串S\n");
  SString S;
  printf("将字符串的元素赋值给S\n");
  StrAssign(S,"AB##CD##E##");
  printf("遍历输出字符串S\n");
  StrPrint(S);
  printf("定义一个树T\n");
  BiTree T;
  printf("初始化树T\n");
  InitBiTree(T);
  printf("创造树T\n");
  CreatBiTree(T,S);
  printf("先序遍历树T\n");
  PreOrder(T);
  printf("\n");
  printf("中序遍历树T\n");
  InOrder(T);
  printf("\n");
  printf("后序遍历树T\n");
  PostOrder(T);
  printf("\n");
  printf("层次遍历树T\n");
  LevelOrder(Q,T);
  return 0;
}

//初始化队列
void InitQueue(LinkQueue &Q)
{
	Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
	Q.front->next = NULL;
}

//入队
void EnQueue(LinkQueue &Q, BiTree &s)
{
  LinkNode *p = (LinkNode*)malloc(sizeof(LinkNode));
  p->data = s;
  p->next = NULL;
  Q.rear->next = p;
  Q.rear = p;
}

//出队
bool DeQueue(LinkQueue &Q,BiTree &s)
{
  if(Q.front == Q.rear)//这是判断队列为空的条件
		return false;
  LinkNode *p= Q.front->next;//定义一个指针指向Q.front->next的这个地址的结点
  s = p->data;//因为层次遍历输出,入列的是BiTNode类型的地址,所以将地址存于s中
  Q.front->next = p->next;//使p指向结点的后继节点挂在头结点的后面
  if(p == Q.rear)//因为此时Q.front->next已经改变,所以要表示头结点后面就只有一个结点这种特殊情况只能用p == Q.rear表示,因为最开始就令p = Q.front,该条件没有改变
	  Q.rear = Q.front;
  free(p);
  return true;
}

//判断队列为空
bool IsEmpty(LinkQueue &Q)
{
	if(Q.front == Q.rear)
		return true;
	else
		return false;
}

//串赋值操作
void StrAssign(SString &T,char *chars)
{
	T.length = strlen(chars);
	T.ch[0] = T.length;
	for(int i = 1; i <= Maxsize; ++i)
	{
		T.ch[i] = *(chars++);
	}
}

//串遍历输出操作
void StrPrint(SString &T)
{
  int i;
  for(i = 1; i <= T.length; ++i)
  {
	  printf("%c",T.ch[i]);
  }
  printf("\n");
}

//初始化二叉树
void InitBiTree(BiTree &T)
{
	T = NULL;
	printf("初始化成功!\n");
}

//创建二叉树(类似于前序遍历来创建二叉树)
void CreatBiTree(BiTree &T,SString &S)
{

  char s = S.ch[h++];//定义一个变量s用来存储字符串的元素,然后作为判断是否为#;S.ch[h++]先将S.ch[h]的值赋值给s,然后S.ch[h]就为字符串的下一个元素;如刚才是S.ch[1],此时就是S.ch[2]
  if(s == '#')//如果元素为#,则说明该结点元素为空!表明该结点为一个空结点
	  T = NULL;//因为指向的结点为空结点,自然指针指向空,然后栈会弹出改行代码,继续执行if(s == '#')代码
  else
  {
    T = (BiTree)malloc(sizeof(BiTNode));//如果不是#,就会动态分配内存作为二叉树的结点,此时指针T指向该结点
	if(T == NULL)
	{
	  printf("动态内存分配失败,结束程序!\n");
	  exit(-1);
	}
	else
	{
		T->data = s;//将字符串中的元素赋值给结点的数据域
		CreatBiTree(T->lchild,S);//如刚才的例子,因为函数间用的是地址的形式,所以此时S指向ch为:S.ch[2],然后进行递归
		CreatBiTree(T->rchild,S);//原理一致
	}
  }
}

//访问结点
void Visit(BiTree T)
{
	printf("%c",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 LevelOrder(LinkQueue &Q, BiTree &T)
{
  BiTree p = T;//定义一个指针,指向树的根节点
  EnQueue(Q,p); //先将指向根节点的指针压入队列中
  while(!IsEmpty(Q))//判断空,作为循环结束条件
  {
	DeQueue(Q,p);//出队
	Visit(p);//访问出队指针指向的结点
	if(p->lchild != NULL)//判断刚出队指针指向的结点是否有左孩子
		EnQueue(Q,p->lchild);//如果有就将指向其左孩子结点的指针入队
	if(p->rchild != NULL)//判断刚出队指针指向的结点是否有右孩子
		EnQueue(Q,p->rchild);//如果有就将指向其右孩子结点的指针入队
  }
}

 

你可能感兴趣的:(算法,c++,开发语言,数据结构)