多对多的数据结构-图结构
图结构的两个概念:
顶点:就像链表的节点树的子树 存数据的节点
边:用来描述顶点之间的关系
如图:有6个顶点和7条边
如果边有方向叫有向图,没有则叫无向图(上面的就是)。
如图为有向图:
有向图:单行线
无向图:
描述图的方式有很多种
这里搞2种:
临接表:链表
临接矩阵:数组
无向图临接表的表现如图:
有向图临接表的表现如图:
无向图临接矩阵如图:
是对称的,0是不可以,1是可以
有向图临接矩阵如图:
是对称的,0是不可以,1是可以,是从竖的到横的来看的,A能不能到B,A能不能到C。
特殊的图:
网图:
不一样的是边有长度(权值):无限大表示自己(也可以用没有出现过的值),0表示到不了
用临接表来表示的话,只需要加一个长度(权值)即可。
就比如A星寻路算法里面的f=g+h+w中的w,权值。
就比如地图是用图结构的,那么所谓的寻路算法就是遍历。
图的遍历:
深度优先遍历(Depth First Search, 简称 DFS) :一路走到底,没有了在换其他周围节点
广度优先遍历(Breath First Search, 简称 BFS):先把周围遍历完,然后在向下一个搜索
而做这两个遍历,首先需要做的是两个函数:
找某个顶点的第一个相邻顶点
找某个顶点的第二个相邻顶点
源码:
#include<iostream>
#include<stdio.h>
#include<queue>//队列
using namespace std;
//临接矩阵描述图
#define VERTICES 6
#define EDGES 7
//图类型
struct Tu
{
int VerticesNum;//顶点个数
int EdgesNum; //边 条数
char* pVertices;//指向 存储所有顶点的一维数组
int** pEdges; //二级指针 描述矩阵
};
//创建图对象
Tu* createTu();
//初始化图对象
void initTu(Tu* graph,int vertices,int edges,char* pv,int map[VERTICES][VERTICES]);
//显示图对象
void showGraph(Tu* graph);
//从图中找到参数顶点在顶点数组中的下标并返回
int _getIndex(Tu* graph, char c);
//找某个顶点的第一个相邻顶点,找到返回下标,没有则-1 标记有没有找过
int findFirstVertics(Tu* graph, int beg, bool isFind[VERTICES]);
//找某个顶点的第二个相邻顶点,找到返回下标,没有则-1 标记有没有找过
int findSecondVertics(Tu* graph, int beg, bool isFind[VERTICES]);
//深度遍历
void DFS(Tu* graph, char beg, bool isFind[VERTICES]);
//广度遍历
void BFS(Tu* graph, char beg, bool isFind[VERTICES]);
//方便遍历 通过bool用广度或者深度
void travel(Tu* graph, char beg, bool isD = true);
int main()
{
Tu* g = createTu();
char buff[7] = "ABCDEF";
int map[VERTICES][VERTICES] = {
{0,1,0,1,0,0 },
{1,0,1,0,0,0 },
{0,1,0,1,1,1 },
{1,0,1,0,0,1 },
{0,0,1,0,0,0 },
{0,0,1,1,0,0 }
};
initTu(g, VERTICES, EDGES, buff, map);
showGraph(g);
//travel(g, 'A');
travel(g, 'A',false);
while (1){}
return 0;
}
//创建图对象
Tu* createTu()
{
Tu* pNew = new Tu;
if (NULL==pNew)
{
cout << "创建图对象失败";
return NULL;
}
memset(pNew, 0, sizeof(Tu));
return pNew;
}
//初始化图对象
void initTu(Tu* graph, int vertices, int edges, char* pv, int map[VERTICES][VERTICES])
{
#if 1 //测试用
//开内存
graph->VerticesNum = vertices;
graph->EdgesNum = edges;
graph->pVertices = new char[graph->VerticesNum];
graph->pEdges = new int* [graph->VerticesNum];//因为是二级,所以开二次
for (int i = 0; i < graph->VerticesNum; i++)
{
graph->pEdges[i] = new int[graph->VerticesNum];
}
//数据拷贝
memcpy(graph->pVertices, pv, sizeof(char) * graph->VerticesNum);
for (int i = 0; i < graph->VerticesNum; i++)
{
memcpy(graph->pEdges[i], map[i], sizeof(int) * graph->VerticesNum);
}
#else
//开内存
graph->VerticesNum = vertices;
graph->EdgesNum = edges;
graph->pVertices = new char[graph->VerticesNum];
graph->pEdges = new int* [graph->VerticesNum];//因为是二级,所以开二次
for (int i = 0; i < graph->VerticesNum; i++)
{
graph->pEdges[i] = new int[graph->VerticesNum];
memset(graph->pEdges[i], 0, sizeof(int) * graph->VerticesNum);
}
cout << "请输入顶点:";
cin >> graph->pVertices;//输入的时候自己把握,不要越界
char buff[5] = { 0 };
char src, dst;//起始和结束
int srcIdx, dstIdx;//坐标
for (int i = 0; i < graph->EdgesNum; i++)
{
cout << "请输入第" << i + 1 << "条边:";
cin >> buff;//怎么输入:A->B
src = buff[0];
dst = buff[3];
srcIdx = _getIndex(graph, src);
dstIdx = _getIndex(graph, dst);
graph->pEdges[srcIdx][dstIdx] = 1;
#if 1 //如果是无向图,加这一行
graph->pEdges[dstIdx][srcIdx] = 1;
#endif // 1
}
#endif //0
}
//显示图对象
void showGraph(Tu* graph)
{
for (int i = 0; i <=graph->VerticesNum; i++)
{
for (int j = 0; j <=graph->VerticesNum; j++)
{
if (0==i && 0==j)//角落
{
cout << " ";
}
else if(0==i)//第一行
{
cout << graph->pVertices[j - 1] << " ";
}
else if (0 == j)//第一列
{
cout << graph->pVertices[i - 1]<<" ";
}
else//对应数字
{
cout << graph->pEdges[i-1][j-1] << " ";
}
}
cout << endl;
}
}
int _getIndex(Tu* graph, char c)
{
for (int i = 0; i < graph->VerticesNum; i++)
{
if (c==graph->pVertices[i])
{
return i;
}
}
return -1;
}
int findFirstVertics(Tu* graph, int beg, bool isFind[VERTICES])
{
for (int i = 0; i < graph->VerticesNum; i++)
{
if (isFind[i])
{
continue;
}
if (1==graph->pEdges[beg][i])
{
return i;
}
return -1;
}
}
int findSecondVertics(Tu* graph, int beg, bool isFind[VERTICES])
{
for (int i = beg +1; i < graph->VerticesNum; i++)
{
if (isFind[i])
{
continue;
}
if (1 == graph->pEdges[beg][i])
{
return i;
}
return -1;
}
}
void DFS(Tu* graph, char beg, bool isFind[VERTICES])
{
//找beg的第一个相邻顶点x,继续找x的第一个相邻顶点y,直到没找到,结束
int currentIdx=_getIndex(graph,beg);//当前下标 字符转下标的函数
isFind[currentIdx] = 1;
int nextIdx = findFirstVertics(graph, currentIdx, isFind);
while (1)
{
if (-1== nextIdx)
{
break;
}
if (false==isFind[nextIdx])
{
cout << graph->pVertices[nextIdx]<<" ";
DFS(graph, graph->pVertices[nextIdx], isFind);//递归
}
//找过了就找下一个
nextIdx = findFirstVertics(graph, currentIdx, isFind);
}
}
void BFS(Tu* graph, char beg, bool isFind[VERTICES])
{
queue<int> q;
q.push(_getIndex(graph, beg));//把第一个放到队列里面
isFind[_getIndex(graph, beg)] = true;//标记
int headIdx;//做队列头
int idx;//临时下标
while (!q.empty())//在队列不是空的情况下
{
headIdx = q.front();//第一个
q.pop();//删第一个
//把第一个相邻的顶点都放到队列中
idx = findFirstVertics(graph, headIdx, isFind);
while (1)
{
if (-1 == idx)
{
break;
}
if (!isFind[idx])
{
isFind[idx] = true;
cout << graph->pVertices[idx] << " ";
q.push(idx);
}
//找过了就找下一个
idx = findFirstVertics(graph, headIdx, isFind);
}
}
}
void travel(Tu* graph, char beg,bool isD )
{
bool isFind[VERTICES] = { 0 };
if (isD)
{
cout << "深度优先遍历: " << beg<<" ";
DFS(graph, beg, isFind);
cout << endl;
}
else
{
cout << "广度优先遍历: " << beg << " ";
BFS(graph, beg, isFind);
cout << endl;
}
}