1
0
mirror of https://github.com/Didnelpsun/CS408.git synced 2026-06-17 15:37:31 +08:00

更新树

This commit is contained in:
Didnelpsun
2021-04-25 23:46:32 +08:00
parent 5dc818f9af
commit 400009cb71
6 changed files with 255 additions and 0 deletions

View File

@@ -142,6 +142,7 @@
<ClCompile Include="head.h" />
<ClCompile Include="main.c" />
<ClCompile Include="sequence_stack.h" />
<ClCompile Include="sequence_tree.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="double_link_list.h" />
@@ -149,6 +150,7 @@
<ClInclude Include="link_queue.h" />
<ClInclude Include="link_stack.h" />
<ClInclude Include="link_string.h" />
<ClInclude Include="link_tree.h" />
<ClInclude Include="sequence_list.h" />
<ClInclude Include="sequence_string.h" />
<ClInclude Include="static_link_list.h" />

121
Code/link_tree.h Normal file
View File

@@ -0,0 +1,121 @@
#include <stdio.h>
#include <stdlib.h>
#include "head.h"
#include "sequence_queue.h"
// 链树
typedef struct LinkTreeNode {
element_type data;
// 左右孩子结点
struct LinkTreeNode* lchild, * rchild;
} LinkTreeNode, *LinkTree;
// 初始化链树
int InitLinkTree(LinkTree tree, element_type elem) {
tree->data = elem;
tree->lchild = NULL;
tree->rchild = NULL;
return 0;
}
// 插入链树结点
int InsertLinkTree(LinkTree tree, element_type elem) {
LinkTreeNode* node = (LinkTreeNode*)malloc(sizeof(LinkTreeNode));
if (node) {
node->data = elem;
node->lchild = NULL;
node->rchild = NULL;
tree->lchild = node;
}
else {
printf("InsertLinkTree:分配新内存空间失败!");
return 1;
}
return 0;
}
// 先序遍历链树
int PreorderTraversalLinkTree(LinkTree tree, int(*visit)(LinkTree elem)) {
if (tree != NULL) {
// 访问根元素
int result = visit(tree);
if (result == 1) {
printf("PreorderTraversalLinkTree:访问元素操作失败!");
return 1;
}
// 递归调用先序遍历
PreorderTraversalLinkTree(tree->lchild, visit);
PreorderTraversalLinkTree(tree->rchild, visit);
}
return 0;
}
// 中序遍历链树
int InorderTraversalLinkTree(LinkTree tree, int(*visit)(LinkTree elem)) {
if (tree != NULL) {
// 递归调用中序遍历
PreorderTraversalLinkTree(tree->lchild, visit);
// 访问根元素
int result = visit(tree);
if (result == 1) {
printf("InorderTraversalLinkTree:访问元素操作失败!");
return 1;
}
PreorderTraversalLinkTree(tree->rchild, visit);
}
return 0;
}
// 后序遍历链树
int PostorderTraversalLinkTree(LinkTree tree, int(*visit)(LinkTree elem)) {
if (tree != NULL) {
// 递归调用后序遍历
PreorderTraversalLinkTree(tree->lchild, visit);
PreorderTraversalLinkTree(tree->rchild, visit);
// 访问根元素
int result = visit(tree);
if (result == 1) {
printf("PostorderTraversalLinkTree:访问元素操作失败!");
return 1;
}
}
return 0;
}
// 求树的深度
int GetLinkTreeDeepth(LinkTree tree) {
if (tree == NULL) {
return 0;
}
else {
// 递归遍历左右子树
int l = GetLinkTreeDeepth(tree->lchild);
int r = GetLinkTreeDeepth(tree->rchild);
// 最后访问根
return l > r ? l + 1 : r + 1;
}
}
// 层序遍历链树
int LevelorderTraversalLinkTree(LinkTree tree, int(*visit)(LinkTree elem)) {
// 建立辅助队列
SequenceQueue queue;
// 初始化队列
InitSequenceQueue(&queue);
// 指定元素
LinkTree p;
// 根结点入队
//EnterSequenceQueue(&queue, tree);
// 队列不空则循环
while (IsSequenceQueueEmpty(queue) == 0) {
// 队头出队
//ExitSequenceQueue(&queue, &p);
visit(p);
if (p->lchild != NULL) {
//EnterSequenceQueue(&queue, p->lchild);
}
if (p->rchild != NULL) {
//EnterSequenceQueue(&queue, p->rchild);
}
}
}

View File

@@ -54,4 +54,12 @@ int GetSequenceQueueHead(SequenceQueue* queue, element_type* elem) {
// 根据队头指针赋值元素
*elem = queue->data[queue->front];
return 0;
}
// 判断顺序队是否为空
int IsSequenceQueueEmpty(SequenceQueue queue) {
if (queue.rear == queue.front) {
return 1;
}
return 0;
}

6
Code/sequence_tree.cpp Normal file
View File

@@ -0,0 +1,6 @@
#include <stdio.h>
#include <stdlib.h>
#include "head.h"
// 定义一个顺序树数组
typedef int SequenceTree[MAXSIZE];

View File

@@ -60,3 +60,4 @@ KMP算法是对朴素模式匹配算法的优化。
KMP算法对于重复部分比较多的模式串匹配效果更好。
KMP算法的next数组存在一定问题当当前索引的值匹配失败那么模式串的其他同样值的地方也一定会匹配失败。所以可以直接将模式串所有相同值的部分的next值全部取为其next值对应索引的next值。

117
Data-Structrue/tree.md Normal file
View File

@@ -0,0 +1,117 @@
# 树
## 基本概念
### 树的基本概念
+ 树n个结点的有限集树是一种递归的数据结构适合于表示具有层次的数据结构。是递归定义的。
+ 根结点:只有子结点没有父结点的结点。除了根结点外,树任何结点都有且仅有一个前驱。
+ 分支结点:有子结点也有父结点的结点。
+ 叶子结点:没有子结点只有父结点的结点。
+ 空树结点数为0的数。
+ 子树当n>1时其余结点可分为m个互不相交的有限集合每个集合本身又是一棵树其就是根结点的子树。
+ 结点的度:一个结点的孩子(分支)个数。
+ 树的度:树中结点的最大度数。
+ 结点的层次(深度):从上往下数。
+ 结点的高度:从下往上数。
+ 树的高度(深度):多少层。
+ 两结点之间的路径:由两个结点之间所经过的结点序列构成。
+ 两结点之间的路径长度:路径上所经过的边的个数。
+ 树的路径长度:指树根到每个结点的路径长的总和,根到每个结点的路径长度的最大值是树的高。
+ 有序树:树各结点的子树从左至右有次序不能互换。
+ 无序树:树各结点的子树从左至右无次序可以互换。
### 森林的基本概念
+ 森林时m棵互不相交的树的集合。
+ 一颗树可以被分为森林。
### 树的性质
+ 结点数=总度数+1。
+ 树的度m代表至少一个结点度是为m且一定是非空树至少有m+1个结点而m叉树指所有结点的度都小于等于m可以是空树。
+ 度为m的树以及m叉树第i层至多有$m^{i-1}$个结点。
+ 高度为h的m叉树至多有$\dfrac{m^h-1}{m-1}$个结点。
+ 高度为h的m叉树至少有$h$个结点度为m的树至少有$h+m-1$个结点。
+ 具有n个结点的m叉树最小高度为$\lceil\log_m(n(m-1)+1)\rceil$。已知高度最小时所有结点都有m个孩子所以$\dfrac{m^{h-1}-1}{m-1}<n<\leqslant\dfrac{m^h-1}{m-1}$,从而得到$h-1<\log_m(n(m-1)+1)\leqslant h$。
## 二叉树
### 二叉树的基本概念
+ 二叉树是n个结点构成的有限集合。
+ 二叉树可以为空二叉树,也可以是由一个根结点和两个互不相交的被称为根的左子树和右子树构成。左子树和右子树又分别是一棵二叉树,左右子树不能颠倒。
+ 满二叉树一棵高度为h含有$2^h-1$个结点的二叉树只有最后一层有叶子结点不存在度为1的结点按层序从1开始编号结点i的左孩子为$2i$,右孩子为$2i+1$,父结点如果有为$\lfloor\dfrac{i}{2}\rfloor$。
+ 完全二叉树当且仅当其每个结点都与高度h满二叉树编号1到n的结点一一对应时该二叉树就是完全二叉树只有最后两层有叶子结点最多只有一个度为1的结点且一定为左孩子$i\leqslant\lfloor\dfrac{n}{2}\rfloor$为分支结点,$i>\lfloor\dfrac{n}{2}\rfloor$为叶子结点。
+ 二叉排序树:左子树上所有结点的关键字均小于根结点的关键字;右子树上所有结点的关键字均大于根结点的关键字;左右子树又各是一棵二叉排序树。
+ 平衡二叉树树上任一结点的左子树和右子树的深度之差不超过1。
### 二叉树的性质
+ 设非空二叉树中度为0、1和2的结点个数分别为$n_0$$n_1$、$n_2$,则$n_0=n_2+1$(叶子结点比二分结点多一个)。假设树中结点的总数为$n$,则$n=n_0+n_1+n_2$,又根据树的结点等于总度数+1得到$n=n_1+2n_2+n_0$,所以相减就得到结论。
+ 二叉树的第$i$层至多有$2^{i-1}$个结点。
+ 高度为$h$的二叉树至多有$\dfrac{m^h-1}{m-1}$个结点。
+ 具有$n$个结点的完全二叉树的高度$h=\lceil\log_2(n+1)\rceil$或$\lfloor\log_2n\rfloor+1$。$2^{h-1}-1<n\leqslant2^h-1$。
+ 完全二叉树最多只有一个度为1的结点度为0度为2的结点的个数和一定为奇数若完全二叉树有$2k$个结点,则必然$n_1=1$$n_0=k$$n_2=k-1$,若完全二叉树有$2k-1$个结点,则必然$n_1=0$$n_0=k$$n_2=k-1$。
### 二叉树存储结构
#### 顺序存储
如果是完全二叉树,可以按照顺序进行存储,如果$i$有左孩子则$2i\leqslant n$,若有右孩子则$2i+1\leqslant n$,若有叶子或分支结点则$i>\lfloor\dfrac{n}{2}\rfloor$。
如果不是完全二叉树,则让二叉树的编号与完全二叉树相对应再存入数组,其他的结点为空,这种存储方法会浪费较多内存,最坏情况下高度为$h$,且只有$h$个结点的单支树也需要$2^h-1$个存储单元。
#### 链式存储
链式树具有两个分别指向左右子树的指针。
如果要保存父结点的位置,可以添加一个父结点指针,从而变成三叉链表。
### 二叉树的遍历
遍历是按照某种次序将所有结点都访问一遍。
#### 顺序遍历
顺序遍历就是深度优先的遍历,分为三种:
+ 先序遍历根左右NLR。
+ 中序遍历左根右LNR。
+ 后序遍历左右根LRN。
根据算算数表达式的分析树的不同先序、中序、后序遍历方式可以得到前缀、中缀、后缀表达式。
若树的高度为$h$,则时间复杂度为$O(h)$。
#### 层序遍历
层序遍历就是广度优先的遍历。
1. 初始化一个辅助队列。
2. 根结点入队。
3. 若队列非空,则队头结点出队,访问该结点,如果有并将其左右孩子入队。
4. 重复步骤3直至队列空。
### 遍历序列构造二叉树
若只给出一棵二叉树的前/中/后/层序遍历序列中的一种不能唯一确定一棵二叉树。只有给出中序遍历序列才可能推出唯一二叉树,因为无法确定根结点相对于左右结点的位置:
+ 前序+中序。
+ 后序+中序。
+ 层序+中序。
#### 前序+中序
前序:根+左+右;中序:左+根+右。所以根据三个部分对应相同可以推出。
#### 后序+中序
后序:左+根+右;中序:左+根+右。所以根据三个部分对应相同可以推出。
#### 层序+中序
层序:根+左根+右根;中序:左+根+右。所以根据根结点和左右子树的根结点来确定。
### 线索二叉树