【数据结构-树】1.树与森林(树的遍历、树的存储方法、并查集的实现)
樹的定義
樹是一種數據結構,它是由 n(n>=1)n(n>=1)n(n>=1) 個有限結點組成一個具有層次關系的集合。把它叫做 “樹” 是因為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。它具有以下的特點
樹適合于表示具有層次的數據。樹中的某個結點(除了根結點)最多只和上一層的一個結點(及其父結點)有直接關系,根結點沒有直接上層結點,因此在n個結點的樹中有n-1條邊。
而樹中每個結點與其下一層的零個或多個結點(及其子女結點)有直接關系。
基本術語
以下概念不要求去記,理解概念即可,腦中要有圖
祖先結點:根結點10到結點3的唯一路徑上的任意結點,如結點10是結點3的祖先結點
子孫結點:如結點3是結點10的子孫結點
雙親結點:路徑上最靠近結點3的結點,如5是3的雙親結點(注意,根結點是沒有雙親結點的,如結點10)
孩子結點:如結點3是結點5的孩子結點
兄弟結點:有相同雙親的結點,如結點3和結點7有共同的雙親結點5,所以結點3和結點7是兄弟結點
結點的度:樹中一個結點的子結點個數
樹的度:樹中結點最大度數
分支結點:度大于0的結點
葉子結點:度為0的結點
結點的層次:從樹根開始定義,根節點為第1層,它的子結點為第2層,以此類推
結點的深度:從根結點開始自頂向下逐層累積的
結點的高度:從也結點開始自底向上逐層累積的
樹的高度:樹中結點的最大層數
有序樹:樹中結點的字數從左到右是有次序的,不能交換
無序樹:不是有序樹,就是無序樹
路徑:樹中兩個結點之間的所經過的結點序列構成的
路徑長度:路徑上所經過邊的個數
森林:由m(m≥0)m(m≥0)m(m≥0) 棵互不相交的樹的集合。(樹把根結點去掉就變成了森林,給n棵獨立的樹加上一個結點,并把這n棵樹變成該結點的子樹,則森林變成了樹)
樹的性質
樹有這么幾最基本的性質
樹的存儲結構
樹的存儲方式有很多種,既可以采用順序存儲,也可以采用連式存儲。下面介紹3中常用的存儲結構。下圖為使用的示例樹形結構。
1. 雙親表示法
這種存儲方式采用一組連續空間來存儲每個結點,同時在每個結點中增設一個偽指針,指示其雙親結點在數組中的位置。
| parent | -1 | 0 | 0 | 0 | 1 | 1 | 3 | 6 | 6 | 6 |
| data | R | A | B | C | D | E | F | G | H | K |
解釋:R是根結點,沒有雙親結點,所以parent為-1;A,B,C是結點R的子節點,所以A,B,C的parent均為R的位置,也就是0;D,E是結點A的子節點,所以D,E的parent均為A的位置,也就是1;B沒有子結點;F是結點C的子節點,所以F的parent為C的位置,也就是3;G,H,K是結點F的子節點,所以G,H,K的parent均為F的位置,也就是6
其存儲結構可以是
struct TreeNode{int pos;char value;int parent;TreeNode *next; }這種存儲結構的優點 :很快找到每個結點的雙親結點
缺點 :如果要求結點的孩子時,需要遍歷整個結構
2. 孩子表示法
孩子表示法是將每個結點的孩子結點都用單鏈表接起來,形成一個線性結構,此時n個結點就有n個孩子鏈表
| 0 | R | 1 | 2 | 3 | - |
| 1 | A | 4 | 5 | - | |
| 2 | B | - | |||
| 3 | C | 6 | - | ||
| 4 | D | - | |||
| 5 | E | - | |||
| 6 | F | 7 | 8 | 9 | - |
| 7 | G | - | |||
| 8 | H | - | |||
| 9 | K | - |
解釋:A,B,C是結點R的子節點,所以結點R子節點的位置為1,2,3;D,E是結點A的子節點,結點A的子結點位置為4,5;B沒有子結點;F是結點C的子節點,C的子結點位置為6;G,H,K是結點F的子節點,F的子結點位置是7,8,9
其存儲結構可以是
struct TreeNode{int pos;char value;vector<int> child;TreeNode *next; }這種存儲結構的優點 :很快找到每個結點的結點
缺點 :如果要求結點的雙親時,需要遍歷整個結構
3. 孩子兄弟表示法
孩子兄弟表示法又稱二叉樹表示法,即以二叉鏈表作為樹的存儲結構。
孩子兄弟表示法是每個結點包括三部分內容:結點值、指向結點第一個孩子結點的指針,及指向結點下一個兄弟結點的指針
樹與森林的遍歷
樹的遍歷
樹的遍歷主要有先根遍歷、后根遍歷和層次遍歷(使用的示例樹形結構如下圖)
先根遍歷:若樹非空,則先訪問根節點,再按從左到右的順序遍歷根結點的每棵子樹。如上圖:RADEBCFGHK
后根遍歷:若樹非空,按從左到右的順序遍歷根結點的每棵子樹,再訪問根節點。如上圖:DEABGHKFCR
void treeLastOrder(TreeNode *node){if(node==NULL) return;for(int i = 0; i< node.child.size(); i++) {treePreOrder(node.child[i]);}printf("%d",node->value);return; }層次遍歷:
void treeLayerOrder(TreeNode *node){queue<int> q;q.push_back(node);while(!q.empty()){TreeNode *root = q.front();q.pop();printf("%d",node->value);for(int i = 0; i< node.child.size(); i++) {q.push_back(node.child[i]);}}return; }森林遍歷
1. 先序遍歷森林
2. 中序遍歷森林
并查集的應用
并查集是一種樹型的數據結構,用于處理一些不相交集合的合并及查詢問題(森林→樹)。使用雙親表示法
原始的 parent 列表
| 結點的值 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 雙親結點 | -1 | 0 | 0 | 0 | -1 | 4 | 4 | -1 | 7 | 7 |
合并后parent 列表
| 結點的值 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 雙親結點 | -1 | 0 | 0 | 0 | 0 | 4 | 4 | -1 | 7 | 7 |
并查集支持三種操作:1. 初始化,2. 尋找集合,3. 合并集合
// 初始化 void initialise(int parent[], int len) {for(int i = 0; i < len; i++) {// 記錄當前結點的雙親結點parent[i] = -1;// 記錄當前樹高rank[i] = 1;}return; } // 尋找x的根 int find_node(int x, int parent[]) {int x_node = x;while(parent[x_node]!=-1) {x_node = parent[x_node];}return x_node; } // 合并,在合并的時候,要注意樹的高度,應該把樹低的樹合并到樹高的樹中 int union(int x, int y, int parent[]) {int x_node = find_node(x,parent);int y_node = find_node(y,parent);// 在同一個集合里if(x_node==y_node) {return 0;} eles {// 不在同一個集合里if(rank[x_node] > rank[y_node]) {parent[y_node] = x_node;} else (rank[x_node] < rank[y_node]) {parent[x_node] = y_node} else {parent[x_node] = y_node; // x_node的父節點在y_node的位置里rank[y_node]++;}return 1;} }總結
以上是生活随笔為你收集整理的【数据结构-树】1.树与森林(树的遍历、树的存储方法、并查集的实现)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【数据结构-查找】4.五千字干活长文带你
- 下一篇: 【数据结构-树】2.二叉树遍历与线索二叉