From 018a10d0bc4f1dc354f2d4e6ce90badb5888ab18 Mon Sep 17 00:00:00 2001
From: Didnelpsun <2675350965@qq.com>
Date: Mon, 26 Apr 2021 23:48:51 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=B2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Code/Code.vcxproj | 1 +
Code/Code.vcxproj.filters | 15 ++++++
Code/link_tree.h | 12 ++---
Code/sequence_tree.h | 46 ++++++++++++++++++
Code/thread_tree.h | 81 ++++++++++++++++++++++++++++++++
Data-Structrue/tree.md | 99 +++++++++++++++++++++++++++++++++++++++
6 files changed, 248 insertions(+), 6 deletions(-)
create mode 100644 Code/sequence_tree.h
create mode 100644 Code/thread_tree.h
diff --git a/Code/Code.vcxproj b/Code/Code.vcxproj
index 9c770fd..7d07a56 100644
--- a/Code/Code.vcxproj
+++ b/Code/Code.vcxproj
@@ -143,6 +143,7 @@
+
diff --git a/Code/Code.vcxproj.filters b/Code/Code.vcxproj.filters
index 54435f4..9de69db 100644
--- a/Code/Code.vcxproj.filters
+++ b/Code/Code.vcxproj.filters
@@ -24,6 +24,12 @@
头文件
+
+ 源文件
+
+
+ 头文件
+
@@ -47,5 +53,14 @@
头文件
+
+ 头文件
+
+
+ 头文件
+
+
+ 头文件
+
\ No newline at end of file
diff --git a/Code/link_tree.h b/Code/link_tree.h
index 87565ea..46d25ec 100644
--- a/Code/link_tree.h
+++ b/Code/link_tree.h
@@ -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);
+ }*/
}
}
\ No newline at end of file
diff --git a/Code/sequence_tree.h b/Code/sequence_tree.h
new file mode 100644
index 0000000..0be10ad
--- /dev/null
+++ b/Code/sequence_tree.h
@@ -0,0 +1,46 @@
+#include
+#include
+#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;
diff --git a/Code/thread_tree.h b/Code/thread_tree.h
new file mode 100644
index 0000000..18eaced
--- /dev/null
+++ b/Code/thread_tree.h
@@ -0,0 +1,81 @@
+#include
+#include
+#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);
+ }
+}
\ No newline at end of file
diff --git a/Data-Structrue/tree.md b/Data-Structrue/tree.md
index 356a5e5..6b70adb 100644
--- a/Data-Structrue/tree.md
+++ b/Data-Structrue/tree.md
@@ -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. 中序遍历除去第一棵树之后剩余的树构成的森林。
+
+可以把每个树先按序遍历再合在一起,也可以先转换为二叉树再遍历。