数据结构-树

树 2021/8/8 20:00

除了根节点外,任何结点有且只有一个前驱

树的常考性质

性质1:结点数=总度数+1 (除了根结点外任何一个结点头上都有一根天线)
性质2:度为m的树:树中各结点的度的最大值
m叉树:每个结点最多只能有m个孩子的树
注意两者区别!!!!
性质3:高度为h的m叉树至多有(m^h-1)/(m-1)个结点
性质4:高度为h的m叉树至少有h个结点,高度为h、度为m的树至少有h+m-1个结点
性质5:在这里插入图片描述

二叉树

满二叉树

在这里插入图片描述

完全二叉树

在这里插入图片描述
如果完全二叉树某结点只有一个孩子,那么一定是左孩子

二叉排序树

在这里插入图片描述

平衡二叉树

在这里插入图片描述

二叉树的常考性质

性质1:设非空二叉树中度为0、1和2的结点个数分别为n0、n1和n2,则 n0 = n2 + 1
(叶子结点比二分支结点多一个)
假设树中结点总数为 n,则
① n = n0 + n1 + n2 ② n = n1 + 2*n2 +1
得:n0=n2+1
性质2:m叉树第i层最多有m^(i-1)个结点
二叉树第i层最多有m^(i-1)个结点
性质3:
在这里插入图片描述

完全二叉树的常考性质

性质1:在这里插入图片描述
性质2:对于完全二叉树,可以由的结点数 n 推出度为0、1和2的结点个数为n0、n1和n2
完全二叉树最多只有一个度为1的结点,即n1=0或1
又n0 = n2 + 1则 n0 + n2 一定是奇数
可推出
若完全二叉树有2k个(偶数)个结点,则
必有 n1=1, n0 = k, n2 = k-1
若完全二叉树有2k-1个(奇数)个结点,则
必有 n1=0, n0 = k, n2 = k-1

二叉树的顺序储存

#include <iostream>
using namespace std;
#define MaxSize 100
typedef int ElemType;

 struct TreeNode{
	ElemType value;
	bool IsEmpty;
};
 
 //定义一个数组,在数组中从上到下,从左到右储存完全二叉树,根据下标的相对位置确定结点的孩子和父母,所在层
 //若不是完全二叉树,则按照树的结构储存到数组相应的位置,这种储存方法造成了内存的大量浪费

int main()
{
	TreeNode tree[MaxSize];
}

二叉树的链式储存

#include <iostream>
using namespace std;

#define MaxSize 100
typedef int ElemType;

typedef struct BiTNode {
	ElemType data;//数据域
	struct BiTNode* lchild, * rchild;//左孩子,右孩子
}BiTNode,*BiTree;
//n个结点的二叉链表共有n+1个空链域
/*
* 访问结点,打印结点的值
*/
void Visit(BiTNode *T)
{
	cout << T->data<< " ";
}
/*
* 先序遍历
*/
void PreOrder(BiTree T)
{
	if (T != NULL)
	{
		Visit(T);
		PreOrder(T->lchild);
		PreOrder(T->rchild);
	}
}
/*
* 中序遍历
*/
void InOrder(BiTree T)
{
	if (T != NULL)
	{
		
		InOrder(T->lchild);
		Visit(T);
		InOrder(T->rchild);
	}
}
/*
* 后序遍历
*/
void PostOrder(BiTree T)
{
	if (T != NULL)
	{

		PostOrder(T->lchild);
		PostOrder(T->rchild);
		Visit(T);
	}
}
/*
* 求树的深度
*/
int treeDepth(BiTree T)
{
	if (T == NULL)
		return 0;
	int l = treeDepth(T->lchild);
	int r = treeDepth(T->rchild);
	return l > r ? l + 1 : r + 1;
}
int main()
{
	BiTree T;
	BiTNode* A, * B, * C, * D,*E,*F;
	T = (BiTree)malloc(sizeof(BiTNode));
	A = (BiTree)malloc(sizeof(BiTNode));
	B = (BiTree)malloc(sizeof(BiTNode));
	C = (BiTree)malloc(sizeof(BiTNode));
	D = (BiTree)malloc(sizeof(BiTNode));
	E= (BiTree)malloc(sizeof(BiTNode));
	F = (BiTree)malloc(sizeof(BiTNode));
	T->data = 1;
	A->data = 2;
	B->data = 3;
	C->data = 4;
	D->data = 5;
	E->data = 6;
	F->data = 7;
	T->lchild = A;
	T->rchild = B;
	A->lchild = C;
	A->rchild = D;
	B->lchild = E;
	B->rchild = F;
	C->lchild = NULL;
	C->rchild = NULL;
	D->lchild = NULL;
	D->rchild = NULL;
	E->lchild = NULL;
	E->rchild = NULL;
	F->lchild = NULL;
	F->rchild = NULL;
	cout << "先序遍历"; PreOrder(T); cout<< endl;
	cout << "中序遍历"; InOrder(T); cout << endl;
	cout << "后序遍历"; PostOrder(T); cout << endl;
	cout << treeDepth(T) << endl;
	return 0;
}

//层序遍历:利用队列

//由先序、中序、后序遍历序列确定二叉树
//需要前+中 或 后+中 或 层+中
//单独给出前、后、层中的两个不能唯一确定二叉树
//
三叉链表
#include <iostream>
using namespace std;

#define MaxSize 100
typedef int ElemType;

typedef struct BiTNode {
	ElemType data;//数据域
	struct BiTNode* lchild, * rchild;//左孩子,右孩子
	struct BiTNode* parent;//父结点
}BiTNode, * BiTree;
//n个结点的二叉链表共有n+1个空链域
//在算术表达式中的先中后序遍历对应着前缀中缀(需要加界限符)后缀表达式

线索二叉树

#include <iostream>
using namespace std;

#define MaxSize 100
typedef int ElemType;

typedef struct ThreadNode 
{
	ElemType data;//数据域
	struct ThreadNode* lchild, * rchild;//左孩子,右孩子
	int ltag, rtag;//左右线索标志
}ThreadNode, * ThreadTree;

/*
* 二叉树的中序线索化
*/
void CreateInThread(ThreadTree &T)
{
	ThreadTree pre = NULL;
	if (T != NULL)
	{
		InThread(T, pre);
		pre->rchild = NULL;//对最后一个结点进行处理
		pre->rtag = 1;
	}
}
/*
* 中序线索化
*/
void InThread(ThreadTree T, ThreadTree & pre)
{
	if (T != NULL)
	{
		InThread(T->lchild, pre);
		if (pre != NULL && T->lchild == NULL)
		{
			T->lchild = pre;
			T->ltag = 1;
		}
		if (T != NULL && pre->rchild == NULL)
		{
			pre->rchild = T;
			pre->ltag = 1;
		}
		pre = T;
		InThread(T->rchild, pre);
	}
}
/*
* 先序线索化(解决爱的魔力转圈圈问题)
*/
void PreThread(ThreadTree T, ThreadTree& pre)
{
	if (T != NULL)
	{
		
		if (pre != NULL && T->lchild == NULL)
		{
			T->lchild = pre;
			T->ltag = 1;
		}
		if (T != NULL && pre->rchild == NULL)
		{
			pre->rchild = T;
			pre->ltag = 1;
		}
		pre = T;
		if(T->ltag==0)//只有当左孩子没有被线索化时才访问左孩子,解决爱的魔力转圈圈问题
			PreThread(T->lchild, pre);
		PreThread(T->rchild, pre);
	}
}

/*
* 后序线索化(中序和后序不会出现爱的魔力转圈圈问题)
*/
void PostThread(ThreadTree T, ThreadTree& pre)
{
	if (T != NULL)
	{
		InThread(T->lchild, pre);
		InThread(T->rchild, pre);
		if (pre != NULL && T->lchild == NULL)
		{
			T->lchild = pre;
			T->ltag = 1;
		}
		if (T != NULL && pre->rchild == NULL)
		{
			pre->rchild = T;
			pre->ltag = 1;
		}
		pre = T;
	}
}
/*
* 寻找以p为根的子序列中,第一个被中序遍历的结点
*/
ThreadNode* FirstNode(ThreadNode* p)
{
	while (p->ltag == 0) p=p->lchild;
	return p;
}
/*
* 在中序线索二叉树中找到P结点的中序遍历后继
*/
ThreadNode* NextNode(ThreadNode* p)
{
	if (p->rtag == 0) return FirstNode(p->rchild);//寻找右子树的最左下结点
	else return p->rchild;
}
/*
* 访问结点
*/
void Visit(ThreadNode* T)
{
	cout << T->data << " ";
}
/*
* 对整个已经中序线索化的二叉树进行中序遍历
*/
void InOder(ThreadTree T)
{
	for (ThreadNode* p =FirstNode(T); p != NULL; p = NextNode(p))
	{
		Visit(p);
	}
}
/*
* 寻找以p为根的子序列中,最后一个被中序遍历的结点
*/
ThreadNode* LastNode(ThreadNode* p)
{
	while (p->rtag == 0) p = p->rchild;
	return p;
}
/*
* 在中序线索二叉树中找到P结点的中序遍历前驱
*/
ThreadNode* PreNode(ThreadNode* p)
{
	if (p->ltag == 0) return LastNode(p->lchild);//寻找右子树的最左下结点
	else return p->lchild;
}
/*
* 对整个已经中序线索化的二叉树进行逆向中序遍历
*/
void InOder(ThreadTree T)
{
	for (ThreadNode* p = LastNode(T); p != NULL; p = PreNode(p))
	{
		Visit(p);
	}
}
//在先序线索二叉树中找先序后继:右孩子是线索则后继是右孩子指向的结点;否则则根据“根左右”的规律可知,后继是左孩子,没有左孩子则是右孩子
//在先序线索二叉树中找先序前驱:需要先找到该结点的父节点!!一般的二叉树没法办到,三叉链表可以。
//当前结点有左兄弟则前驱是左兄弟,否则是父节点,没有父节点(当前结点为根节点)的话则没有前驱。
//在先序线索二叉树中找最后一个被遍历的结点:从根节点优先往右找,找到没有右结点但有左结点的点时才往左找,否则但凡遇见了有右结点的点就往右找

//在后序线索二叉树中找后序前驱:左孩子为线索则前驱为其所指;否则根据“左右根”的原则,当前结点的前驱为其右孩子,右孩子为空则为左孩子
//在后序线索二叉树中找后序后继:右孩子为线索则后继为其所指;否则一般的二叉树找不到,三叉链表可以。当前结点为其夫右孩子,则后继为其父节点;
//当前节点为其父左孩子,其父无右孩子,则该结点后继为其父结点;
//当前结点为其父左孩子,其父有右孩子,则其后继为右兄弟的左下方元素(一直往左下找,有右孩子无左孩子才往右找,若途中有左孩子则仍往左找)

树的储存结构

1.双亲表示法
#include <iostream>
using namespace std;
typedef int ElemType;
#define MaxSize 100
typedef struct {
	ElemType data;
	int parent;//指向父结点位置,-1表示根节点
}PTNode;
typedef struct {
	PTNode nodes[MaxSize];
	int n;//结点数
};
//双亲表示法找父节点较为简单,但是找孩子只能遍历

2.孩子表示法
#include <iostream>
using namespace std;
#define MaxSize 100
typedef int ElemType;
struct CTNode {
	int child;
	struct CTNode* next;
};
typedef struct {
	ElemType data;
	CTNode* FirstNode;
}CTBox;
typedef struct {
	CTBox nodes[MaxSize];
	int n,r;//n结点数,r根的位置
}CTree;

3.孩子兄弟表示法
#include<iostream>
using namespace std;

typedef int ElemType;
typedef struct CSNode{
	ElemType data;//数据域
	struct CSNode* firstchild, * nextsibling;//第一个孩子,第一个孩子的兄弟
}CSNode,*CSTree;
//在树和二叉树的转化中有应用

二叉排序树

#include<iostream>
using namespace std;

#define MaxSize 100
typedef int ElemType;
typedef struct BSNode{
	ElemType key;
	struct BSNode* lchild, * rchild;
}BSTNode,*BSTree;
/*
* 二叉排序树的搜索,返回值为e的结点
*/
BSTNode* BST_Search(BSTree T, ElemType e)
{
	while (T!=NULL&&T->key != e)
	{
		if (T->key > e)
			T = T->lchild;
		else
			T = T->rchild;
	}
	return T;
}
/*
* 二叉树的插入
*/
int BST_Insert(BSTree& T, ElemType e)
{
	if (T == NULL)//当前树为空则插入的结点为子树根节点
	{
		T = (BSTNode*)malloc(sizeof(BSTNode));
		T->key = e;
		T->lchild = NULL;
		T->rchild = NULL;
		return 1;
	}
	if (T->key == e)//树中已有相同值,插入失败
		return 0;
	if (T->key > e)//当前结点值比待插值大,则待插值插入左子树
	{
		return BST_Insert(T->lchild, e);
	}
	else //当前结点值比待插值小,则待插值插入右子树
		return BST_Insert(T->rchild, e);
	
}
/*
* 按照给定的数组顺序建立二叉排序树
*/
void Create_BST(BSTree& T, ElemType a[], int n)//n为数组元素个数
{
	T = NULL;//初始时树为空
	int i = 0;
	while (i < n)//依次将数组中的元素插入到二叉排序树中
	{
		BST_Insert(T, a[i]);
		i++;
	}
}
//二叉排序树的删除:1.若当前删除的结点只有左子树或只有右子树,则删除该结点后直接用子树接上即可
//2.若当前删除的结点既有左子树又有右子树,则可以用当前结点的直接后继(右子树中的最小值)或者直接前驱(左子树中的最大值)代替,
//代替方法可以看成把代替结点值复制到当前结点,由于代替结点是左子树最大值/右子树最小值,因此必定只有左子树或右子树,
//此时按照1中方法补上即可
//查找长度:对比关键字的次数 平均查找长度ASL
/****************************************************************************************************************************/

平衡二叉树

平衡二叉树:AVL
结点平衡因子:左子树高-右子树高
最小不平衡子树:从插入结点往回找的第一个不平衡结点的子树
LL:在A的左孩子的左子树中插入导致不平衡
在这里插入图片描述

RR:在A的右孩子的右子树中插入导致不平衡
在这里插入图片描述
在这里插入图片描述

LR:在A的左孩子的右子树中插入导致不平衡
在这里插入图片描述

RL:在A的右孩子的左子树中插入导致不平衡
在这里插入图片描述

哈夫曼树

在含有n个带权叶结点的二叉树中,其中带权路径长度(WPL)最小的二叉树称为哈夫曼树,也称最优二叉树
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Handsome Wong

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

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

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

打赏作者

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

抵扣说明:

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

余额充值