数据结构中树与图的常用算法实现
树
树
:是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网中得到一个关键路径,意义:确定并行事件中的关键活动
的算法