数据结构--树

一、树的定义

1.定义:n个系欸但那的有限集。N = 0时成为空树
非空树中:
(1)有且只有一个根节点
(2)n>1时其余系欸但可以分为m个互不相交的子树

2.节点分类
2.1节点的度:节点拥有子树的个数,度为0的节点成为叶子节点或者终端节点,不为0的节点称为分支节点,树的度时树内各节点度的最大值。
2.2节点之间的关系:
节点的子树称为该节点的孩子,称该节点为孩子的双亲,同一个双亲的孩子之间称为兄弟。

简单的一个树
2.3节点的层次
节点的层次:从根开始定义起,根为第一层,根的孩子为第二层
双亲在同一层的节点称为堂兄弟。
有序树:各子树从左至右是有次序的,不能呼唤的,称该树有序。
森林:互不相交的树的集合。
在这里插入图片描述

二、树的存储结构

1.双亲表示法:
利用顺序数组进行树中元素的存储,利用这样一个结构体来实现节点的关系

#define MAX_TREE_SIZE 100
typedef int TElemType;
typedef struct PTNode//树节点的结构
{
	TElemType data;
	int parent;
}PTNode;
typdef struct //树的结构
{
	PTNode nodes[MAX_TREE_SIZE];
	Int r,n;//根的位置和节点数
}Ptree;

在这里插入图片描述
在这里插入图片描述
很容易找到双亲节点,但是要知道结点的孩子是什么需要遍历整个结构。
其他的类型:
添加一个右边兄弟的数据情况。
在这里插入图片描述
2.孩子表示法:
给所有的节点分配孩子变量,这样可能会造成空间的浪费。
在这里插入图片描述
//代码,双亲孩子表示法

结构作用
CTNode孩子节点,包含孩子节点的下标,指向下一个孩子的指针
CTbox表头节点,包含表头节点的数据,双亲坐标,表头指针
Tree包含表头数组,树的节点数和根的位置
#define MAX_TREE_SIZE 100
//孩子节点
typedef struct CTNode
{
	int child;//孩子节点的下标
	struct CTNode *next;//指向下一个孩子节点的指针
}*ChildPtr;

//表头节点
typedef struct 
{
	TElemType data;    //存放在树中节点的数据
	int parent;           //存放双亲的位置下标
	ChildPtr firstchild;//存放第一个孩子的指针
}CTbox;

//树结构
typedef struct
{
	Ctbox nodes[MAX_TREE_SIZE 100];
	int r,n;//根的位置和节点数
}Tree;

三、二叉树

1.二叉树的定义:n个节点的有限集合,一个根节点,子树只能是两个。
特点:
二叉树度不大于2,可以没有。左右子树存在顺序,不能颠倒。即使只有一棵子树,也要区分是左右。

五种形态:
在这里插入图片描述
2.特殊二叉树
2.1斜树
2.2满二叉树,所有的非叶子节点的度都是2,所有的叶子节点都在同一层上
2.3完全按二叉树,n个节点按层序编号,位置正确,顺序连贯,并且和满二叉树顺序相同。

3.二叉树性质
3.1第i层上至多有2^(i-1)个节点
3.2深度为k的二叉树至多有2^i - 1个节点
3.3二叉树终端节点n0,度为2的节点n2,n0 = n2+1
3.4具有n个节点的二叉树的深度为log2(n) + 1
3.5i的子节点为2i和2i+1

4.二叉树的存储结构
4.1顺序二叉树
在这里插入图片描述
在这里插入图片描述
4.2二叉链表

	typedef struct BiTNode
	{
		ElemType data;
		struct BiTNode *lchild,*rchild;
	}BiTNode,*BiTree;

四、遍历二叉树

遍历二叉的过程使用了递归的过程,因为二叉树的建立过程中,节点之间的关系不存在前驱后继的关系,在访问一个节点之后都会产生新的选择。
1.先序遍历:若二叉树为空,则空操作返回,否则先访问根节点,接着前序遍历左子树,接着前序遍历右子树。
在这里插入图片描述
2.中序遍历:若树为空,则空操作返回,否则从根节点开始,中序遍历根节点的左子树,然后访问根节点,然后中序遍历右子树
在这里插入图片描述
3.后序遍历:若树为空,则空操作返回,否则从左至有先叶子后节点的方式访问左右子树,最后是访问根节点。
在这里插入图片描述
4.层序遍历:若树为空,则空操作返回,否则从上到下,从左至右,一层一层访问。
在这里插入图片描述

五、二叉树的建立和遍历算法

中序建立:创建根节点,创建左节点,创建右节点,使用递归的方法
遍历的方法也就是递归的过程:
1.先序遍历:若二叉树为空,则空操作返回,否则先访问根节点,接着前序遍历左子树,接着前序遍历右子树。
2.中序遍历:若树为空,则空操作返回,否则从根节点开始,中序遍历根节点的左子树,然后访问根节点,然后中序遍历右子树
3.后序遍历:若树为空,则空操作返回,否则从左至有先叶子后节点的方式访问左右子树,最后是访问根节点。

#include <iostream>
#include <malloc.h>

using namespace std;

typedef struct BiTNode
{
	char data;
	BiTNode *lchild,*rchild;
}BiTNode,*BiTree;

void initBiTree(BiTree &T)
{
	T = NULL;
}
void DestoryBiTree(BiTree &T)
{
	if(T)
	{
		if(T->lchild)
			DestoryBiTree(T->lchild);
		if(T->rchild)
			DestoryBiTree(T->rchild);
		T = NULL;
	}
}
void Visit(char c,int level)
{
	cout << c <<"在层"<<level<<endl;
}
void InOrderTraverse(BiTree T,int level)
{ 
	if(T)
	{
		InOrderTraverse(T->lchild,level+1); 
		Visit(T->data,level); 
		InOrderTraverse(T->rchild,level+1); 
	}
}
void CreateBiTree(BiTree &T)
{ 
	char ch;
	ch = getchar();
	if(ch==' ') // 空
		T=NULL;
	else
	{
		T=(BiTree)malloc(sizeof(BiTNode)); // 生成根结点
		if(!T)
		exit(0);
		T->data=ch;
		CreateBiTree(T->lchild); // 构造左子树
		CreateBiTree(T->rchild); // 构造右子树
	}
}
void PreOrderTraverse(BiTree T,int level)
{ 
	if(T) // T不空
	{
		Visit(T->data,level); 
		PreOrderTraverse(T->lchild,level+1); 
		PreOrderTraverse(T->rchild,level+1); 
	}
}
void back(BiTree T,int level)
{
	if(T) 
	{
		back(T->lchild,level+1); 
		back(T->rchild,level+1); 
		Visit(T->data,level); 
	}
}

int main(int argc, char** argv) {
	BiTree T;


	initBiTree(T);
	CreateBiTree(T);
	int level = 1;
	cout <<"中序遍历" <<endl;
	InOrderTraverse(T,level);
	cout <<"前序遍历" <<endl;
	level = 1;
	PreOrderTraverse(T,level);
	cout <<"后序遍历" <<endl;
	level = 1;
	back(T,level);
	DestoryBiTree(T);
	return 0;
}

六、线索二叉树

节省空节点的问题,二叉树的结构中包含了左右孩子,如果没有左右孩子,则会浪费了这个空间。
在中序遍历时,可以采用一种结构,设置了前驱和后继,将二叉树联系起来

dataLTaglchildrchildRTag

LTag 和 RTag 为 0 时指向的是孩子,为 1 时指向的是前后继。
设置一个头节点,头节点的左孩子指向根节点,头节点的右孩子指向遍历的最后一个节点;第一个访问的节点的左孩子(前驱)指向头节点;最后一个节点的右孩子(后继)指向这个头节点。在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>

typedef char ElemType;

typedef enum {Link,Thread} PointerTag;//Link = 0,Thread = 1
//LTag 和 RTag 为 0 时指向的是孩子,为 1 时指向的是前后继。
typedef struct BiTrNode
{
	ElemType data;
	struct BiTrNode *lchild,*rchild;
	PointerTag LTag;
	PointerTag RTag;
}BiThrNode,*BiThrTree;

//创建一棵二叉树,约定用户遵照前序遍历的方式输入数据
void CreateBiThrTree(BiThrTree &T)
{
	char c;
	scanf("%c",&c);
	if(' ' == c)
		T = NULL;
	else
	{
		T = (BiThrTree)malloc(sizeof(BiThrNode));
		T->data = c;
		T->LTag = Link;//默认都为孩子,之后我们通过一个函数进行修改
		T->RTag = Link;

		CreateBiThrTree(T->lchild);
		CreateBiThrTree(T->rchild);
	}
}

//全局变量,始终指向刚刚访问过的节点
BiThrTree pre;

void InThreading(BiThrTree &T)
{
	//中序遍历线索化
	if(T)
	{
		InThreading(T->lchild);//递归左孩子线索化
		//节点处理
		if (!T ->lchild)//如果该节点没有左孩子,设置LTag为Thread并且把lchild指向上一个访问的节点
		{
			T->LTag = Thread;
			T->lchild = pre;
		}
		if(!pre->rchild)
		{
			pre->RTag = Thread;
			pre->rchild = T;
		}
		pre = T;
		InThreading(T->rchild);//递归右孩子线索化
	}
}

void InOrderThreading(BiThrTree &p,BiThrTree &T)
{
	p = (BiThrTree)malloc(sizeof(BiThrNode));
	p->LTag = Link;
	p->RTag = Thread;
	p->rchild = p;
	if(!T)
		p->lchild = p;
	else
	{
		p->lchild = T;
		pre = p;
		InThreading(T);
		pre -> rchild = p;
		pre -> RTag = Thread;
		p->rchild = pre;
	}
}

void visit(char c)
{
	printf("%c ",c);
}

//中序遍历二叉树非递归
void InOrderTraverse(BiThrTree T)
{
	BiThrTree p;
	p = T->lchild;
	while(p!=T)
	{
		while(p->LTag == Link)
		{
			p = p->lchild;
		}
		visit(p->data);
		while (p->RTag == Thread&&p->rchild!=T)
		{
			p = p->rchild;
			visit(p->data);
		}
		p = p->rchild;
	}
}

int main()
{
	BiThrTree p,T = NULL;
	CreateBiThrTree(T);
	InOrderThreading(p,T);

	printf("中序遍历输出结果为:");
	InOrderTraverse(p);//p是头指针
	return 0;
}

七、树和森林的转换

普通树转化为二叉树:
1.兄弟之间加连线
2.去除长子之外的连线、调整
在这里插入图片描述
森林转为二叉树:
1.每棵树变为二叉树
2.将根依次连线
在这里插入图片描述
二叉树到树到森林的转换:
1.若节点x是其双亲y的左孩子,则把x的右孩子,右孩子的右孩子,…都与y用连线连起来
2.去除双亲到右孩子之间的连线。
在这里插入图片描述
判断二叉树是否能转换称森林:是否有右孩子,有的话就是森林。
树与森林的遍历:
先根遍历:先访问树的根节点,然后一次遍历子树
后根遍历:先子树,再根

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值