數據結構中樹與圖的常用算法實現
樹
樹
:是N(N>=0)個結點的有限集合。任意一顆非空樹應滿足:
- 有且僅有一個特定的稱爲
根
的結點- 當N>1時,其餘結點可分爲m(m>0)個互不相交的有限集合T1,T2,…,Tm,其中每一個集合本身又是一顆樹,並且稱爲根結點的
子樹
樹適合表示具有層次結構的數據。
結構體
typedef struct Tree{
int data;
Tree *left,*right;
Tree(int x):data(x),left(NULL),right(NULL){}
}TreeNode,*pTreeNode;
樹的遍歷
樹中最重要的算法就是樹的遍歷,分爲前中後
序三種遍歷方法,本文分別用遞歸和非遞歸的方式實現了以上三種遍歷方法以及樹的層次遍歷。
源碼實現
#include <iostream>
#include <stack>
#include <queue>
using namespace std;
//樹的結構體
typedef struct Tree{
int data;
Tree *left,*right;
Tree(int x):data(x),left(NULL),right(NULL){}
}TreeNode,*pTreeNode;
int res[6]={0},i=0; //res輔助讀取樹的內容
//初始化一顆樹
/*1
|-2
| |-4
| |-5
|
|-3
| |-NULL
| |-7
*/
Tree * InitOneTree()
{
pTreeNode pTree7 = new Tree(7);
pTreeNode pTree5 = new Tree(5);
pTreeNode pTree4 = new Tree(4);
pTreeNode pTree3 = new Tree(3);
pTreeNode pTree2 = new Tree(2);
pTreeNode pTree1 = new Tree(1);
pTree1->left = pTree2;
pTree1->right = pTree3;
pTree2->left = pTree4;
pTree2->right = pTree5;
pTree3->right = pTree7;
return pTree1;
}
//存儲結果
void visit(pTreeNode root){
res[i++] = root->data;
}
//遞歸 前序遍歷
void PreOrder(pTreeNode root)
{
if(root)
{
visit(root);
PreOrder(root->left);
PreOrder(root->right);
}
}
//遞歸 中序遍歷
void InOrder(pTreeNode root)
{
if(root)
{
InOrder(root->left);
visit(root);
InOrder(root->right);
}
}
//遞歸 後序遍歷
void PostOrder(pTreeNode root)
{
if(root)
{
PostOrder(root->left);
PostOrder(root->right);
visit(root);
}
}
//非遞歸 前序遍歷
void PreOrder2(pTreeNode root)
{
pTreeNode pTmp = root;
stack<pTreeNode>mStack;
mStack.push(pTmp);
while(!mStack.empty())
{
pTmp = mStack.top();
mStack.pop();
visit(pTmp);
if(pTmp->right)
mStack.push(pTmp->right);
if(pTmp->left)
mStack.push(pTmp->left);
}
}
//非遞歸 中序遍歷
void InOrder2(pTreeNode root)
{
stack<pTreeNode>mStack;
pTreeNode pTmp = root;
while(pTmp||!mStack.empty())
{
if(pTmp)
{
mStack.push(pTmp);
pTmp = pTmp->left;
}else{
pTmp = mStack.top();
mStack.pop();
visit(pTmp);
pTmp = pTmp->right;
}
}
}
//非遞歸 後序遍歷
//前序遍歷 根->左->右 變成 根->右->左 結果再reverse
void PostOrder2(pTreeNode root)
{
pTreeNode pTmp = root;
stack<pTreeNode>mStack;
mStack.push(pTmp);
while(!mStack.empty())
{
pTmp = mStack.top();
mStack.pop();
visit(pTmp);
if(pTmp->left)
mStack.push(pTmp->left);
if(pTmp->right)
mStack.push(pTmp->right);
}
//結果reverse
for(int i=0; i<5; i++) //6個數需要搬5趟
{
//先將最後的一個數放到當前第一個位置上
int tmp = res[i];
res[i] = res[5];
//從後面開始賦值
for(int j=5-1;j>i;j--)
res[j+1] = res[j];
res[i+1] = tmp;
}
}
//非遞歸 層次遍歷 即廣度優先
void LevelOrder(pTreeNode root)
{
queue<pTreeNode>mQueue;
mQueue.push(root);
while(!mQueue.empty())
{
pTreeNode pTmp = mQueue.front();
mQueue.pop();
visit(pTmp);
if(pTmp->left)
mQueue.push(pTmp->left);
if(pTmp->right)
mQueue.push(pTmp->right);
}
}
int main()
{
pTreeNode pTree = InitOneTree();
i=0;
//PreOrder(pTree);
//InOrder(pTree);
//PostOrder(pTree);
//PreOrder2(pTree);
//InOrder2(pTree);
//PostOrder2(pTree);
LevelOrder(pTree);
for(int j=0;j<6;j++)
cout<<res[j]<<" ";
cout<<endl;
}
圖
圖G
:由頂點集V和邊集E組成,記爲G=(V,E),其中V(G)表示圖G中頂點的有限非空集;E(G)表示圖G中頂點之間的關係(邊)集合。
因爲圖的構造比較複雜,所以主要理解原理即可。
重點掌握內容
- 圖的概念
- 圖的存儲(圖的表示)
鄰接矩陣法(二維數組)、鄰接表(數組+鏈表)、十字鏈表(不要求)、鄰接多重表(不要求)
鄰接矩陣會比較浪費空間,而鄰接表則可以克服這個問題- 圖的遍歷
廣度優先搜索(BFS-藉助隊列)、深度優先搜索(DFS-類似於樹的前序遍歷、遞歸)- 圖的應用(算法的應用-考點)
最小生成樹
:1.普利姆(Prim)算法 從已有集合中選取最小未到達頂點的邊;2.克魯斯卡爾(Kruskal)算法 一直選取最小邊
最短路徑
:單源最短路徑-迪傑斯特拉(Dijkstra)算法 類似Prim算法 每趟得到一個最短路徑
拓撲排序
:AOV網中得到一個拓撲排序,意義:確定事件先後順序
的算法
關鍵路徑
:AOE網中得到一個關鍵路徑,意義:確定並行事件中的關鍵活動
的算法