樹及二叉樹:
樹:(數據結構中常見的樹)
樹的定義
樹的存儲:下面介紹三種不同的樹的表示法:雙親表示法,、孩子表示法,、孩子兄弟表示法。
- 雙親表示法
我們假設以一組連續空間存儲樹的結點,同時在每個結點中,附設一個指示器指向其雙親結點到鏈表中的位置。也就是說每個結點除了知道自己之外還需要知道它的雙親在哪里。
它的結構特點是如圖所示:

以下是我們的雙親表示法的結構定義代碼:
-
/*樹的雙親表示法結點結構定義 */ #define MAXSIZE 100 typedef int ElemType; //樹結點的數據類型,暫定為整形 typedef struct PTNode //結點結構 { ElemType data; //結點數據 int parent; //雙親位置 }PTNode; typedef struct { PTNode nodes[MAXSIZE]; //結點數組 int r,n; //根的位置和結點數 }PTree;
-
孩子表示法
-
換一種不同的考慮方法。由於每個結點可能有多棵子樹,可以考慮使用多重鏈表,即每個結點有多個指針域,其中每個指針指向一棵子樹的根結點,我們把這種方法叫做多重鏈表表示法。不過樹的每個結點的度,也就是它的孩子個數是不同的。所以可以設計兩種方案來解決。
方案一:
一種是指針域的個數就等於樹的度(樹的度是樹的各個結點度的最大值)
其結構如圖所示:

不過這種結構由於每個結點的孩子數目不同,當差異較大時,很多結點的指針域就都為空,顯然是浪費空間的,不過若樹的各結點度相差很小時,那就意味着開辟的空間都被利用了,這時這種缺點反而變成了優點。
方案二:
第二種方案是每個結點指針域的個數等於該結點的度,我們專門取一個位置來存儲結點指針域的個數。
其結構如圖所示:

這種方法克服了浪費空間的缺點,對空間的利用率是很高了,但是由於各個結點的鏈表是不相同的結構,加上要維護結點的度的數值,在運算上就會帶來時間上的損耗。
能否有更好的方法呢,既可以減少空指針的浪費,又能是結點結構相同。
說到這大家肯定就知道是有的麥,那就是孩子表示法。
具體辦法是,把每個結點的孩子排列起來,以單鏈表做存儲結構,則n個結點有n個孩子鏈表,如果是葉子結點則此單鏈表為空。然后n個頭指針有組成一個線性表,采用順序存儲結構,存放進入一個一維數組中。
為此,設計兩種結點結構,
一個是孩子鏈表的孩子結點,如下所示:

其中child是數據域,用來存儲某個結點在表頭數組中的下標。next是指針域,用來存儲指向某結點的下一個孩子結點的指針。
另一個是表頭結點,如下所示:

其中data是數據域,存儲某結點的數據信息。firstchild是頭指針域,存儲該結點的孩子鏈表的頭指針。
以下是孩子表示法的結構定義代碼:
/*樹的孩子表示法結點結構定義 */ #define MAXSIZE 100 typedef int ElemType; //樹結點的數據類型,暫定為整形 typedef struct CTNode //孩子結點 { int child; struct CTNode *next; }*ChildPtr; typedef struct //表頭結構 { ElemType data; ChildPtr firstchild; }CTBox; typedef struct //樹結構 { CTBox nodes[MAXSIZE]; //結點數組 int r,n; //根結點的位置和結點數 }CTree;
3、孩子兄弟表示法
我們發現,任意一顆樹,它的結點的第一個孩子如果存在就是的,它的右兄弟如果存在也是唯一的。因此,我們設置兩個指針,分別指向該結點的第一個孩子和此結點的右兄弟。
其結點結構如圖所示:

以下是孩子兄弟表示法的結構定義代碼:
/*樹的孩子兄弟表示法結構定義 */ typedef struct CSNode { ElemType data; struct CSNode *firstchild, *rightsib; }CSNode, *CSTree;
二叉樹:
二叉樹的定義就不贅述了,這里提供二叉樹幾種常見的算法:
- 建樹(根據數組建樹)
- 先、中、后序遍歷二叉樹
- 層序遍歷二叉樹
- 求二叉樹的寬度、高度
- 判斷一棵樹是否為另一克二叉樹的子樹
- 根據前序、中序遍歷重建二叉樹
#include <iostream> #include <vector> #include <queue> using namespace std; struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int val) :val(val) {}; }; //建立二叉樹 void CreateTree(TreeNode* &T,int a[],int len,int index) { if (index > len) return; TreeNode* root = new TreeNode(a[index]); root->left = NULL; root->right = NULL; CreateTree(root->left, a, len, index * 2 + 1); CreateTree(root->right, a, len, index * 2 + 2); } TreeNode* CreateTree_2(int a[], int len, int index) { if (index > len) return NULL; TreeNode* root = new TreeNode(a[index]); root->left = CreateTree_2(a, len, 2 * index + 1); root->right = CreateTree_2(a, len, 2 * index + 2); return root; } //先序遍歷 void PreOrder(TreeNode* root) { if (!root) return; else { cout << root->val; PreOrder(root->left); PreOrder(root->right); } return; } //中序遍歷 void InOrder(TreeNode* root) { if (!root) return; else { InOrder(root->left); cout << root->val; InOrder(root->right); } return; } //后序遍歷 void PostOrder(TreeNode* root) { if (!root) return; else { PostOrder(root->left); PostOrder(root->right); cout << root->val; } return; } //層序遍歷並計算寬度 int LevelOrder(TreeNode* root) { if (root == NULL) return; queue<TreeNode*> q1; q1.push(root); int cursize = 1,maxsize=1; while (!q1.empty()) { TreeNode* tmp = q1.front(); q1.pop(); cursize--; cout << tmp->val; if (tmp->left != NULL) q1.push(tmp->left); if (tmp->right != NULL) q1.push(tmp->right); if (cursize == 0) { cursize = q1.size(); maxsize = cursize > maxsize ? cursize : maxsize; } } return maxsize; } //計算樹的高度 int HeightTree(TreeNode* root) { if (root == NULL) return 0; int left = 0, right = 0; if (root->left != NULL) left = HeightTree(root->left) + 1; if (root->right != NULL) right = HeightTree(root->right) + 1; return left > right ? left : right; } //根據前序和中序重建二叉樹 TreeNode* RebuildTree(vector<int>& preorder, vector<int>::iterator& i, vector<int>& inorder, vector<int>::iterator start, vector<int>::iterator end) { if (preorder.size() == 0 || inorder.size() == 0 || start == end) return NULL; vector<int>::iterator j = find(start, end, *i); if (j == end) return NULL; TreeNode* root = new TreeNode(*i++); root->left = RebuildTree(preorder, i, inorder, start, j); root->right = RebuildTree(preorder, i, inorder, j + 1, end); return root; } TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { auto i = preorder.begin(); return RebuildTree(preorder, i, inorder, inorder.begin(), inorder.end()); } //
圖
關於圖的搜索有兩種:廣度優先(bfs)深度優先 (dfs)。
以下圖為例:
深度優先的基本思想簡單說就是搜到底,重新搜。從v0為起點進行搜索,如果被訪問過,則做一個標記,直到與v0想連通的點都被訪問一遍,如果這時,仍然有點沒被訪問,可以從中選一個頂點,進行再一次的搜索,重復上述過程,所以深度優先的過程也是遞歸的過程。
深度優先訪問的結果是:0->1->3->7->4->2->5->6
廣度優先的基本思想是,從頂點v0出發,訪問與v0相鄰的點,訪問結束后,再從這些點出發,繼續訪問,直到訪問結束為止。標記被訪問過的點同深搜一下,不過,廣度優先一般需要用到隊列。
上圖廣度優先的結果:0->1->2->3->4->5->6->7
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<stack> #define maxn 100 using namespace std; typedef struct { int edges[maxn][maxn]; //鄰接矩陣 int n; //頂點數 int e; //邊數 }MGraph; bool vis[maxn]; //標記頂點是否被訪問過 void createGraph(MGraph &G) //用引用做參數 { int i,j; int s,t; //存儲頂點的編號 int v; //存儲邊的權值 for(i=0;i<G.n;i++) //初始化 { for(j=0;j<G.n;j++) { G.edges[i][j] = 0; } vis[i] = false; } for(i = 0;i<G.e;i++) //對鄰接矩陣的邊賦值 { scanf("%d%d%d",&s,&t,&v); //輸入邊頂點的編號以及權值 G.edges[s][t] = v; } } void dfs(MGraph G,int v) { int i; printf("%d ",v); //訪問結點v vis[v] = true; for(int i = 0;i<G.n;i++) //訪問與v相鄰且未被訪問過的結點 { if(G.edges[v][i]!=0&&vis[i]==false) { dfs(G,i); } } } void dfs1(MGraph G,int v) //非遞歸實現(用到了棧,其實在遞歸的實現過程,仍是用到了棧,所以。。。) { stack<int> s; printf("%d",v); //訪問初始的結點 vis[v]=true; s.push(v); //入棧 while(!s.empty()) { int i,j; i=s.top(); //取棧頂頂點 for(j=0;j<G.n;j++) //訪問與頂點i相鄰的頂點 { if(G.edges[i][j]!=0&&vis[j]==false) { printf("%d ",j); //訪問 vis[j]=true; s.push(j); //訪問完后入棧 break; //找到一個相鄰未訪問的頂點,訪問之后跳出循環 } } if(j==G.n) //如果與i相鄰的頂點都被訪問過,則將頂點i出棧 s.pop(); } } void bfs(MGraph G,int v) { queue<int> Q; printf("%d",v); vis[v] = true; Q.push(v); while(!Q.empty()) { int i,j; i = Q.front(); //取隊首頂點 Q.pop(); for(j=0;j<G.n;j++) //廣度遍歷 { if(G.edges[i][j]!=0&&vis[j]==false) { printf("%d",j); vis[j]=true; Q.push(j); } } } } int main() { int n,e; //建圖的頂點數和邊數 while(scanf("%d%d",&n,&e)==2&&n>0) { MGraph G; G.n = n; G.e = e; createGraph(G); dfs(G,0); printf("\n"); // dfs1(G,0); // printf("\n"); // bfs(G,0); // printf("\n"); } return 0; } /*測試數據: 8 9 0 1 1 1 3 1 1 4 1 3 7 1 4 7 1 0 2 1 2 5 1 5 6 1 2 6 1 */
