目录
一、概念
深度、广度搜索是图中遍历搜索的两种算法。
深度搜索DFS:一直往下查找,直到到头走不动了,往回走上一个分支,在该分支内一直往下走,循环这样走。
对下左图进行如右图的深度搜索:从顶点A开始遍历,接着遍历A的第一个邻接顶点继续从B开始遍历B的第一个邻接顶点C,接着从C始遍历C的第一个邻接顶点D...直到遍历到F,F的第一个临界顶点已经被遍历,则遍历F的下一个邻接顶点G,接着从G的第一个邻接顶点遍历。
假设最外圈是第一个临界顶点(图里面先假设,而代码具体在创建时要根据邻接表的存储顺序来看)A->B->C->D->E->F->G->H(此时H的临界结点都被访问过,回到G的位置,G的邻接结点也都被访问过了,回到F....一直回到了D,开始访问D下一个未被放过的结点)->I
广度搜索BFS:所有只走一次,要标记被访问过的结点。
对下左图进行广度搜索: 从顶点A开始遍历,接着遍历A的第一个邻接顶点B继续从B开始遍历B的第一个邻接顶点C,接着从C开始遍历C的第一个邻接顶点D.直到遍历到F,F的第一个临界顶点已经被遍历,则遍历F的下一个邻接顶点G,接着从G的第一个邻接顶点遍历
A->B->F,对A为访问过的邻接结点一直入队,直到都入进去
B->C->I->G,当A的邻接结点都入队后,判断队列是否为空,队列不空,将队头元素B出队,开始依次对队头元素的邻接结点入队
F->E,当B的邻接结点都入队后,判断队列是否为空,队列不空,将队头元素F出队,开始依次对队头元素的邻接结点入队
.....
直到队为空,结束。
总结:
- 深度是到达一个顶点后就开始往下走,一条路走到黑(就像走迷宫一样,把一条路走到死)
- 广度:到达一个顶点后把与该顶点相连的所有结点都访问一下
- 深度 -> 递归,类似于树的前序遍历
- 广度 -> 非递归,类似于树的前序层遍历
二、C++实现代码
BFS
void Graph::BFS(char v)
{
queue<int>qq;
bool* visited = new bool[m_NumVertex];
int front;
Edge* p = NULL;
for (int i = 0; i < m_NumVertex; i++)
{
visited[i] = false;
}
int vindex = GetVertexIndex(v);
if (vindex == -1)
{
return;//说明不存在该顶点
}
qq.push(vindex);
cout << v << " ";
visited[vindex] = true;
while (!qq.empty())//当队列不空,获取队头元素出队
{
front = qq.front;
qq.pop();
//访问队头元素的邻接顶点
p = m_Vertex[vindex].m_list;//让p指向队头元素的邻接表,用邻接表访问邻接结点
while (p)//有邻接顶点
{
if (!visited[p->m_destindex])//未被访问
{
cout << m_VerArr[p->m_destindex].m_value;//输出该结点的值
visited[p->m_destindex] = true;
qq.push(p->m_destindex);
}
p = p->m_next;
}
}
delete[]visited;
visited = NULL;
}
DFS
void Graph::DFS(char v)
{
bool* visited = new bool[m_NumVertex];
for (int i = 0; i < m_NumVertex; i++)
{
visited[i] = false;
}
int vindex = GetVertexIndex(v);
if (vindex == -1)
return;
DFS(vindex,visited);//递归调用
delete[]visited;
visited = NULL;
}
void Graph::DFS(int v, bool* visited)//递归
{
cout << m_VerArr[v].m_value << " ";
visited[v] = true;
Edge* p = m_VerArr[v].m_list;//p指向邻接表
while (p)
{
if (!visited[p->m_destindex])
DFS(p->m_destindex);
p = p->m_next;
}
}
完整代码
#include<iostream>
#include<queue>
using namespace std;
#define SIZE 10
class Edge//边
{
public:
Edge() :m_next(NULL) {}
Edge(int index) :m_destindex(index), m_next(NULL) {}
int m_destindex;
Edge* m_next;
};
class Vertex//顶点
{
public:
Vertex():m_list(NULL){}
Vertex(char v):m_value(v),m_list(NULL){}
char m_value;
Edge* m_list;
};
//图存储顶点的一维数组
class Graph
{
public:
Graph();
~Graph();
void InsertVertex(char v);//插入顶点
void InsertEdge(char v1, char v2);//边
int GetVertexIndex(char v);//获得顶点下标
void ShowGraph();
void BFS(char v);
void DFS(char v);
void DFS(int vindex, bool* visited);
void DeleteEdge(char v1, char v2);//删除边函数
void DeleteVertex(char v);//删除顶点函数
private:
int m_MaxVertex;//最大能存储的顶点个数
int m_NumVertex;//实际存储的个数
//Vertex* m_VerArr;
Vertex m_VerArr[SIZE];
};
Graph::Graph()
{
m_MaxVertex = SIZE;
m_NumVertex = 0;
//m_VerArr = new Vertex[m_MaxVertex];
}
Graph::~Graph()
{
/* if (m_VerArr != NULL)
{
delete[]m_VerArr;
m_VerArr = NULL;
}*/
m_NumVertex = 0;
}
void Graph::InsertVertex(char v)
{
if (m_NumVertex >= m_MaxVertex)//空间满了就不放
return;
//没满就放
m_VerArr[m_NumVertex++].m_value = v;
}
int Graph::GetVertexIndex(char v)
{
//循环查找
for (int i = 0; i < m_NumVertex; i++)
{
if (m_VerArr[i].m_value == v)//找到返回下标
return i;
}
return -1;//没找到
}
void Graph::InsertEdge(char v1, char v2)
{
//1.获得顶点下标,没有该下标(下标=-1)
int p1index = GetVertexIndex(v1);
int p2index = GetVertexIndex(v1);
if (p1index == -1 || p2index == -1)
{
return;//任有一个不存在就返回
}
//插入v1到v2的边,头插法把p2的结点插入到p1前面
Edge* p = new Edge(p2index);//new出p2的结点
p->m_next = m_VerArr[p1index].m_list;//p2的头
m_VerArr[p1index].m_list = p;//然后把头挪到P的位置
//v2到v1,同理操作
p = new Edge(p1index);
p->m_next = m_VerArr[p2index].m_list;
m_VerArr[p2index].m_list = p;
}
void Graph::ShowGraph()
{
Edge* p = NULL;
for (int i = 0; i < m_MaxVertex; i++)
{
cout << i << " " << m_VerArr[i].m_value << "->";//先把每个顶点输出
p = m_VerArr[i].m_list;//让p=边链表的头
while (p != NULL)
{
cout << p->m_destindex << "-》";
p = p->m_next;
}
}
}
//删除边
void Graph::DeleteEdge(char v1, char v2)
{
int p1index = GetVertexIndex(v1);
int p2index = GetVertexIndex(v2);
if (p1index == -1 || p2index == -1)//没找到
return;
//先删除v1到v2
Edge* pf = NULL;
Edge* p = m_VerArr[p1index].m_list;//p定位到头节点位置
while (p != NULL && p->m_destindex != p2index)
{
pf = p;
p = p->m_next;
}
if (p == NULL)//没有边
return;
if(pf==NULL)//删除的是第一个边p,就把头指向第二个边
{
m_VerArr[p1index].m_list = p->m_next;
}
else//删除的是第二个及后面的边,就让第一个指向p的下一个
{
pf->m_next = p->m_next;
}
delete p;
pf = NULL;
//删除v2到v1
p = m_VerArr[p2index].m_list;//p指向v2
while (p->m_destindex != p1index)
{
pf = p;
p = p->m_next;
}
//1.第一个
if (pf == NULL)
m_VerArr[p2index].m_list = p->m_next;
//2.第二个以后
else
pf->m_next = p->m_next;
delete p;
}
//删顶点,删除顶点不仅需要删除某一顶点还需要删除和他相连的边
void Graph::DeleteVertex(char v)//删除顶点v
{
int vindex = GetVertexIndex(v);
if (vindex == -1)
return;
Edge* p = m_VerArr[vindex].m_list;
char destVerTex;
while (p != NULL)//先把顶点内边删除完毕
{
destVerTex = m_VerArr[p->m_destindex].m_value;
DeleteEdge(v, destVerTex);
p = m_VerArr[vindex].m_list;
}
//然后开始删除顶点
//顶点是个一维数组,普通删除是移动进行替换,但是此处采用的方式是覆盖,用最后一个顶点覆盖待删除顶点的所有信息
m_NumVertex--;//顶点个数先--
m_VerArr[vindex].m_value = m_VerArr[m_NumVertex].m_value;
m_VerArr[vindex].m_list = m_VerArr[m_NumVertex].m_list;
//在其他节点内对删除结点的边进行下标替换为最后一个结点的下标
p = m_VerArr[vindex].m_list;//p是记录最后一个结点对应的链内下标
Edge* q = NULL;//q记录新结点对应链内下标
while (p != NULL)
{
int k = p->m_destindex;//p内找到结点下标a
q = m_VerArr[k].m_list;//q记录新结点对应链内下标
while (q != NULL)//在q内查找这个a下标
{
if (q->m_destindex == m_NumVertex)//找到了就把a结点的值改为p所在的结点(最后挪上来的结点)
{
q->m_destindex = vindex;
break;
}
else
q = q->m_next;
}
p = p->m_next;
}
}
void Graph::BFS(char v)
{
queue<int>qq;
bool* visited = new bool[m_NumVertex];
int front;
Edge* p = NULL;
for (int i = 0; i < m_NumVertex; i++)
{
visited[i] = false;
}
int vindex = GetVertexIndex(v);
if (vindex == -1)
{
return;//说明不存在该顶点
}
qq.push(vindex);
cout << v << " ";
visited[vindex] = true;
while (!qq.empty())//当队列不空,获取队头元素出队
{
front = qq.front;
qq.pop();
//访问队头元素的邻接顶点
p = m_Vertex[vindex].m_list;//让p指向队头元素的邻接表,用邻接表访问邻接结点
while (p)//有邻接顶点
{
if (!visited[p->m_destindex])//未被访问
{
cout << m_VerArr[p->m_destindex].m_value;//输出该结点的值
visited[p->m_destindex] = true;
qq.push(p->m_destindex);
}
p = p->m_next;
}
}
delete[]visited;
visited = NULL;
}
void Graph::DFS(char v)
{
bool* visited = new bool[m_NumVertex];
for (int i = 0; i < m_NumVertex; i++)
{
visited[i] = false;
}
int vindex = GetVertexIndex(v);
if (vindex == -1)
return;
DFS(vindex,visited);//递归调用
delete[]visited;
visited = NULL;
}
void Graph::DFS(int v, bool* visited)//递归
{
cout << m_VerArr[v].m_value << " ";
visited[v] = true;
Edge* p = m_VerArr[v].m_list;//p指向邻接表
while (p)
{
if (!visited[p->m_destindex])
DFS(p->m_destindex);
p = p->m_next;
}
}
int main()
{
Graph g;
g.InsertVertex('a');
g.InsertVertex('b');
g.InsertVertex('c');
g.InsertVertex('d');
g.InsertEdge('a', 'b');
g.InsertEdge('a', 'c');
g.InsertEdge('a', 'd');
g.InsertEdge('b', 'c');
g.InsertEdge('c', 'd');
g.ShowGraph();
g.BFS('a');//从a开始调用
g.DFS('a');
}