入门小白学数据结构与算法(4)——树

树——“一对多”的数据关系

1.概念

1.结点的度:结点拥有的子树数。度为0的结点称为叶结点或终端结点,不为0的称为分支节点,除根节点外,分支节点也称为内部结点。

2.树的度:树内各节点的度的最大值。

注意:
1.树的关系是一对多,之前研究的线性表是一对一,不要混淆
2.当树的结点数n>0时,有且只有一个根节点
3.子数个数m>0时,子树的数目没有限制,但每个子树之间必须是不相交,若有相交,则不符合树的定义

2.相关代码操作

树的结构代码——双亲表示法

#define MAX_TREE_SIZE 100
typedef int Elemtype
typedef struct PTNode
{
	Elemtype data;//结点数据
	int parent;//双亲位置
}PTNode;
typedef struct PTree
{
	PTNode nodes[MAX_TREE_SIZE];//结构体数组
	int r;//根的位置
	int n;//结点数目
}PTree;

树的结构代码——双亲-孩子表示法

#define MAX_TREE_SIZE 100
typedef struct CTNode//孩子结点
{
	int child;//孩子结点的下标
	struct CTNode* next;//指向下一个孩子结点的指针
} *ChildPtr;
typedef struct//表头结构
{
	ElemType data;//存放在树中的结点的数据
	int parent;//结点双亲的下标
	ChildPtr firstchlid;//指向第一个孩子的指针
}CTBox;
typedef struct//定义树结构
{
	CTBox nodes[MAX_TREE_SIZE];//结点数组
	int r,n;//根的位置和结点数 
}CTree;

树的结构代码——孩子-兄弟表示法(所有的树都可以通过该方法变为二叉树结构)

typedef struct CSNode
{
	Elemtype data;
	struct CSNode *firstchild, *rightsib;
}CSNode; *CSTree;

3.二叉树——每个结点的度必须小于等于2

3.1五种基本形态:

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

3.2特殊二叉树

1.斜树:所有的结点都只有左子树(左斜树)或只有右子树(右斜树)
**2.满二叉树:所有的结点都存在左子树和右子树,并且所有叶子都在同一层上,且都在最后一层 **
3.完全二叉树:先按层对树A每个结点进行编号,然后在另一个有相同深度的满二叉树B上也进行按层编号,如果同一个结点在两棵树上的编号相等,那么这颗树A就是完全二叉树。所以满二叉树一定是完全二叉树,但是完全二叉树不一定是满二叉树

3.3二叉树的存储结构——链式存储(二叉链表)

结构代码

typedef struct BiTNode
{
	ElemType data;
	struct BiTNode *lchild, *rchild;//定义左右子树
}BiTNode, *BiTree;

3.4 遍历二叉树——从根节点出发,按照次序依次访问二叉树中所有的结点,使得每个结点都被访问依次且仅被访问一次**

遍历方法(如果限制了从左到右的习惯方式):
1.前序遍历:若二叉树为空,则返回空操作,否则先访问根节点,然后前序遍历左子树,再前序遍历右子树。遍历的优先级为:根节点>左子树>右子树。
2.中序遍历:若二叉树为空,则返回空操作,否则从根节点开始(不是先访问根节点),中序遍历根节点的左子树,然后访问根节点,最后中序遍历右子树。遍历优先级:左子树>根节点>右子树。
3.后序遍历:若二叉树为空,则返回空操作,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根节点。遍历优先级:左子树>右子树>根节点
4.层序遍历:若二叉树为空,则返回空操作,否则从树的第一层开始,也就是根节点开始访问,从上而下逐层遍历,同一层中,按从左到右的顺序对结点逐个访问。

3.4.1 遍历算法——递归的思想

创建二叉树

void CreateTree(BiTree *T)
{
	char c;
	printf("input the char:\n");
	scanf("%c",&c);
	if(c == ' ')
		(*T) = NULL;
	else
	{
		*T = (BiTNode*)malloc(sizeof(BiTNode));//分配内存,将malloc()返回的指针强制转换为指向BiTNode型
		(*T)->data=c;
		CreateTree(&((*T)->lchild));//递归方法
		CreateTree(&((*T)->rchild));
	}
}

遍历二叉树——前序遍历,遍历的优先级为:根节点>左子树>右子树。

void PreOrderTraverse(BiTree T)
{
	if(T == NULL)
		return
	else
	{
		printf("%c",T->data);
		PreOrderTraverse(T->lchild);
		PreOrderTraverse(T->rchild);
	}
}

遍历二叉树——中序遍历,遍历优先级:左子树>根节点>右子树。

void PreOrderTraverse(BiTree T)
{
	if(T == NULL)
		return
	else
	{
		PreOrderTraverse(T->lchild);
		printf("%c",T->data);
		PreOrderTraverse(T->rchild);
	}
}

遍历二叉树——后序遍历,遍历优先级:左子树>右子树>根节点。

void PreOrderTraverse(BiTree T)
{
	if(T == NULL)
		return
	else
	{
		PreOrderTraverse(T->lchild);
		PreOrderTraverse(T->rchild);
		printf("%c",T->data);
	}
}

3.5 线索二叉树——每个结点中带有指向前驱和后继的指针

如下图所示,Ltag控制lchild的指向,若为0,指向左孩子,若为1,指向前驱,rtag同理
在这里插入图片描述
3.5.1 线索二叉树结构代码

typedef enum {Link, Thread} PointerNag;//定义枚举
typedef struct BiTNode
{
	Elemtype data;
	struct BiTNode *lchild, *rchild;
	/*
	定义两个枚举变量,若枚举值等于Link,则代表指向孩子,若枚举值等于Thread则代表指向前驱或者后继
	*/
	PointerNag ltag;
	PointerNag rtag;
}BiThrNode, *BiThrTree;

创建二叉树

void CreateThrTree(BiThrTree* T)
{
	char c;
	scanf_s("%c",&c,1);
	if (c == ' ')
		(*T) = NULL;
	else
	{
		(*T) = (BiThrTree)malloc(sizeof(BiThrNode));
		(*T)->data = c;
		(*T)->ltag = Link;//初始化将ltag和rtag都设为Link,即指向左右孩子
		(*T)->rtag = Link;
		CreateTree(&((*T)->lchild));
		CreateTree(&((*T)->rchild));
	}
}

二叉树遍历——将二叉树线索化,改变指针指向位置

void InThreading(BiThrTree* T)
{
	if (!(*T))
		return;
	else
	{
		InThreading(&((*T)->lchild));
		if ((*T)->lchild == NULL)
		{
			(*T)->ltag = Thread;
			(*T)->lchild = pre;
		}
		if (pre->rchild == NULL)
		{
			pre->rtag = Thread;
			pre->rchild = (*T);
		}
		printf("%c", (*T)->data);
		pre = *T;
		InThreading(&((*T)->rchild));
	}
}

由于线索二叉树的运行过程需要设置一个前驱,因此在程序初始设置一个全局变量,初始化该全局变量时,指向全树的根节点

BiThrTree pre;
void InorderThreading(BiThrTree* p, BiThrTree* T)
{
	*p = (BiThrNode*)malloc(sizeof(BiThrTree));//创建一个头结点
	(*p)->ltag = Link;
	(*p)->rtag = Thread;
	if (!T)
		(*p)->rchild = *p;
	else
	{
		(*p)->lchild = *T;//头节点指向整棵树的根节点
		pre = *p;//将该头结点赋值给pre
		InThreading(T);//遍历头结点
		pre->rchild = (*p);
		pre->rtag = Thread;
		(*p)->rchild = pre;
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值