数据结构与算法-树和森林

在这里插入图片描述
🌞 “永远面朝阳光,阴影被甩在身后!”

🎈1.线索二叉树

遍历二叉树是以一定的规则将二叉树中的结点排列成一个线性序列的过程。与线性表相比,二叉树的遍历存在以下问题:
(1).遍历算法复杂而费时
(2).为检索或查找二叉树中某结点在某种遍历下的前驱结点和后继结点,必须从根结点开始遍历,直到找到该结点的前驱结点和后继结点。
为此,我们引入线索二叉树的概念,线索二叉树可以简化遍历算法,提高遍历效率。

为了避免混淆,重新定义结点的结构,在结点的存储结构上增加标志位ltagrtag来区分这两种情况。
在这里插入图片描述

其中,ltag0时,lchild域指向结点的左孩子,ltag1时,lchild指向结点的前驱;rtag0时,rchild域指向结点的右孩子,rtag1时,rchild域指向结点的后继。
在这里插入图片描述

🎈2.树和森林

🔭2.1树的存储结构

🔎树的存储结构常分为:

  1. 双亲表示法
  2. 孩子链表表示法
  3. 孩子兄弟表示法

🔭2.2双亲表示法

由树的定义知,除根结点之外,树中的每个结点都有唯一的双亲。根据这一特点,双亲表示法可以用一组连续的存储空间存放结点信息,即用一维数组来存储树中的各个结点。数据元素为结构体类型,其中包括结点本身信息以及指示其双亲结点在数组中的位置信息。

🔎双亲表示法在类型定义如下:

#define MaxSize 100
typedef int ElemType;
typedef struct PtNode
{
	ElemType data;
	int parent;
}PtNode;
typedef struct
{
	PtNode nodes[MaxSize];
	int r, n;
}PTree;

在这里插入图片描述

🔭2.3孩子链表表示法

孩子链表表示法存储单元的主体是一个与结点个数一样大小的一维数组,数组的每一个元素由两个域组成,一个域用来存放结点自身的数据信息,另一个用来存放指针,该指针指向由该结点孩子组成的单链表的表头。单链表的结点结构也有两个域组成,一个存放孩子结点在一维数组中的下标,另一个是指针域,指向下一个孩子。
在这里插入图片描述

📝2.3.1孩子链表表示法的实现

#define MaxSize 100
typedef struct CtNode
{
	int child;
	CtNode* next;//指向下一个孩子结点
}CtNode;
typedef struct
{
	char data;
	int parent;
	CtNode* firstchild;//指向第一个孩子
	int r, n;
}CtBox;
class CTree
{
private:
	CtBox nodes[MaxSize];
	int r, n;
public:
	int LocateNode(char x);//查找结点x在树中的下标
	void CreateCtree(int k);//创建k个结点的树
	int DegreeNode(char x);//计算结点x的度数
	void InsertNode(char u, char v);//插入结点u的孩子v
};

📝2.3.2查找结点x在树中的下标

在这里插入图片描述

int CTree::LocateNode(char x)
{
	for (int i = 0; i < n; i++)
	{
		if (x == nodes[i].data)
			return i;
		else
			return -1;
	}
}

📝2.3.3创建k个结点的树

void CTree::CreateCtree(int k)
{
	int i;
	for (i = 0; i < k; i++)
	{
		cin >> nodes[i].data;
		nodes[i].firstchild = NULL;
	}
	n = k;
	r = 0;
	char u, v;
	int h, t;
	CtNode* p;
	for (i = 0; i < k; i++)
	{
		cin >> u;
		h = LocateNode(u);
		cin >> v;
		while (v != '.')
		{
			t = LocateNode(v);
			p = new CtNode;
			p->child = t;
			p->next = nodes[h].firstchild;
			nodes[h].firstchild = p;
			cin >> v;
		}
	}
}

📝2.3.4计算结点x的度数

int CTree::DegreeNode(char x)
{
	int h = LocateNode(x);
	CtNode* p = nodes[h].firstchild;
	int count = 0;
	while (p)
	{
		count++;
		p = p->next;
	}
	return count;
}

📝2.3.5插入结点u的孩子v

void CTree::InsertNode(char u, char v)
{
	int h = LocateNode(u);
	if (h == -1)
		return;
	nodes[n].data = v;
	nodes[n].firstchild = NULL;
	n++;
	CtNode* p = new CtNode;
	int t = LocateNode(v);
	p->child = t;
	p->next = nodes[h].firstchild;
	nodes[h].firstchild = p;
}

🔭2.4孩子兄弟表示法

孩子兄弟表示法又称二叉链表表示法或二叉树表示法。即以二叉链表作为树的存储结构,链表中结点的两个链域分别指向结点的第一个孩子结点和下一个兄弟结点。
在这里插入图片描述

typedef char ElemType;
typedef struct CSNode
{
	ElemType data;
	CSNode* firstchild;
	CSNode* nextsibling;
}CSNode;

📖2.4.1例题

已知一棵树以孩子兄弟表示法为其存储结构,设计一个递归算法计算树的高度。

int CSTreeHeight(CSNode* t)
{
	int m, max = 0;
	CSNode* p;
	if (t == NULL)
		return 0;
	else
	{
		p = t->firstchild;
		while (p)
		{
			m = CSTreeHeight(p);
			if (max < m)
				max = m;
			p = p->nextsibling;
		}
		return 1 + max;
	}
}

🎈3.二叉树的小练笔

题目:递归创建一棵二叉树,中序输出二叉树序列,计算树的深度,计算度数为1的结点数,计算度数为1的结点数以及判断两棵树是否相似。

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
typedef struct BitNode
{
	char data;
	BitNode* lchild;
	BitNode* rchild;
}BitNode;
class BiTree
{
private:
	BitNode* bt;
	void Rcreate(BitNode*& t);//递归创建一棵二叉树
	void InTraverse(BitNode* t);//中序输出二叉树序列
	int BTNodeDepth(BitNode* t);//计算树的深度
	int CountNode1(BitNode* t);//计算度数为1的结点数
	int CountNode2(BitNode* t);//计算度数为1的结点数
public:
	BiTree()
	{
		bt = NULL;
	}
	void RecreateBiTree();
	void InTraverseBiTree();
	int BTNodeDepthBiTree();
	int CountNode1BiTree();
	int CountNode2BiTree();
	int Alike(BitNode* s, BitNode* t);//判断两棵树是否相似
	BitNode*& GetRoot()
	{
		return bt;
	}
};
void BiTree::Rcreate(BitNode*& t)
{
	char ch;
	cin >> ch;
	if (ch == '*')
	{
		t = NULL;
	}
	else
	{
		t = new BitNode;//申请空间
		t->data = ch;
		Rcreate(t->lchild);//递归创建左子树
		Rcreate(t->rchild);//递归创建右子树
	}
}
void BiTree::RecreateBiTree()
{
	BitNode* t;
	Rcreate(t);
	bt = t;
}
void BiTree::InTraverse(BitNode* t)
{
	if (t)
	{
		InTraverse(t->lchild);
		cout << t->data << " ";
		InTraverse(t->rchild);
	}
}
void BiTree::InTraverseBiTree()
{
	BitNode* p = bt;
	InTraverse(p);
}
int BiTree::BTNodeDepth(BitNode* t)
{
	if (t == NULL)
	{
		return 0;
	}
	else
	{
		int m = 1 + BTNodeDepth(t->lchild);
		int n = 1 + BTNodeDepth(t->rchild);
		if (m >= n)
			return m;
		else
			return n;
	}
}
int BiTree::BTNodeDepthBiTree()
{
	BitNode* p = bt;
	cout<< BTNodeDepth(p);
	return 1;
}
int BiTree::CountNode1(BitNode* t)
{
	if (t == NULL)
		return 0;
	if (t->lchild == NULL && t->rchild != NULL)
		return 1 + CountNode1(t->rchild);
	if (t->rchild == NULL && t->lchild != NULL)
		return 1 + CountNode1(t->lchild);
	return CountNode1(t->lchild) + CountNode1(t->rchild);
}
int BiTree::CountNode1BiTree()
{
	BitNode* p = bt;
	cout << CountNode1(p);
	return 1;
}
int BiTree::CountNode2(BitNode* t)
{
	if (t == NULL)
		return 0;
	if (t->lchild != NULL && t->rchild != NULL)
		return 1 + CountNode2(t->lchild) + CountNode2(t->rchild);
	return CountNode2(t->lchild) + CountNode2(t->rchild);
}
int BiTree::CountNode2BiTree()
{
	BitNode* p = bt;
	cout << CountNode2(p);
	return 1;
}
int BiTree::Alike(BitNode* s, BitNode* t)
{
	if (s == NULL && t == NULL)
	{
		return 1;
	}
	else if (s == NULL || t == NULL)
	{
		return 0;
	}
	else
		return Alike(s->lchild, t->lchild) && Alike(s->rchild, t->rchild);
}
int main()
{
	BiTree t,t1,t2;
	cout << "递归创建t: ";
	t.RecreateBiTree();
	cout << "中序遍历的序列为:";
	t.InTraverseBiTree();
	cout << endl;
	cout << "树的深度为:";
	t.BTNodeDepthBiTree();
	cout << endl;
	cout << "度为1的结点数为:";
	t.CountNode1BiTree();
	cout << endl;
	cout << "度为2的结点数为:";
	t.CountNode2BiTree();
	cout << endl;
	cout << "递归创建t1: ";
	t1.RecreateBiTree();
	cout << "递归创建t2: ";
	t2.RecreateBiTree();
	if (t.Alike(t1.GetRoot(), t2.GetRoot()))
		cout << "t1和t2相似" << endl;
	else
		cout << "t1和t2不相似" << endl;
	return 0;
}

✅运行示例:
在这里插入图片描述

评论 66
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一口⁵个团子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值