救命!我在老师讲【栈】和【队列】的课上不小心睡过去了..........是睡了很久的那种。。(感觉我的精神只能支撑一节半的课,每次到第二节课的后半节我就会倒下。...
不过今天讲的【树】有好好听了!(指听了1.5节)
Anyway,我们今天先来讲讲树!准确来说大标题是:树 形 结 构 。!(老师专门指出了大标题不是"树"。)
树形结构(Tree Structure)
树(Tree)形结构包括:树、森林(后续好像只有概念了,全转换成二叉树去了)、二叉树、哈夫曼树(这个我只会写用数组存的版本啊啊啊啊不好意思)...
树(Tree)
类似族谱(Family Tree)!(我当时可兴奋了XD。For Tangle Tower and Harry Potter
- (暂时不讨论自由树。有根)树的定义是递归的!
- 有一些表示方法,比如文氏图、平行线法(我喜欢这个)、...
- 有一些术语!(但基本可以猜到是什么意思)
结点(Node)、结点的度(Degree of Node)(我比较喜欢称之为宽度!)、树的度(Degree of Tree)(取最大的结点的度)、
叶子结点(Leaf Node)(终端结点)、分支结点(Branch Node)(非终端结点、内部结点)、
孩子(Child)、双亲(Parent)、祖先(Ancestor)(可以理解为继承过的所有结点)、子孙(Descendant)、兄弟(Brother)(同一双亲的孩子)、堂兄弟(Sibling)(草)、
结点的层次(Level of Node)(根结点的层次规定为1)、树的深度(Depth of Tree)(高度)、
无序树(Unordered Tree)、有序树(Ordered Tree)、
森林(Forest):m(m≥0)棵树的集合。(老师指着一堆空气说这是森林wwww我真的要笑死。)
- 有一些基本的方法(函数)!比如插入、删除(从族谱除名(株连九族.....
树 to 二叉树:(我有空画个图示吧!文字描述得我想死(。写得想死看得也想死)
二叉树 to 树:
森林(Forest)
0个或一堆树。
森林 to 二叉树:把树根们当成同级的,按一般方法串起来。(有空画图示)
二叉树(Binary Tree)
二叉树(binary tree)是指树中节点的度不大于2的有序树,它是一种最简单(又开始最简单了。。)且最重要(太好了!)的树。二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树。
老师:我比较喜欢叫它异叉树。(用粤语读试试!
PS:度为2的树不一定是二叉树哦!二叉树是有序树,二叉树的左/右结点是有特殊意义的。
关于左结点和右结点分别代表什么,请听我慢慢道来!(暂时不知道怎么解释。。
左结点是连接(正常树中)上与右(父节点和第一个子节点)的结点,
右结点是连接左与下(同级的子节点)的。(也就是说斜向右下角的对角线上的结点们在正常树中是兄弟关系!
特殊的二叉树们
正则二叉树:每个叉的分叉数都是2
平衡二叉树:平衡高度
理想平衡二叉树:倒数第二层是满的
满二叉树:完全满(金字塔的感觉)
完全二叉树:按顺序走的尽量满(可以用数组存)
存储结构
顺序结构(数组)(一般用在完全二叉树里)、链式结构(链表)
我流链式结构的结点Node:丨left丨data丨right丨
(哭死... 我又开始在大半夜写作业了。我真的很需要一个电子宠物在旁边陪我!!)
参考:
【C/C++编程技术:数据结构与算法之二叉树(一个小时掌握)_哔哩哔哩_bilibili】
【二叉树的先序、中序、后序遍历C++_二叉树的先序,中序,后序遍历-CSDN博客】详细+好看。
【完全二叉树的顺序储存结构(C++完整代码)_构造n节点的完全二叉树,先序输出-CSDN博客】完整代码可以用!
【【纯干货】三分钟教会你遍历二叉树!学不会举报我!!_哔哩哔哩_bilibili】评论好牛:本质上是代码里面的访问顺序,左子树递归和右子树递归。先序是第一次访问节点,就直接写上,中序是第二次访问节点,后续是递归结束,最后离开这个节点的顺序。第一次线是第一次访问,过节点下的记号是第二次访问,过节点的右边记号是离开这个节点。
【二叉树的创建和基本操作(详解)_建立二叉树-CSDN博客】
碎碎念:
每一次写数据结构作业都觉得好无助。但是仔细想想,我已经无助过很多次了,加上我从来都不是一个人在学(不是指大家都要写这个作业,而是我的脑子里不止一个我自己。),所以没有什么好担心的!最起码不会死掉。
遍历
遍历方式:
先/前序遍历(PreOrder):根左右
中序遍历(InOrder):左根右
后序遍历(PostOrder):左右根
- 每一个三人组都按照同一个遍历方式进行
- 三种遍历方式又分别有递归的和非递归的
- 先序遍历和中序遍历可以确定一棵二叉树。(后面写作业的时候我天天拿这俩来输出。)
递归遍历
递归遍历的实现是比较简单的!搞清楚顺序就好了。
先序遍历(PreOrder)(根左右)
// 先序遍历(根左右)递归
void BinaryTree::PreOrder(BinTreeNode* root)
{
if (root != NULL) // root == NULL是递归终止条件
{
// 1. 输出
cout << root->data << " ";
// 2. 遍历左子树
PreOrder(root->left);
// 3. 遍历右子树
PreOrder(root->right);
}
}
中序遍历(InOrder)(左根右)
// 中序遍历(左根右)递归
void BinaryTree::InOrder(BinTreeNode* root)
{
if (root != NULL) // root == NULL是递归终止条件
{
// 1. 遍历左子树
InOrder(root->left);
// 2. 输出
cout << root->data << " ";
// 3. 遍历右子树
InOrder(root->right);
}
}
后序遍历(PostOrder)(左右根)
// 后序遍历(左右根)递归
void BinaryTree::PostOrder(BinTreeNode* root)
{
if (root != NULL) // root == NULL是递归终止条件
{
// 1. 遍历左子树
PostOrder(root->left);
// 2. 遍历右子树
PostOrder(root->right);
// 3. 输出
cout << root->data << " ";
}
}
非递归遍历(迭代遍历)
需要用到栈!
先序非递归遍历(PreOrder)(根左右)
小过程:
1. 定义一个移动指针(我们的劳模指针p),指向根部root
2. 沿着左边走,直到走到空的位置
(边走边打印,并记录下来(入栈))
3. 回退(出栈)(判断出栈元素是否存在右边)
4. 如果存在右边,重复步骤2、3
我的脑子:
p就是用来指你的路径的(不过回退了就删掉)
void InOrderByStack(BinTreeNode* root) // 中序遍历(左根右)迭代
{
// 0. root == NULL 直接结束
if (root == NULL)
{
return;
}
// 1. 定义一个移动指针(我们的劳模指针p),指向根部root
struct BinTreeNode* p = root;
struct BinTreeNode* stack[100];
int stackTop = -1;
while (stackTop != -1 || p != NULL) {
// 2. 沿着左边走,直到走到空的位置
while (p != NULL) {
// 边走边打印
cout << p->data << " ";
// 并记录下来(入栈)
stack[++stackTop] = p;
p = p->left;
}
// 3. 回退(出栈)(判断出栈元素是否存在右边)
if (stackTop != -1) {
p = stack[stackTop--];
p = p->right;
}
} // 4. 如果存在右边,重复步骤2、3
}
中序非递归遍历(InOrder)(左根右)
过程:
1. 创造p
2. 边走边记录(不用打印)
3. 回退,出栈,打印
后序非递归遍历(PostOrder)(左右根)
难!
平衡二叉树
暂时先写这么多!
又!学了好多东西(堆、哈夫曼树)。。。TT
堆(heap)
堆(heap)
完全二叉树:按顺序走的尽量满(可以用数组存)
大根堆(最大堆)
小根堆(最小堆)
向上调整、向下调整...
哈夫曼树(Huffman Tree)
(这个有实验作业!所以后续可能会讲。)
哈夫曼树(Huffman Tree)/ 最优二叉树。可以提高传送信息的效率!(编码最短?)
(树的)带权路径长度 WPL(Weighted Path Length):(数值 x 路径长度)的总和
- 只计算叶子结点
- 在哈夫曼编码中的意义:出现频率 x 编码长度
哈夫曼树的结点个数为:n = 2n' - 1 (n' 表示叶子节点)所以...大概可以开数组存?
参考:【赫夫曼编码树(图解+完整代码)_哈夫曼数编码_~在下小吴的博客-CSDN博客】
【【数据结构C/C++】C语言哈夫曼编码与解码实现细则(附代码以及详细实现解释)_哈夫曼编码代码_ZhangBlossom的博客-CSDN博客】
hehe...【C/C++中函数参数传递的三种方式:值传递,指针传递,引用传递_引用传参-CSDN博客】
哈夫曼编码
一般设定为左0右1。
参考:【【数据结构】构造哈夫曼树手写代码_哔哩哔哩_bilibili】【[数据结构] 哈夫曼树HuffmanTree、哈夫曼编码的c/c++语言实现_怎么使用c++实现使用哈夫曼树求哈夫曼编码-CSDN博客】
Emmmmmmm顺带一提,这个作业不知道怎么的就写完了。。(最起码哈夫曼编码的部分是纯手工的!现在被困在文件读写那块。。
二叉查找树(BST)
二叉查找树/二叉排序树(BST,Binary Search Tree)。
特征:左结点 < 根结点 < 右结点(中序遍历递增)
// 节点 BinTreeNode
struct Node {
int data; // 数据
Node* left; // 指向左孩子节点的指针
Node* right; // 指向右孩子节点的指针
Node() :left(NULL), right(NULL) {} // 不懂TT
Node(int x, Node* l = NULL, Node* r = NULL, bool ava = true)
: data(x), left(l), right(r) {} // 还是不懂TT
// 我真的。。举三反一是有一套的hehehehe
~Node() {}
};
// 二叉树 Binary Search Tree
class BSTree {
public:
Node* root; // 根节点
BSTree() {} // 构造函数
~BSTree() {} // 析构函数
void PreOrder(Node* root); // 先序遍历(根左右)递归
void InOrder(Node* root); // 中序遍历(左根右)递归
Node* Create(int n); // 建立(在插入的基础上建立)
Node* Insert(int x, Node* t); // 插入(递归)
Node* Search(int x, Node* p); // 查找(递归)
};
// 插入(递归)
Node* BSTree::Insert(int x, Node* t) {
// 树是空树
if (t == NULL) {
Node* p = new Node; // 建立根节点
p->data = x;
if (root == NULL) // 整个树都是空树
root = p;
return p;
}
if (x < t->data) {
t->left = Insert(x, t->left); // 插入到树的左子树
}
else if (x > t->data) {
t->right = Insert(x, t->right); // 插入到树的右子树
}
// else if (x == t->data) // 重复
return t; // t!!! 不是t->left/right呜呜呜呜呜呜卡了我好久!!
}
// 建立
Node* BSTree::Create(int n) {
int x;
root = NULL;
for (int i = 0; i < n; i++) {
cin >> x;
if (i == 0)
root = Insert(x, root);
else
Insert(x, root);
}
// cout << "root " << root << endl;
return root;
}
// 查找(递归)
Node* BSTree::Search(int x, Node* p) {
// 1. 判断空 - 没查到
if (p == NULL) return NULL; // 是没查到的意思!相当于往下查查查发现查不动了
// 2. 查到了
if (x == p->data) {
return p;
}
// 3. 不是上述情况,那我们继续查
else if (x < p->data) return Search(x, p->left); // 在左子树中查找
else if (x > p->data) return Search(x, p->right); // 在右子树中查找
}
// 查找(非递归) // 空间复杂度更低
Node* BSTree::Search(int x) {
Node* t = root;
// 1. 判断空
while (t != NULL) {
// 2. 查到了
if (x == t->data) {
return t;
}
// 3. 不是上述情况,那我们继续查
else if (x < t->data) t = t->left); // 在左子树中查找
else if (x > t->data) t = t->right); // 在右子树中查找
}
return NULL; // 没找到
}
作业
救命。巨难无比。。
参考:
【二叉树的前序、中序、后序遍历以及根据前序和中序或中序和后序生成二叉树_输入后序遍历序列和中序遍历序列以生成二叉树二叉链式结构,并采用前序遍历序列输_Karan_01的博客-CSDN博客】
【【二叉树构建与遍历2】后序遍历+中序遍历构建一个二叉树并输出先序遍历 C++实现-CSDN博客】
【用递归法把二叉树的叶子结点按从左到右的顺序连成一个单链表_实现双向循环链表倒置的程序-CSDN博客】
【设计一个算法将二叉树的叶结点按从左到右的顺序连成一个单链表,表头指针为head,链接时用叶结点的右指针域来存放单链表指针-CSDN博客】
新作业。难得想死。。
参考: