diff --git a/.vscode/settings.json b/.vscode/settings.json index f6572ed..64a242d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,11 @@ { "cSpell.words": [ + "firstchild", "infty", "inorder", "lchild", "ltag", + "nextsibling", "nextval", "nlog", "postorder", diff --git a/README.md b/README.md index 225d916..8fe8653 100644 --- a/README.md +++ b/README.md @@ -276,19 +276,19 @@ $$ ## 6. [树与二叉树](ch5/README.md#树与二叉树) -- 二叉树 - - 二叉树的基本概念 - - 定义及特点 - - 二叉树的存储结构 - - 二叉树的遍历 - - 搜索二叉树 - - 二叉树的应用 - - 二叉排序树 - - 平衡二叉树 - - 哈夫曼树和哈夫曼编码 -- 树和森林 - - 树的基本概念 - - 树的存储结构 - - 树和森林的遍历 - - 树和森林及二叉树的转换 - - 树的应用:查并集 +- [二叉树](ch5/binary-tree/README.md#二叉树) + - [二叉树的基本概念](ch5/binary-tree/README.md#二叉树) + - [定义及特点](ch5/binary-tree/README.md#4-二叉树的性质) + - [二叉树的存储结构](ch5/binary-tree-storage/README.md#二叉树的存储结构) + - [二叉树的遍历](ch5/binary-tree-traversal/README.md#二叉树的遍历) + - [线索二叉树](ch5/binary-tree-traversal/README.md#7-线索二叉树) + - [二叉树的应用]() + - [二叉排序树]() + - [平衡二叉树]() + - [哈夫曼树和哈夫曼编码]() +- [树和森林](ch5/README.md#树与二叉树) + - [树的基本概念](ch5/README.md#1-树的基本概念) + - [树的存储结构](ch5/tree-storage/README.md#树的存储结构) + - [树和森林的遍历](ch5/tree-traversal/README.md#树与森林) + - [树和森林及二叉树的转换](ch5/tree-traversal/README.md#树与森林) + - [树的应用:查并集](ch5/tree-applications/README.md#1-并查集) diff --git a/ch5/README.md b/ch5/README.md index ec5de14..dff0f4c 100644 --- a/ch5/README.md +++ b/ch5/README.md @@ -83,3 +83,40 @@ $n$ 个结点的树中只有 $n-1$ 条边。 - 后序线索二叉树 #### 4.7.2. [线索二叉树的构造](binary-tree-traversal/README.md#72-线索二叉树的构造) + +## 5. [树的存储结构](tree-storage/README.md#树的存储结构) + +- [双亲表示法](tree-storage/README.md#1-双亲表示法) +- [孩子表示法](tree-storage/README.md#2-孩子表示法) +- [孩子兄弟表示法](tree-storage/README.md#3-孩子兄弟表示法) + +| | 优点 | 缺点 | +| -------------- | ------------------------------------------------ | ------------------------ | +| 双亲表示法 | 寻找结点的双亲结点效率高 | 寻找结点的孩子结点效率低 | +| 孩子表示法 | 寻找结点的孩子结点效率高 | 寻找结点的双亲结点效率低 | +| 孩子兄弟表示法 | 寻找结点的孩子结点效率高,方便实现树转换为二叉树 | 寻找结点的双亲结点效率低 | + +## 6. [树与森林](tree-traversal/README.md#树与森林) + +- [树与二叉树的转换](tree-traversal/README.md#1-树与二叉树的转换) +- [森林与二叉树的转换](tree-traversal/README.md#2-森林与二叉树的转换) + +[树的遍历](tree-traversal/README.md#3-树的遍历) + +- [先根遍历](tree-traversal/README.md#31-先根遍历) +- [后根遍历](tree-traversal/README.md#32-后根遍历) +- [层次遍历](tree-traversal/README.md#33-层次遍历) + +[森林的遍历](tree-traversal/README.md#4-森林的遍历) + +- [先序遍历](tree-traversal/README.md#41-先序遍历) +- [中序遍历](tree-traversal/README.md#42-中序遍历) + +| 树 | 森林 | 二叉树 | +| -------- | -------- | -------- | +| 先根遍历 | 先序遍历 | 先序遍历 | +| 后根遍历 | 中序遍历 | 中序遍历 | + +## 7. [树的应用](tree-applications/README.md) + +- [并查集](tree-applications/README.md#1-并查集) diff --git a/ch5/tree-applications/README.md b/ch5/tree-applications/README.md new file mode 100644 index 0000000..9b3eb30 --- /dev/null +++ b/ch5/tree-applications/README.md @@ -0,0 +1,48 @@ +# 树的应用 + +## 1. 并查集 + +一种简单的集合表示。 + +通常用书的双亲表示法作为并查集的存储结构。 + +通常用数组元素的下标代表元素名,用根结点的下标代表子集合名,根结点的双亲结点为负数。 + +- `Initial(S)`:将集合 $S$ 的每个元素都初始化为只有一个单元素的子集合。 +- `Union(S, Root1, Root2)`:将集合 $S$ 的子集合(互不相交) $Root2$ 并入到子集合 $Root1$。 +- `Find(S, x)`:查找集合 $S$ 中单元素 $x$ 所在的子集合,并返回该子集合的名字。 + +![并查集1](and-search-set1.png) + +![并查集2](and-search-set2.png) + +```cpp +#define SIZE 100 +void Initial(int S[]) +{ + for (int i = 0; i < size; i++) + { + S[i] = -1; + } +} +``` + +```cpp +int Find(int S[], int x) +{ + while (S[x] >= 0) + { + x = S[x]; + } + return x; +} +``` + +```cpp +void Union(int S[], int Root1, int Root2) +{ + S[Root2] = Root1; +} +``` + +![并查集3](and-search-set3.png) diff --git a/ch5/tree-applications/and-search-set1.png b/ch5/tree-applications/and-search-set1.png new file mode 100644 index 0000000..abc1f4e Binary files /dev/null and b/ch5/tree-applications/and-search-set1.png differ diff --git a/ch5/tree-applications/and-search-set2.png b/ch5/tree-applications/and-search-set2.png new file mode 100644 index 0000000..ba7d3b2 Binary files /dev/null and b/ch5/tree-applications/and-search-set2.png differ diff --git a/ch5/tree-applications/and-search-set3.png b/ch5/tree-applications/and-search-set3.png new file mode 100644 index 0000000..b1ab5d7 Binary files /dev/null and b/ch5/tree-applications/and-search-set3.png differ diff --git a/ch5/tree-storage/README.md b/ch5/tree-storage/README.md new file mode 100644 index 0000000..c742aba --- /dev/null +++ b/ch5/tree-storage/README.md @@ -0,0 +1,62 @@ +# 树的存储结构 + +## 1. 双亲表示法 + +采用一组连续的存储空间来存储每个结点,同时在每个结点中增设一个伪指针,指示双亲结点在数组的位置。 + +根结点的下标为 $0$,其伪指针域为 $-1$。 + +```cpp +#define MAX_TREE_SIZE 100 +typedef struct +{ + ElemType data; + int parent; +} PTNode; +typedef struct +{ + PTNode nodes[MAX_TREE_SIZE]; + int n; +} PTree; +``` + +![双亲表示法](parental-representation.png) + +## 2. 孩子表示法 + +将每个结点的孩子结点都用单链表链接起来形成一个线性结构,$n$ 个结点具有 $n$ 个孩子链表。 + +```cpp +#define MAX_TREE_SIZE 100 +typedef struct +{ + int child; + struct CNode *next; +} CNode; +typedef struct +{ + ElemType data; + struct CNode *child; +} PNode; +typedef struct +{ + PNode nodes[MAX_TREE_SIZE]; + int n; +} CTree; +``` + +![孩子表示法](child-representation.png) + +## 3. 孩子兄弟表示法 + +以二叉链表作为树的存储结构,又称二叉树表示法。(左孩子右兄弟) + +```cpp +typedef struct CSNode +{ + ElemType data; + struct CSNode *firstchild, *nextsibling; +} CSNode, CSTree; +``` + +![孩子兄弟表示法](child-brother-representation.png) diff --git a/ch5/tree-storage/child-brother-representation.png b/ch5/tree-storage/child-brother-representation.png new file mode 100644 index 0000000..2baa898 Binary files /dev/null and b/ch5/tree-storage/child-brother-representation.png differ diff --git a/ch5/tree-storage/child-representation.png b/ch5/tree-storage/child-representation.png new file mode 100644 index 0000000..de5a486 Binary files /dev/null and b/ch5/tree-storage/child-representation.png differ diff --git a/ch5/tree-storage/parental-representation.png b/ch5/tree-storage/parental-representation.png new file mode 100644 index 0000000..6cf3a51 Binary files /dev/null and b/ch5/tree-storage/parental-representation.png differ diff --git a/ch5/tree-traversal/README.md b/ch5/tree-traversal/README.md new file mode 100644 index 0000000..0505690 --- /dev/null +++ b/ch5/tree-traversal/README.md @@ -0,0 +1,59 @@ +# 树与森林 + +## 1. 树与二叉树的转换 + +- 树转二叉树:每个结点左指针指向它的第一个孩子结点,右指针指向它在树中相邻兄弟结点。 +- 二叉树转树:每个结点都指向他们的双亲结点。 + +## 2. 森林与二叉树的转换 + +- 森林转二叉树:将每一棵树转换为二叉树,将每棵二叉树的根依次作为上一棵二叉树的右子树。 +- 二叉树转森林 + +## 3. 树的遍历 + +按照某种方式访问树中的每个结点,且仅访问一次。 + +### 3.1. 先根遍历 + +若树非空,则先访问根结点,再按从左到右的顺序遍历根结点的每棵子树。 + +**树的先根遍历序列与这棵树的对应二叉树的先序遍历序列相同。** + +![树的先根遍历](tree-preorder-traversal.png) + +### 3.2. 后根遍历 + +若树非空,则先按从左到右的顺序遍历根结点的每棵子树,再访问根结点, + +**树的后根遍历序列与这棵树的对应二叉树的中序遍历序列相同。** + +![树的后根遍历](tree-postorder-traversal.png) + +### 3.3. 层次遍历 + +## 4. 森林的遍历 + +### 4.1. 先序遍历 + +若森林非空,则, + +- 访问森林中第一棵树的根结点 +- 先序遍历第一棵树的子树森林 +- 先序遍历除去第一棵树之后剩余的树构成的子树森林 + +**森林的先序遍历序列与森林对应二叉树的先序遍历序列相同。** + +![森林的中序遍历](forest-preorder-traversal.png) + +### 4.2. 中序遍历 + +若森林非空,则, + +- 中序遍历第一棵树的根结点的子树森林 +- 访问第一棵树的根结点 +- 中序遍历除去第一棵树之后剩余的树构成的子树森林 + +**森林的中序遍历序列与森林对应二叉树的中序遍历序列相同。** + +![森林中序遍历](forest-inorder-traversal.png) diff --git a/ch5/tree-traversal/forest-inorder-traversal.png b/ch5/tree-traversal/forest-inorder-traversal.png new file mode 100644 index 0000000..04306a1 Binary files /dev/null and b/ch5/tree-traversal/forest-inorder-traversal.png differ diff --git a/ch5/tree-traversal/forest-preorder-traversal.png b/ch5/tree-traversal/forest-preorder-traversal.png new file mode 100644 index 0000000..2076cbe Binary files /dev/null and b/ch5/tree-traversal/forest-preorder-traversal.png differ diff --git a/ch5/tree-traversal/tree-postorder-traversal.png b/ch5/tree-traversal/tree-postorder-traversal.png new file mode 100644 index 0000000..056202b Binary files /dev/null and b/ch5/tree-traversal/tree-postorder-traversal.png differ diff --git a/ch5/tree-traversal/tree-preorder-traversal.png b/ch5/tree-traversal/tree-preorder-traversal.png new file mode 100644 index 0000000..432dc9e Binary files /dev/null and b/ch5/tree-traversal/tree-preorder-traversal.png differ