学习笔记:树和二叉树

1. 树的定义

树(Tree)是n (n>=0) 个结点的有限集。n=0 时称为空树。在任意一个非空树中:

  1. 有且仅有一个特定的称为根 (root) 的结点。
  2. 当 n > 1时。其余结点可分为 m (m>0) 个互不相交的有限集T1,T2,…Tm, 其中每一个集合本身又是一颗树,并且称为根的子树(SubTree)

在这里插入图片描述

在这里插入图片描述

注意:

  1. n>0 时根结点是唯一的。
  2. m>0 时,子树的个数没有限制,但它们一定是互不相交的。

在这里插入图片描述

1.1 结点的分类

树的结点包含一个数据元素及若干指向其子树的分支。

结点拥有的子树数称为结点的度。

度为0的结点称为叶结点或终端结点;度不为0的结点称为非终端节点和分支结点。

除根结点之外,分支结点也称为内部结点。

树的度是树内各节点的度的最大值。

在这里插入图片描述

1.2 结点之间的关系

在这里插入图片描述

1.3 树的其它相关概念

**结点的层次从根开始定义起,根为第一层,根的孩子为第二层。**若某结点在第l层,则其子树就在第l+1层。若其双亲在同一层的结点互为堂兄弟。

树中结点的最大层次称为树的深度或高度。

在这里插入图片描述

如果将树中结点的各子树看成从左至右是有次序的,不能互换的,则称该树为有序树,否则称为无序树。

森林是m (m>=0) 棵互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林。

2. 树的抽象数据类型

ADT 树 (tree)
Data
    树是由一个根结点和若干棵子树构成的。树中结点具有相同数据类型及层次关系。
Operation
    InitTree(&T):构造空树T。
    DestroyTree(&T):销毁树T。
    CreateTree(&T,definition):按definition中给树的定义来构造树。
    ClearTree(&T):若树T存在,则将树T清为空树。
    TreeEmpty(T):若T为空树,返回true,否则返回false。
    TreeDepth(T):返回T的深度。
    Root(T):返回T的根结点。
    Value(T.cur_e):cur_e是树T中的一个结点,返回此结点的值。
    Assgin(T,cur_e,value):给树T的结点cur_e赋值为value。
    Parent(T,cur_e):若cur_e是树T的非根结点,则返回它的双亲,否则返回空。
    LeftChild(T,cur_e):若cur_e是树T的非叶结点,则返回它的最左双亲,否则返回为空。
    RightSibling(T,cur_e):若cur_e有右兄弟,则返回它的右兄弟,否则返回空。
    InsertChild(&T,&p,i,c):其中p指向树T的某个结点,i为所指结点p的度加上1,非空树c与T不相交,操作结果为插入c为树T中p所指结点的第i棵子树。
    DeleteChild(&T,&p,i):其中p指向树T的某个结点,i为所指结点p的度,操作结果为删除T中p所指结点的第i棵子树。
endADT

2. 二叉树

二叉树,是n (n>=0) 个结点的有限集合,该集合为空集或者为空集(称为空二叉树),或者由一个根结点和两颗互不相交的,分别称为根结点的左子树和右子树的二叉树组成。

在这里插入图片描述

2.1 二叉树的特点

  1. 每个结点最多有两棵子树,所以二叉树中不存在度大于2的结点。注意不是只有两棵子树,而是最多有。
  2. 左子树和右子树是有顺序的,次序不能任意颠倒。
  3. 即使树中某结点只有一颗子树,也要区分它是左子树还是右子树。

二叉树具有以下五种基本形态:

  1. 空二叉树。
  2. 只有一个根结点。
  3. 根结点只有左子树。
  4. 根结点只有右子树。
  5. 根结点既有左子树又有右子树。

2.2 特殊二叉树

  1. 斜树

    所有的结点都只有左子树的二叉树叫左斜树。所有结点都只有右子树的二叉树叫右斜树。这两者统称为斜树。

在这里插入图片描述
在这里插入图片描述

  1. 满二叉树

    在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。

在这里插入图片描述

满二叉树的特点:

  1. 叶子只能出现在最下层。

  2. 非叶子结点的度一定是2。

  3. 在同样深度的二叉树中,满二叉树的结点个数最多,叶子树最多。

  4. 完全二叉树

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

在这里插入图片描述

完全二叉树的特点:

  1. 叶子结点只能出现在最下两层。
  2. 最下层叶子一定集中在左部连续位置。
  3. 倒数两层,若有叶子结点,一定都在右部连续位置。
  4. 如果结点度为1,则该结点只有左孩子,即不存在只有右子树的情况。
  5. 同样结点数的二叉树,完全二叉树的深度最小。

3. 二叉树的性质

性质1:在二叉树的第n层至多有2^(n-1)个结点( i>=1)。

性质2:深度为k的二叉树至多有2^k-1个结点(k>=1)。

性质3:对任何一棵二叉树T,如果其终端结点数位n0,度为2的结点数位n2,则n0 = n2+1。[外链图在这里插入图片描述

推导:树T结点总数n = n0 + n1 + n2     //5 + 1 + 4 = 10

​			n0 + n1 + n2 - 1 = n1 +2n2  //10 -1 = 1 + 2 *4

​			所以n0 = n2 + 1

性质4:具有n个结点的完全二叉树的深度为[以2为底n的对数]+1。

性质5:如果对一棵有n个结点的完全二叉树(深度为[以2为底n的对数]+1)的结点按层序编号,每层从左到右,对任一结点i(1<= i <=n) 有:

  1. 如果i=1,则结点i是二叉树的根,无双亲;如果i > 1,则其双亲是结点[i/2]。
  2. 如果2i > n,则结点i无左孩子(结点i为叶子结点);否则其左孩子是2i。
  3. 如果2i+1>n,则结点i无右孩子;否则其右孩子是结点2i+1。

4. 二叉树的存储结构

4.1 顺序存储结构

二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置,即数组下标也可以体现结点之间的逻辑关系。

例如:完全二叉树的储存

在这里插入图片描述

在这里插入图片描述

一般二叉树:

在这里插入图片描述

构造空二叉树:
// 构造空二叉树T。T是固定数组   TElemType Nil=0;
Status InitBiTree(SqBiTree T)
{
	int i;
	for(i=0;i<MAX_TREE_SIZE;i++)
		T[i]=Nil;   // 初值为空
	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; 
		i++;
	}

	return OK;
}
判断二叉树是否为空:
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;
}
返回二叉树的根:
Status Root(SqBiTree T,TElemType &e)
{ 
	if(BiTreeEmpty(T)) /* T空 */
		return ERROR;
	else
	{	
		e=T[0];
		return OK;
	}
}
返回处于位置e(层,本层序号)的结点的值:
typedef struct
{
	int level,order; //结点的层,本层序号(按满二叉树计算)
}Position;

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;
}
返回双亲:
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) 
			return T[(i+1)/2-1];
	return Nil; 
}
返回左孩子:
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) 
			return T[i*2+1];
	return Nil;
}
返回右孩子:
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)
			return T[i*2+2];
	return Nil;
}
返回左兄弟:
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; 
}
返回右兄弟:
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; 
}
前序遍历:
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;
}
后序遍历:
oid 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]);
}

//后序遍历
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");
	}
}

4.2 链式存储结构

在这里插入图片描述

// 二叉链表
struct BinaryTreeNode
{
	struct BinTreeNode* _pLeft; // 指向当前节点左孩子
	struct BinTreeNode* _pRight; // 指向当前节点右孩子
	TElemType data; // 当前节点值域
}
// 三叉链表
struct BinaryTreeNode
{
	struct BinTreeNode* _pParent; // 指向当前节点的双亲
	struct BinTreeNode* _pLeft; // 指向当前节点左孩子
	struct BinTreeNode* _pRight; // 指向当前节点右孩子
	TElemType data; // 当前节点值域
}
二叉链表相关代码:

在这里插入图片描述

typedef struct BiTNode  
{
   TElemType data;		
   struct BiTNode *lchild,*rchild; 
}BiTNode,*BiTree;
构造二叉链表:
int createBiTree(BiTree &T)  
{  
        Elemtype ch;  
        Elemtype temp;  
        scanf("%d",&ch);  
        temp=getchar();  
        if(ch==-1)  
        {  
            T=NULL;  
        }  
        else  
        {  
                T=(BiTree)malloc(sizeof(BiTnode) );  
                if(!T)  
                {  
                    exit(-1);  
                }  
                else  
                {  
                        T->data=ch;  
                        printf("请输入%d的左节点的值:",ch);  
                        createBiTree(T->Lchild);  
                        printf("请输入%d的右节点的值:",ch);  
                        createBiTree(T->Rchild);  
                }  
  
        }  
        return 1;  
  
}  
前序遍历:
void Traverse(BiTree &T)
{   
	if(T){
		printf("%d ",T->data);  
        Traverse(T->Lchild);  
        Traverse(T->Rchild);
	} 
} 
中序遍历:
void midTraverse(BiTree &T)
{   
	if(T){  
        midTraverse(T->Lchild); 
        printf("%d ",T->data);
        midTraverse(T->Rchild);
	} 
}
后序遍历:
void lasTraverse(BiTree T)  
{  
        if(T){  
        	lasTraverse(T->Lchild);  
        	lasTraverse(T->Rchild);  
        	printf("%d ",T->data); 
        }
}  
求二叉树的深度:
int TreeDeep(BiTree T)  
{  
        int deep=0;  
        if(T)  
        {  
                int leftdeep=TreeDeep(T->Lchild);  
                int rightdeep=TreeDeep(T->Rchild);  
                deep=leftdeep>=rightdeep?leftdeep+1:rightdeep+1;  
        }  
        return deep;  
  
} 
求二叉树叶子节点个数:
int Leafcount(BiTree T,int &num)  
{  
        if(T)  
        {  
                if(T->Lchild==NULL&&T->Rchild==NULL)  
                {  
                        num++;  
                }  
                Leafcount(T->Lchild,num);  
                Leafcount(T->Rchild,num);  
        }  
        return num;  
} 

在这里插入图片描述
在这里插入图片描述

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿小张的日常笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值