数据结构学习—树(1)

树的定义

树(tree):n个节点构成的有点集合。
当n=0时,称为空树;

对于任一颗非空树(n>0),具有以下性质

在这里插入图片描述

  • 树种有一个称为根的特殊节点,用r表示;
  • 其余节点可分为m(m>0)个互不相交的有限集T1…TM,其中每个集合本身又是一棵树,称为原来树的子树(Subtree)
  • 子树是不相交的;
  • 除了根节点外,每个节点有且仅有一个父节点;
  • 一颗N个节点的树有N-1个条边。
    在这里插入图片描述

树的一些基本术语

  1. 节点的度:节点的子树个数
  2. 树的度:树的所有节点中最大的度数
  3. 叶节点:度为0的节点
  4. 父节点:有子树的节点是其子树的根节点的父节点
  5. 子节点:若A节点是B节点的父节点,则称B节点是A节点的父节点;子节点也称孩子节点
  6. 兄弟节点:具有同一父节点的各个节点是彼此的兄弟节点
  7. 路径和路径长度:从节点n1到nk的路径为一个节点序列n1,n2,…,nk,相邻节点是子父节点。路径所包含的个数为路径的长度。
  8. 祖先节点:沿树根跟到某一节点路径上的所有节点都是这个节点的祖先节点
  9. 子孙节点:某一节点的子树中的所有节点是这个节点的子孙
  10. 节点的层次:规定根节点在1层,其它任一节点的层数是其父节点的层数加1
  11. 树的深度:树中所有节点中的最大层次是这颗树的深度

树的孩子兄弟表示法

在这里插入图片描述

树结构中,位于同一层的节点之间互为兄弟节点。例如,图 1 的普通树中,节点 A、B 和 C 互为兄弟节点,而节点 D、E 和 F 也互为兄弟节点。孩子兄弟表示法,采用的是链式存储结构,其存储树的实现思想是:从树的根节点开始,依次用链表存储各个节点的孩子节点和兄弟节点。
因此,该链表中的节点应包含以下 3 部分内容

  1. 节点的值
  2. 指向孩子节点的指针
  3. 指向兄弟节点的指针

二叉树的定义

二叉树T:一个有穷的节点集合。这个集合可以为空,若不为空,则它是由根节点和称为其左子树Tr和右子树Tr的两个不相交的二叉树组成。

  • 二叉树的五种基本形态
  • ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200329224746796.png

特殊二叉树

在这里插入图片描述

二叉树的性质

  • 一个二叉二叉树第i层的最大节点数为:2^(i-1),i>=1
  • 深度为k的二叉树有最大节点总数为:2^k-1,k>=1
  • 对于任何非空二叉树T,若n0表示叶节点的个数、n2是度为2的非叶节点个数,
    那么两者满足关系n0=n2+1
    在这里插入图片描述

二叉树的存储结构

顺序存储结构

完全二叉树:按从上至下、从左到右顺序存储n个节点的完全二叉树的节点父子关系
在这里插入图片描述
如果一般二叉树采用这种结构存储就会 造成巨大的空间浪费

顺序存储的基本运算

#include "stdio.h"    
#include "stdlib.h"
#include "math.h"  

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

#define MAX_TREE_SIZE 100 //二叉树的最大结点数

typedef int Status;
typedef int TElemType;  // 树结点的数据类型
typedef TElemType SqBiTree[MAX_TREE_SIZE];

typedef struct
{
	int level,order; // 结点的层,以及在层中的序号
}Position;

TElemType Nil=0; //  设整型以0为空
//打印元素值
Status visit(TElemType c)
{
	printf("%d ",c);
	return OK;
}

// 构造空二叉树T
Status InitBiTree(SqBiTree T)
{
	int i;
	for(i=0;i<MAX_TREE_SIZE;i++)
		T[i]=Nil; // 初值为空0
	return OK;
}

// 按层序次序输入二叉树中结点的值, 构造顺序存储的二叉树T
Status CreateBiTree(SqBiTree T)
{ 
	int i=0;
 	printf("请按层序输入结点的值(整型),0表示空结点,输999结束。结点数≤%d:\n",MAX_TREE_SIZE);
	while(i<10)
	{
		T[i]=i+1;
		
		if(i!=0&&T[(i+1)/2-1]==Nil&&T[i]!=Nil) // 此结点(不空)无父亲节点且不是根 
		{
			printf("出现无父亲节点的非根结点%d\n",T[i]);
			exit(ERROR);
		}
		i++;
	}
	while(i<MAX_TREE_SIZE)
	{
		T[i]=Nil; // 将空赋值给T的后面的结点 
		i++;
	}

	return OK;
}

#define ClearBiTree InitBiTree


// 判断二叉树是否为空
Status BiTreeEmpty(SqBiTree T)
{ 
	if(T[0]==Nil) // 根结点为空,则树空
		return TRUE;
	else
		return FALSE;
}

// 返回二叉树的深度
int BiTreeDepth(SqBiTree T)
{ 
   int i,j=-1;
   for(i=MAX_TREE_SIZE-1;i>=0;i--) // 找到最后一个结点
     if(T[i]!=Nil)
       break;
   i++; 
   do
     j++;
   while(i>=powl(2,j));// 计算2的j次幂
   return j;
}

// 用e返回T的根
Status Root(SqBiTree T,TElemType *e)
{ 
	if(BiTreeEmpty(T)) // T空 
		return ERROR;
	else
	{	
		*e=T[0];
		return OK;
	}
}


//  返回处于位置e(层,本层序号)的结点的值
TElemType Value(SqBiTree T,Position e)
{ 
	 return T[(int)powl(2,e.level-1)+e.order-2];
}


// 修改处于位置e(层,本层序号)的结点的值
Status Assign(SqBiTree T,Position e,TElemType value)
{ 
	int i=(int)powl(2,e.level-1)+e.order-2; // 将层、本层序号转为矩阵的序号 
	if(value!=Nil&&T[(i+1)/2-1]==Nil) // 给叶子赋非空值但父亲节点为空 
		return ERROR;
	else if(value==Nil&&(T[i*2+1]!=Nil||T[i*2+2]!=Nil)) //  给父亲节点赋空值但有叶子(不空) 
		return ERROR;
	T[i]=value;
	return OK;
}

// 若e是T的非根结点,则返回它的父亲节点
TElemType Parent(SqBiTree T,TElemType e)
{ 
	int i;
	if(T[0]==Nil) // 空树
		return Nil;
	for(i=1;i<=MAX_TREE_SIZE-1;i++)
		if(T[i]==e) // 找到e
			return T[(i+1)/2-1];
	return Nil; // 没找到e
}



// 返回e的左儿子
TElemType LeftChild(SqBiTree T,TElemType e)
{ 
	int i;
	if(T[0]==Nil) // 空树
		return Nil;
	for(i=0;i<=MAX_TREE_SIZE-1;i++)
		if(T[i]==e) // 找到e
			return T[i*2+1];
	return Nil; // 没找到e
}


// 返回e的右儿子
TElemType RightChild(SqBiTree T,TElemType e)
{ 
	int i;
	if(T[0]==Nil) // 空树
		return Nil;
	for(i=0;i<=MAX_TREE_SIZE-1;i++)
		if(T[i]==e) // 找到e
			return T[i*2+2];
	return Nil; // 没找到e
}


// 返回e的左兄弟
TElemType LeftSibling(SqBiTree T,TElemType e)
{ 
	int i;
	if(T[0]==Nil) // 空树
		return Nil;
	for(i=1;i<=MAX_TREE_SIZE-1;i++)
		if(T[i]==e&&i%2==0) //
			return T[i-1];
	return Nil; // 没找到e
}


// 返回e的右兄弟
TElemType RightSibling(SqBiTree T,TElemType e)
{ 
	int i;
	if(T[0]==Nil) // 空树
		return Nil;
	for(i=1;i<=MAX_TREE_SIZE-1;i++)
		if(T[i]==e&&i%2)
			return T[i+1];
	return Nil; // 没找到e
}


void PreTraverse(SqBiTree T,int e)
{ 
	visit(T[e]);
	if(T[2*e+1]!=Nil) // 左子树不空
		PreTraverse(T,2*e+1);
	if(T[2*e+2]!=Nil) // 右子树不空
		PreTraverse(T,2*e+2);
}


// 先序遍历
Status PreOrderTraverse(SqBiTree T)
{ 
	if(!BiTreeEmpty(T)) // 树不空 
	 PreTraverse(T,0);
	printf("\n");
	return OK;
}

void InTraverse(SqBiTree T,int e)
{ 
	if(T[2*e+1]!=Nil) // 左子树不空 
		InTraverse(T,2*e+1);
	visit(T[e]);
	if(T[2*e+2]!=Nil) // 右子树不空 
		InTraverse(T,2*e+2);
}


// 中序遍历
Status InOrderTraverse(SqBiTree T)
{ 
	if(!BiTreeEmpty(T)) // 树不空
		InTraverse(T,0);
	printf("\n");
	return OK;
}


void PostTraverse(SqBiTree T,int e)
{ 
	if(T[2*e+1]!=Nil) // 左子树不空
		PostTraverse(T,2*e+1);
	if(T[2*e+2]!=Nil) // 右子树不空
		PostTraverse(T,2*e+2);
	visit(T[e]);
}

// 后序遍历T
Status PostOrderTraverse(SqBiTree T)
{ 
	if(!BiTreeEmpty(T)) // 树不空
		PostTraverse(T,0);
	printf("\n");
	return OK;
}

// 层序遍历二叉树
void LevelOrderTraverse(SqBiTree T)
{ 
	int i=MAX_TREE_SIZE-1,j;
	while(T[i]==Nil)
		i--; // 找到最后一个非空结点的序号
	for(j=0;j<=i;j++)  // 从根结点起,按层序遍历二叉树
		if(T[j]!=Nil)
			visit(T[j]); // 只遍历非空的结点
	printf("\n");
}

// 逐层、按本层序号输出二叉树 
void Print(SqBiTree T)
{ 
	int j,k;
	Position p;
	TElemType e;
	for(j=1;j<=BiTreeDepth(T);j++)
	{
		printf("第%d层: ",j);
		for(k=1;k<=powl(2,j-1);k++)
		{
			p.level=j;
			p.order=k;
			e=Value(T,p);
			if(e!=Nil)
				printf("%d:%d ",k,e);
		}
		printf("\n");
	}
}


int main()
{
	Status i;
	Position p;
	TElemType e;
	SqBiTree T;
	InitBiTree(T);
	CreateBiTree(T);
	printf("建立二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T));
	i=Root(T,&e);
	if(i)
		printf("二叉树的根为:%d\n",e);
	else
		printf("树空,无根\n");
	printf("层序遍历二叉树:\n");
	LevelOrderTraverse(T);
	printf("前序遍历二叉树:\n");
	PreOrderTraverse(T);
	printf("中序遍历二叉树:\n");
	InOrderTraverse(T);
	printf("后序遍历二叉树:\n");
	PostOrderTraverse(T);
	printf("修改结点的层号3本层序号2。");
	p.level=3;
	p.order=2;
	e=Value(T,p);
	printf("待修改结点的原值为%d请输入新值:50 ",e);
	e=2020;
	Assign(T,p,e);
	printf("前序遍历二叉树:\n");
	PreOrderTraverse(T);
	printf("结点%d的父亲节点为%d,左右儿子分别为",e,Parent(T,e));
	printf("%d,%d,左右兄弟分别为",LeftChild(T,e),RightChild(T,e));
	printf("%d,%d\n",LeftSibling(T,e),RightSibling(T,e));
	ClearBiTree(T);
	printf("清除二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T));
	i=Root(T,&e);
	if(i)
		printf("二叉树的根为:%d\n",e);
	else
		printf("树空,无根\n");
	
	return 0;
}

链式存储结构

typedef struct TreeNode *BinTree;
typedef BinTree Position;
struct TreeNode{
	ElementType Data;
	BinTree Left;
	BinTree Right;
}

在这里插入图片描述

二叉树的遍历

(1)先序遍历

  1. 访问根节点
  2. 先序遍历其左子树
  3. 先序遍历其右子树
void PreOrderTraverse(BiTree T)
{ 
	if(T==NULL)
		return;
	printf("%c",T->data);
	PreOrderTraverse(T->lchild); //再先序遍历左子树 
	PreOrderTraverse(T->rchild); //最后先序遍历右子树 
}

先序遍历=> A B D F E C G H I
在这里插入图片描述
(2)中序遍历

  1. 中序遍历其左子树
  2. 访问根节点
  3. 中序遍历其右子树

中序遍历=> D B E F A G H C I
在这里插入图片描述
(3)后序遍历

  1. 后序遍历其左子树

  2. 后序遍历其右子树

  3. 访问根节点
    后序遍历=> D E F B H G I C A
    在这里插入图片描述
    (4)中序遍历非递归算法

  4. 遇到一个节点,就把他压栈,并去遍历他的左子树

  5. 当左子树遍历结束后,从栈顶弹出这个节点并访问它

  6. 然后按其右指针再去中序遍历该节点的右子树

void InOrderTraversal(BinTree BT)
{
	BinTree T = BT;
	Stack S = CreatStack(MaxSize);
	while( T || !IsEmpty(S) ){
		while(T){
			Push(S,T);
			T = T->Left;
		}
		if(!IsEmpty((S)){
			T = Pop(S);
			printf("%d",T->Data);
			T = T->Right;
		}
	}
}

(5)层序遍历
核心问题就是二维结构的线性化,可以使用堆栈、队列实现
队列实现:遍历从根节点开始,首先将根节点入队,然后开始执行循环:节点出队、访问该节点、其左右儿子入队
基本过程:

  1. 从队列中取出一个元素
  2. 访问该元素所指节点
  3. 若该元素所指节点的左、右孩子节点为空,则将其左、右孩子的指针顺序入队。
void LevelOrderTraversal(BinTree BT)
{
	Queue Q; BinTree T;
	if(!BT) return;
	Q = CreatQueue(Maxsize);
	AddQ(Q,BT);
	while (!IsEmptyQ(Q)){
		T = DeleteQ(Q);
		printf("%d",T->Data);
		if (T->Left) AddQ(Q,T->Left);
		if (T->Right) AddQ(Q,T->Right);
	}		
}

链式存储的基本运算

#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "io.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;

// 用于构造二叉树
int in=1;
typedef char String[24]; //  0号单元存放串的长度
String str;

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;
	}
}


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

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

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


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

//销毁二叉树
void DestroyBiTree(BiTree *T)
{
	if(*T)
	{
		if((*T)->lchild) // 有左儿子
			DestroyBiTree(&(*T)->lchild); // 销毁左儿子子树
		if((*T)->rchild) // 有右儿子
			DestroyBiTree(&(*T)->rchild); // 销毁右儿子子树
		free(*T); // 释放根结点
		*T=NULL; // 空指针赋0
	}
}

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

	if(ch=='#')
		*T=NULL;
	else
	{
		*T=(BiTree)malloc(sizeof(BiTNode));
		if(!*T)
			exit(OVERFLOW);
		(*T)->data=ch; // 生成根结点
		CreateBiTree(&(*T)->lchild); // 构造左子树
		CreateBiTree(&(*T)->rchild); // 构造右子树
	}
 }

//  若T为空二叉树,则返回TRUE,否则FALSE
Status BiTreeEmpty(BiTree T)
{
	if(T)
		return FALSE;
	else
		return TRUE;
}

#define ClearBiTree DestroyBiTree

// 返回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的根
TElemType Root(BiTree T)
{
	if(BiTreeEmpty(T))
		return Nil;
	else
		return T->data;
}


// 返回p所指结点的值
TElemType Value(BiTree p)
{
	return p->data;
}

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

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

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

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


int main()
{
	int i;
	BiTree T;
	TElemType e1;
	InitBiTree(&T);


	StrAssign(str,"ABDH#K###E##CFI###G#J##");

	CreateBiTree(&T);

	printf("构造空二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T));
	e1=Root(T);
	printf("二叉树的根为: %c\n",e1);

	printf("\n前序遍历二叉树:");
	PreOrderTraverse(T);
	printf("\n中序遍历二叉树:");
	InOrderTraverse(T);
	printf("\n后序遍历二叉树:");
	PostOrderTraverse(T);
	ClearBiTree(&T);
	printf("\n清除二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T));
	i=Root(T);
	if(!i)
		printf("树空,无根\n");

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值