IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 数据结构——树的相关操作 -> 正文阅读

[数据结构与算法]数据结构——树的相关操作

1.树的存储结构

(1)双亲表示法

? ?一组连续空间存储树的结点,同时在每个结点中,附设一个指示器指示其双亲结点在数组中的位置。

结点结构:

typedef struct PTNode {
	int data;//数据域,存储结点的数据信息
	int parent;//位置域,存储该结点的双亲在数组中的下标
}PTNode;

? 双亲表示法是使用数组来表示的。

树结构:

typedef struct {
	PTNode node[MaxSize];//结点数组
	int r, n;//r表示根的位置,n表示结点数
}PTree;

由于根结点是没有双亲的,所以我们约定根结点的位置域设置为-1。结点自己的位置用下标表示。

(2)孩子表示法

? ? ? ?把每个结点的孩子排列起来,以单链表作存储结构,则n个结点有n个孩子链表。如果是叶子结点则此单链表为空。然后n个头指针又组成了一个线性表,采取顺序存储结构,存放进一个一维数组中。

孩子链表的孩子结点:

typedef struct CTNode {
	int child;//数据域,存储某个结点在表头数组中的下标
	struct CTNode* next;//指针域,存储指向表头结点的下一个孩子结点的指针
}*ChildPtr;

?表头数组的表头结点:

typedef struct {
	int data;//数据域,存储表头结点的数据信息
	ChildPtr firstchild;//头指针域,存储表头结点的孩子链表的头指针(指向第一个孩子)
}CTBox;

树结构:

typedef struct {
	CTBox nodes[MaxSize];//结点数组
	int r, n;//r表示根的位置,n表示结点数
}CTree;

?(3)孩子兄弟表示法

? 任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此,我们设置两个指针,分别指向该结点的第一个孩子和此孩子的右兄弟

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

? data是数据域,firstchild为指针域,存储该结点的第一个孩子结点的存储地址,rightsib是指针域,存储该结点的右兄弟结点的存储地址。(类似二叉链表)

?2.二叉树

(1)二叉树的链式存储结构

? ?二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域。我们称这样的链表为二叉链表

typedef struct BiTNode {
	int data;
	struct BiTNode* lchild, * rchild;//左右孩子指针
}BiTNode,*BiTree;

?二叉树的顺序存储结构就是声明一个数组,然后把二叉树按照层次遍历的顺序(从上到下,从左到)标上序号,按照序号存储到数组中即可。

?(2)遍历二叉树

? ? 1.前序遍历

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

? 规则是若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。

? ? 2.中序遍历

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

? 规则是若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。、

? ? 3.后序遍历

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

?规则是若树为空,则空操作返回,否则先后序遍历左子树,再后序遍历右子树,最后访问根结点。

? ?4.层序遍历

int LevelOrderTraverse(BiTree& t) {
	queue<BiTNode*> q;//声明一个数据类型为结点指针的队列q
	BiTNode* h;//声明一个结点指针
	if (t != NULL) {
		q.push(t);//把结点指针入队
		while (!q.empty()) {
			h = q.front();//取出首位元素
			q.pop();//首位元素出队
			printf("%c ", h->data);
			if (h->lchild != NULL) {
				q.push(h->lchild);//入队左孩子
			}
			if (h->rchild != NULL) {
				q.push(h->rchild);//入队右孩子
			}
		}
	}
	return 1;
}

? ?规则是若树为空,则空操作返回。否则从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问。

(3)建立二叉树

?这里选择先序序列(前序序列)来建立二叉树

int CreateBiTree(BiTree& T) {
	char ch;
	cin >> ch;
	if (ch == '#')
		T = NULL;//如果输入的字符为'#',说明这是个空结点
	else {
		T = new BiTNode;
		if (!T)
			return OVERFLOW;
		T->data = ch;
		CreateBiTree(T->lchild);//建立左子树
		CreateBiTree(T->rchild);//建立右子树
	}
	return 1;
}

? 按照先序遍历的顺序输入各个结点的内容。这里值得注意的地方——要把空结点也输入进去,用'#'符号代表空结点。否则二叉树的建立将永远持续下去。举例:下图对应的输入操作应该是? A B D # # E # # C F # # #

示例:

int main()
{
	BiTree t=NULL;
	cout << "请构造一棵树:";
	CreateBiTree(t);
	cout << "先序遍历的结果是:";
	PreOrderTraverse(t);
	cout << endl;
	cout << "中序遍历的结果是:";
	InOrderTraverse(t);
	cout << endl;
	cout << "后序遍历的结果是:";
	PostOrderTraverse(t);
	cout << endl;
	cout << "层次遍历的结果是:";
	LevelOrderTraverse(t);
	return 0;
}

(4)计算二叉树深度

int Depth(BiTree T) {
	if (T == NULL)
		return 0;
	else {
		int m = Depth(T->lchild);//左子树的深度
		int n = Depth(T->rchild);//右子树的深度
		if (m > n)//如果左子树深度较大
			return m + 1;//返回左子树深度+1(带上根结点那层)
		else
			return n + 1;//否则返回右子树深度+1
	}
}

?若是空树,深度为0。否则递归计算左子树的深度为m,递归计算右子树的深度为n,二叉树的深度则为m与n的较大者+1。

(5)计算二叉树结点总数

int NodeCount(BiTree T) {
	if (T == NULL)
		return 0;//为空的结点不能算作结点
	else
		return NodeCount(T->lchild) + NodeCount(T->rchild) + 1;
}

?若是空树,则结点为0(并且递归到空结点的时候也不能算作结点)。如果不是空树,则结点个数为左子树的结点+右子树的结点+1(根)。

(6)计算二叉树叶子结点总数

int LeafCount(BiTree T) {
	if (T == NULL)
		return 0;//空树返回0
	if (T->lchild == NULL && T->rchild == NULL)//叶子结点的判断条件——没有孩子
		return 1;//叶子结点返回1
	else
		return LeafCount(T->lchild) + LeafCount(T->rchild);//根的左子树叶子结点和右子树叶子结点的和
}

? 如果树为空则为0。如果是叶子结点返回1。二叉树叶子结点总数为根的左子树叶子结点个数+右子树叶子结点个数。

示例:

?7 1 10 9 # # 6 # # 5 # # 3 2 # # 8 # #?

?注意这里的10,由于输入的相当于字符型,所以会被拆成1和0分别输入进去,因此要换一种表示方法——输入整形,用-1表示空结点

?7 1 10 9 -1 -1 6 -1 -1 6 -1 -1 3 2 -1 -1 8 -1 -1

int main()
{
	BiTree t=NULL;
	cout << "请构造一棵树:";
	CreateBiTree(t);
	cout << "二叉树的深度为:";
	printf("%d", Depth(t));
	cout << endl;
	cout << "二叉树的结点总数为:";
	printf("%d", NodeCount(t));
	cout << endl;
	cout << "二叉树的叶子结点总数为:";
	printf("%d", LeafCount(t));
	cout << endl;
	return 0;
}

?

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-18 12:56:50  更:2021-08-18 12:57:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 20:22:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码