C++树学习笔记

C++树学习笔记

前言

对于大量的输入数据,链表的线性访问时间太长,不宜使用。对此我们定义一种新的数据结构——树,其大部分操作的运行时间平均为O(log N)。


一、树

1.树的定义与树模型

一颗树是一些节点的集合,这个集合可以是空集,若不是空集,则树由称作(root)的结点r以及零个或者多个非空的(子)树T1,T2…,Tk组成,这些子树中每一棵的根都被来自r的一条有向的所连接。

每一棵子树的根叫做根r的儿子,而r是每一个子树的根的父亲。示意图如下:
在这里插入图片描述

所有的树都可以通过有数的上述结构组成,下面介绍一棵具体 的树,并讲解一些重要的概念:
在这里插入图片描述

  • 根(root): 每一颗树的根只有一个,上述A为根结点。
  • 叶(leaf): 没有儿子的结点称为叶节点,如图中的B、C、H、I等。
  • 儿子(child)和父亲(parent): B是A的儿子,A是 B的父亲
  • 兄弟(siblings): 具有相同父亲的节点称为兄弟,如图中的K、L、M。
  • 祖父(grandparent)和 孙子(grandchild): 类似的定义祖父和孙子。
  • 路径(path)和长(length): 从结点n1到结点n2的一个序列称为路径,如 ADH为A到H的一个路径,其路径的长度为边的数量,此时为2.
  • 深度(depth): 从根到某一结点的唯一路径的长。

2.树的实现

典型的树的声明如下:

struct treeNode
{
	ElemType data;
	treeNode* firstChild;   //指向第一个孩子
	treeNode* nextSibling;  //指向兄弟结点
}

说明:
对于一般的树而言,我们不知道一个父节点有多少个儿子,如果在结构体中增加第二个孩子、第三个孩子…这样的定义时,浪费空间不说还有可能无法与所有孩子产生连接(即当有大于n个孩子而只定义到n个孩子时),因此我们转变思维,用兄弟结点间接让父结点和孩子结点产生连接。下面举一个例子:
在这里插入图片描述
可知A是B、C、D、E、F、G的父结点,而只有B是通过firstChild指向的,其他的都是通过相邻的兄弟结点即nextSibling指向的。

建立一个简单的树测试一下:
代码如下(示例):

//构建一个已经知道形状的树
/*形状如下:
*         A
*        /|\ 
*       B C D
*      / /|  
*     E F G 
**/
#include<iostream>
//#include"Tree.h"
using namespace std;
struct treeNode
{
	char data;
	treeNode* firstChild;
	treeNode* nextSibling;
	treeNode(char value,treeNode* firstChild1=nullptr,treeNode* nextSibling1=nullptr)
	{
		data = value;
		firstChild = firstChild1;
		nextSibling = nextSibling1;
	}
};

int main()
{
	treeNode* Root = new treeNode('A');
	treeNode* movePtr = Root;
	movePtr->firstChild = new treeNode('B');
	movePtr->firstChild->nextSibling = new treeNode('C');
	movePtr->firstChild->nextSibling->nextSibling = new treeNode('D');
	movePtr->firstChild->firstChild = new treeNode('E');
	movePtr->firstChild->nextSibling->firstChild = new treeNode('F');
	movePtr->firstChild->nextSibling->firstChild->nextSibling = new treeNode('G');

	//测试
	treeNode* testPtr = Root;
	int count = 0;
	cout << "根结点是:";
	cout << testPtr->data << endl;
	testPtr = testPtr->firstChild;
	while (testPtr != nullptr)
	{
		count++;
		cout << "结点" << Root->data << "的第" << count << "个儿子为:" <<testPtr->data<< endl;
		testPtr = testPtr->nextSibling;
	}

	return 0;
}

输出结果:

说明:上述代码只是简单的定义了一个一般的树,是在知道树的形状下的定义。

由于一般树的无序性,很多算法思想都不好实现,下面介绍一种常用的树——二叉树。

二、二叉树

1.二叉树的定义与相关概念

介绍了相关树的概念,下面讲讲在树中应用最广泛的二叉树。
二叉树(binary tree)是实行了计划生育的一般树,每个父结点的儿子结点都不能大于两个。如下图显示了一个简单的二叉树。

2.二叉树的实现

与上述的一般树类似,二叉树的实现如下:

struct binaryNode
{
	ElemType data;
	binaryNode* left;   //左孩子
	binaryNode* right;  //右孩子
}

3.二叉树的遍历(前序遍历、中序遍历、后序遍历、层次遍历)

二叉树有常用的四种遍历方式,分别为前序遍历、中序遍历、后续遍历、层次遍历。

前序遍历:根结点 —> 左子树 —> 右子树。
说明:从根结点开始输出根结点,然后是其左孩子,最后才是右孩子。

中序遍历:左子树—> 根结点 —> 右子树。
说明:从树的左孩子(根结点的左孩子的左孩子…)开始,然后输出该结点,最后输出它的右孩子。

后序遍历:左子树 —> 右子树 —> 根结点。
说明:对于当前结点,先输出它的左孩子,然后输出该结点,最后输出它的右孩子。

层次遍历:只需按层次遍历即可

以下图为例介绍几种遍历方法:

前序遍历: 5 2 1 4 3 8 7 (先父结点后右结点然后才是右结点)
(1):输出 5,接着左孩子;
(2):输出 2,接着左孩子;
(3):输出 1,左右孩子都为空,回到2输出2的右孩子;
(4):输出 4,接下来为左孩子;
(5):输出 3,左右孩子都为空,2的左右子树都输出,则回到5输出5的右子树;
(6):输出8,接下来为左孩子
(7):输出7,结束。

中序遍历: 1 2 3 4 5 7 8 (核心思想:有左结点先输出左结点)
(1):输出1,1无左右结点返回父结点2;
(2):输出2,2有右结点4,结点4有左孩子3,3没有左孩;子,停止,输出3(若3有左孩子输出3的左孩子,一层层下去);
(3):输出3,回到4;
(4):输出4,2的右子树输出完毕,回到5;
(5):输出5,开始看5的右子树,同(2)可知输出7;
(6):输出7;
(7):输出8,结束。

后序遍历: 1 3 4 2 7 8 2 (核心思想:先左后右,父结点最后输出)
(1):输出1,然后可知1没有左右结点,回到2开始输出2的右子树,到4发现4有孩子,先输出4的孩子后才是4且秉持先左后右原则;
(2):输出3,4的左右子树输出完毕,然后输出4;
(3):输出4,2的左右子树输出完毕,然后输出4,然后是5的右子树;
(4):输出7;
(5):输出8;
(6):输出2,结束。

三、容器介绍与实战演练

1.STL容器之set和map

相关见:https://blog.csdn.net/ETalien_/article/details/89439892

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值