数据结构图
图这种数据结构体接触的很少, 在这里记录一下最简单的无向图, 以及其相关的Breadth First Search,Depth First Search算法
这里使用邻接表实现
邻接表
邻接表.gif
在代码中的表现就比如这样, 每一行存储一个顶点连接的其他顶点(特别适用于稀疏图)
0 -> 1 5 2 3
1 -> 0 8 4 5
2 -> 0 4 5 6
3 -> 0 7 8 5
4 -> 2 1 7 9
5 -> 0 2 1 3 6 8
6 -> 5 2
7 -> 3 4 8 9
8 -> 1 3 5 7
9 -> 4 7
这里是我在C++中随机生成的一个图, 在C++中就表现为 vector>这样的二维"数组", 每一行表示一个vector中的数据
代码
这里就不做太详细的介绍了, 后面简单介绍一下算法思想
#include
#include
#include
#include
static void path_print(std::vector &prev, int start, int target)
{
if(start != target && prev[target] != -1)
{
path_print(prev, start, prev[target]);
}
printf("%d ", target);
}
class Graph
//无向图,使用邻接表表示
{
public:
Graph(int v):adj(v)
{
vertex = v;
}
void addEdge(int s, int t)
{
adj[s].push_back(t);
adj[t].push_back(s);
}
void print()
{
for(int i = 0; i < adj.size(); i++)
{
printf("%d ->\t", i);
for(auto b : adj[i])
{
printf("%d\t", b);
}
printf("\n");
}
}
/*Breadth First Search, search the shortest path
* 假设点数为V 边数为E, 那么时间复杂度O(V+E), 最多每个边和每个顶点被访问一次
* 需要使用的数组队列, 其长度不会超过V, 空间复杂的为O(V)
* */
bool bsf(int s, int t)
{
if(s == t) return false;
std::queue queue;
std::vector visited(vertex, false);
std::vector prev;
queue.push(s);
prev.push_back(s);
visited[s] = true;
while (queue.size())
{
int index = queue.front();
queue.pop();
for(auto c : adj[index])
{
if(t == c)
{
printf("print bfs result path:\n");
path_print(prev, s, t);
printf("\n");
return true;
}
else if(!visited[c])
{
prev[c] = index;
visited[c] = true;
queue.push(c);
}
}
printf("\n");
}
printf("can't find the path form %d -> %d\n", s, t);
return false;
}
/* Depth First Search
* 空间复杂度为O(V), 时间复杂度为O(E)
* */
bool dfs(int s, int t)
{
if(s == t)
{
return true;
}
std::vector prev(vertex, -1);
std::vector visited(vertex, false);
visited[s] = true;
if(recurDfs(s, t, visited, prev))
{
printf("print dfs result path:\n");
path_print(prev, s, t);
printf("\n");
return true;
}
else
{
return false;
}
}
bool recurDfs(int start, int target, std::vector &visited, std::vector &prev)
{
if(start == target)
{
return true;
}
else
{
for(auto c : adj[start])
{
if(!visited[c])
{
prev[c] = start;
visited[c] = true;
if(recurDfs(c, target, visited, prev))
{
return true;
}
}
}
return false;
}
}
private:
int vertex;
std::vector<:vector>> adj;
};
/*
* create a Random Graph with n vertex and e edge
* max e = (v-1)v/2 min e = v - 1
* */
static void Random_Graph(Graph &graph, int v, int e)
{
srand(time(NULL));
e = e > (v-1)*v/2 ? (v-1)*v/2 : e;
e = e < (v-1) ? (v - 1) : e;
std::vector<:vector>> tmp(v, std::vector(v, 0));
for(int i = 0; i < v; i++)
{
while(true)
{
int k = rand() % v;
if(k != i)
{
tmp[i][k] = 1;
e--;
break;
}
}
}
//确保每一个点都有一条边
while(e > 0)
{
int i = rand() % v;
int j = rand() % v;
if(i != j && !tmp[i][j] && !tmp[j][i])
{
tmp[i][j] = 1;
e--;
}
}
for(int i = 0; i < v; i++)
{
for(int j = 0; j < v; j++)
{
if(i != j && tmp[i][j])
{
tmp[j][i] = 0;
graph.addEdge(i, j);
}
}
}
graph.print();
}
int main() {
Graph gra(10);
Random_Graph(gra, 10, 20);
gra.bsf(4, 5);
gra.dfs(4, 5);
system("pause");
return 0;
}
实现思想
BFS(广度优先)
std::queue queue;
/*记录将要遍历其下一级的顶点,使用队列先进先出,*/
std::vector visited(vertex, false);
/*避免重复进行, 用以判断是否曾经遍历过*/
std::vector prev;
/*用于打印, val表示index对应顶点的上一级*/
DFS(深度优先)
采用递归的方式实现
时间空间复杂度
假设顶点数为V, 边数为E
对于BFS, 每个顶点每个边最多可能经过一次, 时间复杂的为o(V+E), 所需要的一些临时空间其大小不超过顶点数, 空间复杂度为O(V)
对于DFS, 每个边最多被访问两次(递归/回退), 时间复杂度为O(E), 空间复杂度还是O(V)