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.youkuaiyun.com/ETalien_/article/details/89439892