数据结构:二叉树

定义

特点

  • 每个节点最多有两棵子树,所以二叉树中不存在度大于2的节点。
  • 左子树和右子树是有区别的,次序不能颠倒。
  • 即使某个节点只有1个子节点,也是有左右之分的。

特殊的二叉树:

  1. 斜树:正如上图的树1和树2,向一边斜的二叉树。
  2. 满二叉树:叶子节点都在最后一层,也就是说,非叶子节点都有左右子树

  1. 完全二叉树:

    对一棵具有n个节点的二叉树按层序编号,如果编号为 i(1<=i<=n)的节点与同样深度的满二叉树中编号为 i的节点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树,如下图所示

    ​ 就是说,这样 1开始,从左向右,从上至下的编号,如果所有有子节点的节点 i满足:

    ​ 左儿子 j:$ j = 2 * i$ ,右儿子 k: $ k = 2 * i + 1$,那么它就是一棵完全二叉树

    1. 完全二叉树不一定是满二叉树
    2. 满二叉树一定是完全二叉树
    3. 手写的堆就是一棵完全二叉树

性质

  1. 二叉树第 i层最多有\(2^{i-1}\)个节点。
  2. 深度为 k的二叉树最多有\(2^k-1\)个节点。(1 + 2 + 4 + 8 + ··· + \(2^{k - 1}\)
  3. 有 n个节点的完全二叉树的深度为 \(|log_2n| + 1\)\(|x|\)代表对x向下取整。证明略。
  4. 从1开始,将完全二叉树从左到右,从上至下,依次标号(如上图),那么对于节点 i,左儿子 j:$ j = 2 * i$ ,右儿子 k: $ k = 2 * i + 1$。

存储结构

顺序存储结构

就是刚刚的性质4,存储的是完全二叉树。(也可以不是)

用数组模拟,下标代表树的标号。

主要的应用:线段树,堆

链式存储结构

又名二叉链表,即在结构体中定义两个指针,分别指向它的左儿子和右儿子。

typedef struct BiTNode  /* 结点结构 */
{
   TElemType data;		/* 结点数据 */
   struct BiTNode *lchild,*rchild; /* 左右孩子指针 */
}BiTNode,*BiTree;

遍历二叉树

前序遍历

若二叉树为空则返回,否则先访问根节点,再前序遍历左子树,再前序遍历右子树

总结起来:根左右

中序遍历

若二叉树为空则返回,否则先中序遍历左子树,再访问根节点,再中序遍历右子树

总结起来: 左根右

后序遍历

若二叉树为空则返回,否则先后序遍历左子树,再后序遍历右子树,最后访问根节点

总结起来:左右根

层序遍历

顾名思义,一层一层的遍历二叉树。

前三种是递归的遍历,要用到栈,层序遍历则是循环的遍历,要用到队列!

具体实现方法见代码。


作业

作业题目: 二叉树存储结构的建立、遍历和应用树和二叉树遍历是树形结构的最基础、最重要的核心算法。本作业要求掌
握和巩固二叉树的存储结构的建立方法、二叉树的遍历方法、过程及应用。


作业要求:

  1. 编写建立二叉树的动态(或者静态) 二叉链表存储结构(左右链表示) 的程序,并以适当的形式显示和保存二叉树;
  2. 采用二叉树的上述二叉链表存储结构,编写程序实现二叉树的先序、中序和后序遍历的递归非递归算法以及层序遍历算法, 并以适当的形式显示和保存二叉树及其相应的遍历序列;
  3. 设计并实现判断任意一棵二叉树是否为完全二叉树的算法。
  4. 设计并实现计算任意一棵二叉树的宽度的(递归或非递归)算法。 二叉树的宽度是指其各层结点数的最大值。

思路:

  1. 非递归算法则是手写一个栈用于存储节点信息。
  2. 判断是否为完全二叉树,可采用层序遍历的方式,无论子节点是否为空,都加入到队列中。当遍历到空节点时,退出,判断队列剩余元素是否全为空。如果是则为完全二叉树。
  3. 计算宽度也是用的层序遍历的方法,每一层计算下队头到队尾的元素数,更新最大值即可。

栈的实现

typedef struct Stack
{
	BiTree s[MAXSIZE];//存储二叉树节点
	int top;	//栈顶下标
}Stack;

队列的实现

typedef struct Queue
{
	BiTree q[MAXSIZE];//存储节点
	int head;//头
	int tail;//尾
}Queue;

读入

读入前序遍历的二叉树,空节点使用 # 来表示

比如:ABDH#K###E##CFI###G#J##

然后再按照前序遍历的方式依次读入即可,遇到 #不递归。

代码

点击查看代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>  
#include <time.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

#define MAXSIZE 100 /* 存储空间初始分配量 */

typedef int Status;		/* Status是函数返回的类型*/

/* 用于构造二叉树 */
int treeIndex = 1;
typedef char String[MAXSIZE]; /*  0号单元存放串的长度 */
String str; // str为初始化的字符串 
FILE *fp;   // 保存用的文件指针 

typedef char TElemType;
TElemType Nil=' '; /* 字符型以空格符为空 */

typedef struct BiTNode  /* 结点结构 */
{
   TElemType data;		/* 结点数据 */
   struct BiTNode *lchild,*rchild; /* 左右孩子指针 */
}BiTNode,*BiTree;

/* 非递归使用的栈 */
typedef struct Stack
{
	BiTree s[MAXSIZE];
	int top;	
}Stack;

/* 层序遍历使用的队列 */
typedef struct Queue
{
	BiTree q[MAXSIZE];
	int head;
	int tail;
}Queue;

Status StrAssign(String T,char *chars);	// 前序遍历初始化 
Status visit(TElemType e);				// 访问树的节点元素	
Status InitBiTree(BiTree *T);			// 初始化 
void DestroyBiTree(BiTree *T);			// 释放二叉树 
void CreateBiTree(BiTree *T);			// 建立二叉树
Status BiTreeEmpty(BiTree T);			// 判空 
int BiTreeDepth(BiTree T);				// 求二叉树的深度 
TElemType Root(BiTree T);				// 求二叉树的根节点 
TElemType Value(BiTree p);				// 返回该节点的值 
void Assign(BiTree p,TElemType value);	// 给二叉树的节点赋值 
void PreOrderTraverseRec(BiTree T);		// 前序遍历递归版 
void PreOrderTraverse(BiTree T);		// 前序遍历非递归版 
void InOrderTraverseRec(BiTree T);		// 中序遍历递归版 
void InOrderTraverse(BiTree T);			// 中序遍历非递归版 
void PostOrderTraverseRec(BiTree T);	// 后序遍历递归版 
void PostOrderTraverse(BiTree T);		// 后序遍历非递归版 
void SeqTraverse(BiTree T);				// 层序遍历 
int CalcuWidth(BiTree T);				// 返回二叉树的最大宽度 
Status JudgeCBT(BiTree T);				// 判断是否为完全二叉树 


int main(int args, char * argv[])
{
	BiTree T;
	InitBiTree(&T);
	
	FILE *infile = fopen("in.txt","r");
	
	fp = fopen("out.txt","w");
	if(fp == NULL || infile == NULL)
	{
		(void) fprintf (stderr, "File open error!\n");//错误输出流 
		exit(EXIT_FAILURE);
	}
	
	String input;
	fprintf(stdout,"please input BinaryTree with Preorder:(Empty Node with #)\n");
	fscanf(infile,"%s", input);
	StrAssign(str,input);
	
	fprintf(stdout,"%s\n",input);
	fprintf(fp,"%s\n",input);
	
	CreateBiTree(&T);
	
	fprintf(stdout,"\nPreOrderTraverseRec:");
	fprintf(fp,"\nPreOrderTraverseRec:");
	PreOrderTraverseRec(T);
	
	fprintf(stdout,"\nPreOrderTraverse:");
	fprintf(fp,"\nPreOrderTraverse:");
	PreOrderTraverse(T);
	
	fprintf(stdout,"\nInOrderTraverseRec:");
	fprintf(fp,"\nInOrderTraverseRec:");
	InOrderTraverseRec(T);
	
	fprintf(stdout,"\nInOrderTraverse:");
	fprintf(fp,"\nInOrderTraverse:");
	InOrderTraverse(T);
	
	fprintf(stdout,"\nPostOrderTraverseRec:");
	fprintf(fp,"\nPostOrderTraverseRec:");
	PostOrderTraverseRec(T);
	
	fprintf(stdout,"\nPostOrderTraverse:");
	fprintf(fp,"\nPostOrderTraverse:");
	PostOrderTraverse(T);
	
	fprintf(stdout,"\nSeqTraverse:");
	fprintf(fp,"\nSeqTraverse:");
	SeqTraverse(T);
	
	fprintf(stdout,"\nCBT? %d(1:yes 0:no)\n",JudgeCBT(T)); 
	fprintf(fp,"\nCBT? %d(1:yes 0:no)\n",JudgeCBT(T)); 
	
	fprintf(stdout,"BiTree`s width:%d\n",CalcuWidth(T));
	fprintf(fp,"BiTree`s width:%d\n",CalcuWidth(T));

	DestroyBiTree(&T);
	
	fclose(fp);
	fclose(infile);
	
	return 0;
}



Status StrAssign(String T,char *chars)
{ 
	int i;
	if(strlen(chars) > MAXSIZE)
		return ERROR;
	else
	{
		T[0] = strlen(chars);
		for(i = 1;i <= T[0]; i ++ )
			T[i] = *(chars + i - 1);
		return OK;
	}
}

Status visit(TElemType e)
{
	printf("%c ",e);
	return OK;
}

/* 构造空二叉树T */
Status InitBiTree(BiTree *T)
{ 
	*T=NULL;
	return OK;
}

/* 初始条件: 二叉树T存在。操作结果: 销毁二叉树T */
void DestroyBiTree(BiTree *T)
{ 
	if(*T) /* 递归的销毁,类似于后序遍历 */ 
	{
		if((*T)->lchild) /* 有左孩子 */
			DestroyBiTree(&(*T)->lchild); /* 销毁左孩子子树 */
		if((*T)->rchild) /* 有右孩子 */
			DestroyBiTree(&(*T)->rchild); /* 销毁右孩子子树 */
		free(*T); /* 释放根结点 */
		*T = NULL; /* 空指针赋NULL */
	}
}

/* 按前序输入二叉树中结点的值(一个字符) */
/* #表示空树,构造二叉链表表示二叉树T。 */
void CreateBiTree(BiTree *T)
{ 
	TElemType ch;
	
	/* scanf("%c",&ch); */
	ch=str[treeIndex ++];

	if(ch == '#') 
		*T = NULL;
	else
	{
		*T = (BiTree)malloc(sizeof(BiTNode));
		if(!*T)
		{
			(void) fprintf (stderr, "Cannot allocate memory!\n");//错误输出流 
			exit(OVERFLOW);
		} 
			
		(*T)->data=ch; /* 生成根结点 */
		CreateBiTree(&(*T)->lchild); /* 构造左子树 */
		CreateBiTree(&(*T)->rchild); /* 构造右子树 */
	}
}

/* 初始条件: 二叉树T存在 */
/* 操作结果: 若T为空二叉树,则返回TRUE,否则FALSE */
Status BiTreeEmpty(BiTree T)
{ 
	if(T)
		return FALSE;
	else
		return TRUE;
}


/* 初始条件: 二叉树T存在。操作结果: 返回T的深度 */
int BiTreeDepth(BiTree T)
{
	int i, j;
	if(!T)
		return 0;
	if(T->lchild)
		i = BiTreeDepth(T->lchild);
	else
		i = 0;
	if(T->rchild)
		j = BiTreeDepth(T->rchild);
	else
		j = 0;
	return i > j ? i+1 : j+1;
}

/* 初始条件: 二叉树T存在。操作结果: 返回T的根 */
TElemType Root(BiTree T)
{ 
	if(BiTreeEmpty(T))
		return Nil;
	else
		return T->data;
}

/* 初始条件: 二叉树T存在,p指向T中某个结点 */
/* 操作结果: 返回p所指结点的值 */
TElemType Value(BiTree p)
{
	return p->data;
}

/* 给p所指结点赋值为value */
void Assign(BiTree p,TElemType value)
{
	p->data=value;
}

/* 前序递归遍历 */
void PreOrderTraverseRec(BiTree T)
{ 
	if(T == NULL)
		return;
	fprintf(stdout,"%c",T->data);/* 显示结点数据,可以更改为其它对结点操作 */
	fprintf(fp,"%c",T->data);
	PreOrderTraverseRec(T->lchild); /* 再先序遍历左子树 */
	PreOrderTraverseRec(T->rchild); /* 最后先序遍历右子树 */
}

/* 前序非递归遍历 */
void PreOrderTraverse(BiTree T)
{
	Stack stk;
	stk.top = 0;
	
	while(T || stk.top)
	{
		if(T)
		{
			fprintf(stdout,"%c",T->data);// 先打印根节点 
			fprintf(fp,"%c",T->data);
			stk.s[++ stk.top] = T;// 入栈 
			T = T->lchild;//  递归左节点 
		}else
		{
			T = stk.s[stk.top --]; // 回溯 
			T = T->rchild; 		   // 递归右节点 
		}
	}
}

/* 中序递归遍历 */
void InOrderTraverseRec(BiTree T)
{ 
	if(T == NULL)
		return;
	InOrderTraverseRec(T->lchild); /* 中序遍历左子树 */
	fprintf(stdout,"%c",T->data);/* 显示结点数据,可以更改为其它对结点操作 */
	fprintf(fp,"%c",T->data);
	InOrderTraverseRec(T->rchild); /* 最后中序遍历右子树 */
}
/* 中序非递归遍历 */
void InOrderTraverse(BiTree T)
{
	Stack stk;
	stk.top = 0;
	
	while(T || stk.top)
	{
		if(T)
		{
			stk.s[++ stk.top] = T; /* 入栈,递归左节点  */ 
			T = T->lchild;
		}else
		{
			T = stk.s[stk.top --]; /* 出栈,回溯,打印根节点 */ 
			fprintf(stdout,"%c",T->data);
			fprintf(fp,"%c",T->data);
			T = T->rchild;		   /* 递归右节点 */ 
		}
	}
}

/* 后序递归遍历 */
void PostOrderTraverseRec(BiTree T)
{
	if(T == NULL)
		return;
	PostOrderTraverseRec(T->lchild); /* 先后序遍历左子树  */
	PostOrderTraverseRec(T->rchild); /* 再后序遍历右子树  */
	fprintf(stdout,"%c",T->data);/* 显示结点数据,可以更改为其它对结点操作 */
	fprintf(fp,"%c",T->data);
}

/* 后序非递归遍历 */
void PostOrderTraverse(BiTree T)
{
	Stack stk;
	stk.top = 0;

	BiTree right = NULL; /*上一次遍历到的右节点 */ 
	while(T || stk.top)
	{
		if(T)
		{
			stk.s[++ stk.top] = T; /* 不断向左递归 */ 
			T = T->lchild;
		}else
		{
			T = stk.s[stk.top];
			
			if(T->rchild && T->rchild != right) /* 向右 */ 
			{
				T = T->rchild;
			}else /* 左右都不行的话,回溯,打印当前结点 */ 
			{
				stk.top --;
				fprintf(stdout,"%c",T->data);
				fprintf(fp,"%c",T->data);
				right = T;
				T = NULL;/* 防止死循环 */ 
			}
		}
	}
}

/* 层序遍历 */ 
void SeqTraverse(BiTree T)
{
	Queue qu;
	qu.head = 1;
	qu.tail = 0;
	qu.q[++ qu.tail] = T;
	
	while(qu.head <= qu.tail) /* 打印队头的元素,再将队头的元素的左右儿子入队 */ 
	{
		int lenth = qu.tail - qu.head + 1;
		while(lenth --)
		{
			BiTree root = qu.q[qu.head ++];
			fprintf(stdout,"%c",root->data);
			fprintf(fp,"%c",root->data);
			if (root->lchild)
				qu.q[++ qu.tail] = root->lchild;
			if(root->rchild)
				qu.q[++ qu.tail] = root->rchild;
		}
	}
}

/* 宽度计算 */ 
int CalcuWidth(BiTree T)
{
	if(T == NULL) return 0;
	Queue qu;
	qu.head = 1;
	qu.tail = 0;
	qu.q[++ qu.tail] = T;
	int res = 0; 
	
	while(qu.head <= qu.tail) 
	{
		int lenth = qu.tail - qu.head + 1;//一行一行的取宽度最大值
		if(res < lenth) res = lenth;
		while(lenth --)
		{
			BiTree root = qu.q[qu.head ++];
			if (root->lchild)
				qu.q[++ qu.tail] = root->lchild;
			if(root->rchild)
				qu.q[++ qu.tail] = root->rchild;
		}
	}
	return res;
}
/* 判断是否为完全二叉树 */ 
Status JudgeCBT(BiTree T)
{
	if(T == NULL)
	{
		return TRUE;
	}
	
	Queue qu;
	qu.head = 1;
	qu.tail = 0;
	qu.q[++ qu.tail] = T;
	
	while(qu.head <= qu.tail) /* 打印队头的元素,再将队头的元素的左右儿子入队 */ 
	{
		BiTree root = qu.q[qu.head ++];
		
		if(root == NULL)
		{
			break;
		}

		qu.q[++ qu.tail] = root->lchild;
			
		qu.q[++ qu.tail] = root->rchild;
	}
	
	while(qu.head <= qu.tail)
	{
		if(qu.q[qu.head ++] != NULL)
		{
			return FALSE;
		}
	}
	return TRUE;
}


参考文献

程杰. 大话数据结构:溢彩加强版[M]. 北京: 清华大学出版社, 2020.

posted @ 2022-10-15 21:13  江水为竭  阅读(178)  评论(0编辑  收藏  举报