1
0
mirror of https://github.com/Didnelpsun/CS408.git synced 2026-02-13 15:45:58 +08:00

更新串

This commit is contained in:
Didnelpsun
2021-04-26 23:48:51 +08:00
parent 400009cb71
commit 018a10d0bc
6 changed files with 248 additions and 6 deletions

View File

@@ -143,6 +143,7 @@
<ClCompile Include="main.c" />
<ClCompile Include="sequence_stack.h" />
<ClCompile Include="sequence_tree.cpp" />
<ClCompile Include="thread_tree.h" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="double_link_list.h" />

View File

@@ -24,6 +24,12 @@
<ClCompile Include="sequence_stack.h">
<Filter>头文件</Filter>
</ClCompile>
<ClCompile Include="sequence_tree.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="thread_tree.h">
<Filter>头文件</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="link_list.h">
@@ -47,5 +53,14 @@
<ClInclude Include="link_queue.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="link_string.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="link_tree.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="sequence_string.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -3,7 +3,7 @@
#include "head.h"
#include "sequence_queue.h"
// 链树
// 链二叉
typedef struct LinkTreeNode {
element_type data;
// 左右孩子结点
@@ -110,12 +110,12 @@ int LevelorderTraversalLinkTree(LinkTree tree, int(*visit)(LinkTree elem)) {
while (IsSequenceQueueEmpty(queue) == 0) {
// 队头出队
//ExitSequenceQueue(&queue, &p);
visit(p);
if (p->lchild != NULL) {
//EnterSequenceQueue(&queue, p->lchild);
//visit(p);
/*if (p->lchild != NULL) {
EnterSequenceQueue(&queue, p->lchild);
}
if (p->rchild != NULL) {
//EnterSequenceQueue(&queue, p->rchild);
}
EnterSequenceQueue(&queue, p->rchild);
}*/
}
}

46
Code/sequence_tree.h Normal file
View File

@@ -0,0 +1,46 @@
#include <stdio.h>
#include <stdlib.h>
#include "head.h"
// 双亲树结点
typedef struct ParentTreeNode {
element_type data;
// 双亲位置域
int parent;
} ParentTreeNode;
typedef struct {
// 双亲树结点
ParentTreeNode nodes[MAXSIZE];
// 结点树
int n;
} ParentTree;
// 孩子树孩子结点
typedef struct ChildTreeChildNode {
// 孩子结点在数组中的索引位置
int child;
// 下一个孩子
struct ChildTreeChildNode* next;
} ChildTreeChildNode;
// 孩子树结点
typedef struct {
element_type data;
// 指向第一个孩子
ChildTreeChildNode* first_child;
} ChildTreeNode;
// 孩子树
typedef struct {
ChildTreeNode nodes[MAXSIZE];
// 结点树与根的位置
int n, r;
} ChildTree;
// 孩子兄弟树
typedef struct ChildSiblingTreeNode {
element_type data;
// 第一个孩子与右兄弟指针
struct ChildSiblingTreeNode* first_child, * next_sibling;
} ChildSiblingTreeNode, * ChildSiblingTree;

81
Code/thread_tree.h Normal file
View File

@@ -0,0 +1,81 @@
#include <stdio.h>
#include <stdlib.h>
#include "head.h"
// 线索二叉树结点
typedef struct ThreadTreeNode {
element_type data;
struct ThreadTreeNode* lchild, * rchild;
int ltag, rtag;
} ThreadTreeNode, *ThreadTree;
// 全局变量,指向当前访问点的前驱
ThreadTreeNode* pre = NULL;
// 中序线索化
int InorderThread(ThreadTreeNode* node) {
// 左子树为空,建立前驱线索
if (node->lchild == NULL) {
node->lchild = pre;
node->ltag = 1;
}
if (pre != NULL && pre->rchild==NULL) {
// 建立前驱结点的后继线索
pre->rchild = node;
pre->rtag = 1;
}
pre = node;
}
// 找到以node为根结点的子树中第一个被中序遍历的结点
ThreadTreeNode* FristInOrderNode(ThreadTreeNode* node) {
//循环找到最左下角结点,不一定是叶子结点
while (node->ltag == 0) {
node = node->lchild;
}
return node;
}
// 在中序线索二叉树中找到结点node的后继结点
ThreadTreeNode* NextInOrderNode(ThreadTreeNode* node) {
// 如果有右子树
if (node->rtag == 0) {
return FristInOrderNode(node->rchild);
}
else {
return node->rchild;
}
}
// 对中序线索二叉树实现非递归的中序遍历
int InorderTraversalThreadTree(ThreadTree tree, int(*visit)(ThreadTreeNode* node)) {
for (ThreadTreeNode* p = FristInOrderNode(tree); p != NULL; p = NextInOrderNode(p)) {
visit(p);
}
}
// 找到以node为根结点的子树中前面最后一个被中序遍历的结点
ThreadTreeNode* LastInOrderNode(ThreadTreeNode* node) {
//循环找到最右下角结点,不一定是叶子结点
while (node->rtag == 0) {
node = node->rchild;
}
return node;
}
// 在中序线索二叉树中找到结点node的前驱结点
ThreadTreeNode* PreInOrderNode(ThreadTreeNode* node) {
// 如果有左子树
if (node->ltag == 0) {
return LastInOrderNode(node->rchild);
}
else {
return node->lchild;
}
}
// 对中序线索二叉树实现非递归的逆向中序遍历
int ReverseInorderTraversalThreadTree(ThreadTree tree, int(*visit)(ThreadTreeNode* node)) {
for (ThreadTreeNode* p = LastInOrderNode(tree); p != NULL; p = PreInOrderNode(p)) {
visit(p);
}
}

View File

@@ -115,3 +115,102 @@
### 线索二叉树
对于二叉树的遍历,只能从根结点开始遍历,如果给任意一个结点是无法完成遍历的。
所以我们就想能否保存结点的前驱和后继,从而能减少重复遍历树。因为一棵树很多结点的左右结点可能是空的,那么这些空闲的指针可以不代表左右子树的根结点,而是用来表示当前遍历方法的前驱或后继。当这个指针表示的是前驱或后继就称为线索,指向前驱的就是前驱线索,由左孩子指针担当,指向后继的就是后继线索,由右孩子指针担当。
#### 线索化
为了区分其左右孩子指针是指向什么要在结点中新建两个tag位如当ltag=0表示lchild指向的是左孩子结点而为1表示其指向前驱。
+ 确定线索二叉树类型——中序、先序或后序。
+ 按照对应遍历规则,确定每个结点访问顺序并写上编号。
+ 将n+1个空链域连上前驱后继。
#### 查找前驱后继
+ 中序线索二叉树中找到结点*P的中序后继next
+ 若p右孩子指针指向后继p->rtag==1则next=p->rchild。
+ 若p右孩子指针指向右子树根结点p->rtag==0则next=p右子树中最左下结点。
+ 所以可以利用线索对二叉树实现非递归的中序遍历。
+ 中序线索二叉树中找到结点*P的中序前驱pre
+ 若p左孩子指针指向前驱p->ltag==1则pre=p->lchild。
+ 若p左孩子指针指向左子树根结点p->ltag==0则pre=p左子树中的最右下结点。
+ 所以可以利用线索对二叉树实现非递归的逆向中序遍历。
+ 先序线索二叉树中找到结点*P的先序后继next
+ 若p右孩子指针指向后继p->rtag==1则next=p->rchild。
+ 若p右孩子指针指向右子树根结点p->rtag==0如果p有左孩子则p->next=p->lchild如果p没有左孩子则p->next=p->rchild。
+ 所以可以利用线索对二叉树实现非递归的先序遍历。
+ 先序线索二叉树中找到结点*P的中序前驱pre
+ 若p左孩子指针指向前驱p->ltag==1则pre=p->lchild。
+ 若p左孩子指针指向左子树根结点p->ltag==0先序遍历中左右子树的根结点只可能是后继所以这时候就找不到p的前驱如果没有父结点只能从头开始先序遍历。
+ 如果有父结点,则又有三种情况:
+ p为左孩子则根据根左右p的父结点为根所以在p的前面p->pre=p->parent。
+ p为右孩子其左兄弟为空则根据根左右顺序为根右所以p->pre=p->parent。
+ p为右孩子且有左兄弟根据根左右p的前驱就是左兄弟子树中最后一个被先序遍历的结点即在p的左兄弟子树中优先右子树遍历的底部。
+ 后序线索二叉树中找到结点*P后中序后继next
+ 若p右孩子指针指向后继p->rtag==1则next=p->rchild。
+ 若p右孩子指针指向右子树根结点p->rtag==0则根据左右根顺序左右孩子结点必然是p的前驱而不可能是后继所以找不到后序后继如果没有父结点只能使用从头开始遍历的方式。
+ 如果有父结点则又有三种情况:
+ p为右孩子根据左右根所以p->next=p->parent。
+ p为左孩子右孩子为空根据左右根所以p->next=p->parent。
+ p为左孩子右孩子非空根据左右根所以p->next=右兄弟子树中第一个被后序遍历的结点。
+ 后序线索二叉树中找到结点*P后中序前驱pre
+ 若p左孩子指针指向前驱p->ltag==1则pre=p->lchild。
+ 若p左孩子指针指向左子树根结点p->ltag==0则又有两种情况
+ 若p有右孩子则按照左右根的情况遍历右在根的前面所以p->pre=p->rchild。
+ 若p没有右孩子按照左根的顺序则p->pre=p->lchild。
### 二叉排序树
即BST是一种用于排序的二叉树
#### 二叉排序树的定义
二叉排序树也是二叉查找树。左子树上所有结点的关键字均小于根结点的关键字;右子树上所有结点的关键字均大于根结点的关键字;左右子树又各是一棵二叉排序树。
中序遍历二叉排序树会得到一个递增的有序序列。
#### 二叉排序树的查找
1. 若树非空,目标值与根结点的值比较。
2. 若相等则查找成功,返回结点指针。
3. 若小于根结点,则在左子树上查找,否则在右子树上查找。
4. 遍历结束后仍没有找到则返回NULL。
## 树与森林
### 树的存储结构
+ 双亲表示法:是一种顺序存储方式,每个结点中保存指向双亲的指针。查找双亲方便,但是查找孩子就只能从头遍历。
+ 孩子表示法是顺序加链式存储方法顺序存储所有元素添加一个firstChild域指向第一个孩子结构体的指针孩子结构体包括元素位置索引与指向下一个孩子结构体的next指针。
+ 孩子兄弟表示法:是一种链式存储方式,定义了两个指针,分别指向第一个孩子与右兄弟,类似于二叉树,可以利用二叉树来实现对树的处理 。
### 森林与树的转换
树与森林的转换,树与二叉树的转换都可以使用孩子兄弟表示法来实现,左孩子右兄弟,如果是森林则认为其根结点为兄弟。
### 树的遍历
+ 先根遍历:若树非空,先访问根结点,再依次对每棵子树进行先根遍历。
+ 后根遍历:若树非空,先依次对每棵子树进行后根遍历,最后访问根结点。
+ 层次遍历:用辅助队列实现:
1. 若树非空,根结点入队。
2. 若队列非空,队头元素出队并访问,同时将该元素的孩子依次入队。
3. 重复步骤二直到队列为空。
### 森林的遍历
先序遍历森林:
1. 访问森林中第一棵树的根结点。
2. 先序遍历第一棵树中根结点的子树森林。
3. 先序遍历除去第一棵树之后剩余的树构成的森林。
中序遍历森林:
1. 先序遍历第一棵树中根结点的子树森林。
2. 访问森林中第一棵树的根结点。
3. 中序遍历除去第一棵树之后剩余的树构成的森林。
可以把每个树先按序遍历再合在一起,也可以先转换为二叉树再遍历。