前言
根據分類,圖的搜索分類可以分為
- BFS和DFS
- 記憶化搜索(基於深搜)
- 雙向廣搜
- 二分狀態搜索
- 啟發式搜索
- 與或樹搜索
- 博弈樹搜索(α-β剪枝)(極大極小過程搜索)
- A*搜索
- IDA搜索
先看BFS和DFS,因為這是最基礎的搜索策略了,BFS是按照深度進行搜索,DFS則是按照廣度進行搜索;
其實只要你理解了樹的DFS和BFS,那么圖的話,只是在其基礎上加了判斷結點是否訪問過,是否聯通而已;
深度優先搜索
簡介
先看一幅有向圖
可以發現,其遍歷的順序為
0->3->1->2->4
其的概念就是一條路走到黑
圖的表示方法包括鄰接表,鄰接矩陣等,鄰接矩陣表示的是用一個二維數組表示圖的聯通,其中i行j列表示了i結點和j結點的聯通情況,如果其為1,說明是聯通的,如果其為0,反之;鄰接表表示的是一個一維數組,但是數組中每個列表包含着是鏈表;
看個圖加深理解:
其中a是有向圖/原圖,b是鄰接表表示圖,c是鄰接矩陣表示圖;
偽代碼
遞歸實現
1. 訪問數組初始化:visited[n] = 0
2. 訪問頂點:visited[v] = 1
3. 取v的第一個鄰接點w;
4. 循環遞歸:
while(w存在)
if(w未被訪問過)
從頂點w出發遞歸執行;
w = v的下一個鄰接點;
非遞歸實現
1. 棧初始化:visited[n] = 0
2. 訪問頂點:visited[v] = 1
3. 入棧
4. while(棧不為空)
x = 棧的頂元素,並且出棧;
if (存在並找到未被訪問的x的鄰接點w)
訪問w:visited[w] = 1
w進棧
上面的尋找下一個鄰接點,需要根據圖是鄰接表還是鄰接矩陣進行循環判斷;
實現
由於圖的表示有幾種,所以實現的代碼包括了用鄰接矩陣的圖和鄰接表的圖;
.h文件
typedef struct edge {
int vertex;
struct edge* next;
}Edge;
class DFS {
public:
DFS();
~DFS();
void Test_M();
void Test_L();
private:
// 鄰接矩陣
void createGraph_M(int (*edge)[VERTEXNUM], int start, int end);
void displayGraph_M(int (*edge)[VERTEXNUM]);
void DFT_M(int (*edge)[VERTEXNUM], int* vertexStatusArr);
void DFTCore_M(int (*edge)[VERTEXNUM], int i, int* vertexStatusArr);
// 鄰接表
void createGraph_L(Edge** edge, int start, int end);
void displayGraph_L(Edge** edge);
void delGraph_L(Edge** edge);
void DFT_L(Edge** edge, int* vertextStatusArr);
void DFTCore_L(Edge** edge, int i, int* vertexStatusArr);
};
void DFSTest_M();
void DFSTest_L();
.cpp文件
DFS::DFS() {
}
DFS::~DFS() {
}
/*----------------------------鄰接矩陣--------------------------*/
/**
* 創建圖
*
* @param edge <#edge description#>
*/
void DFS::createGraph_M(int (*edge)[VERTEXNUM], int start, int end) {
edge[start][end] = 1;
}
/**
* 打印存儲的圖
*
* @param edge <#edge description#>
*/
void DFS::displayGraph_M(int (*edge)[VERTEXNUM]) {
for (int i = 0; i < VERTEXNUM; i++) {
for (int j = 0; j < VERTEXNUM; j++) {
std::cout << edge[i][j];
}
std::cout << std::endl;
}
}
/**
* 深度優先遍歷
*
* @param edge <#edge description#>
*/
void DFS::DFT_M(int (*edge)[VERTEXNUM], int* vertexStatusArr) {
for (int i = 0; i < VERTEXNUM; i++) {
DFTCore_M(edge, i, vertexStatusArr);
}
std::cout << std::endl;
}
void DFS::DFTCore_M(int (*edge)[VERTEXNUM], int i, int* vertexStatusArr) {
if (vertexStatusArr[i] == 1) return; // 頂點訪問過則不再訪問
std::cout << i << "->";
vertexStatusArr[i] = 1;
// 存在邊的對應關系,才遞歸
for (int j = 0; j < VERTEXNUM; j++) {
if (edge[i][j] == 1) {
DFTCore_M(edge, j, vertexStatusArr);
}
}
}
void DFS::Test_M() {
std::cout << "--------------鄰接矩陣表示-----------------" << std::endl;
//動態創建存放邊的二維數組
int (*edge)[VERTEXNUM] = (int (*)[VERTEXNUM])malloc(sizeof(int) * VERTEXNUM * VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
for (int j = 0; j < VERTEXNUM; j++) {
edge[i][j] = 0;
}
}
// 存放頂點的遍歷狀態;0:未遍歷,1:已遍歷
int *vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM*VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
vertexStatusArr[i] = 0;
}
std::cout << "after init.." << std::endl;
displayGraph_M(edge);
//創建圖
createGraph_M(edge, 0, 3);
createGraph_M(edge, 0, 4);
createGraph_M(edge, 3, 1);
createGraph_M(edge, 3, 2);
createGraph_M(edge, 4, 1);
std::cout << "after create.." << std::endl;
displayGraph_M(edge);
// 遍歷
std::cout << "traversal.." << std::endl;
DFT_M(edge, vertexStatusArr);
free(edge);
}
/*----------------------------鄰接表--------------------------*/
void DFS::createGraph_L(Edge** edge, int start, int end) {
Edge* nedge = (Edge*)malloc(sizeof(Edge));
nedge->vertex = end;
nedge->next = nullptr;
edge = edge + start;
while (*edge != nullptr) {
edge = &((*edge)->next);
}
*edge = nedge;
}
void DFS::displayGraph_L(Edge** edge) {
Edge* nedge;
int edgeCount = 0;
for (int i = 0; i < VERTEXNUM; i++) {
nedge = *(edge + i);
std::cout << i << ":";
while (nedge != nullptr) {
std::cout << nedge->vertex << ",";
nedge = nedge->next;
edgeCount++;
}
std::cout << std::endl;
}
std::cout <<"edge count is " << edgeCount << std::endl;
}
void DFS::delGraph_L(Edge** edge) {
Edge *p, *del;
for (int i = 0; i < VERTEXNUM; i++) {
p = *(edge + i);
while (p != nullptr) {
del = p;
p = p->next;
free(del);
}
edge[i] = nullptr;
}
free(edge);
}
void DFS::DFT_L(Edge** edge, int* vertextStatusArr) {
for (int i = 0; i < VERTEXNUM; i++) {
DFTCore_L(edge, i, vertextStatusArr);
}
std::cout << std::endl;
}
void DFS::DFTCore_L(Edge** edge, int i, int* vertexStatusArr) {
if (vertexStatusArr[i] == 1) {
return;
}
std::cout << i << "->";
vertexStatusArr[i] = 1;
Edge *p = *(edge + i);
while (p != nullptr) {
DFTCore_L(edge, p->vertex, vertexStatusArr);
p = p->next;
}
}
void DFS::Test_L() {
std::cout << "--------------鄰接表表示-----------------" << std::endl;
// 動態創建存放邊的指針數組
Edge **edge = (Edge**)malloc(sizeof(Edge*)*VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
edge[i] = nullptr;
}
// 存放頂點的遍歷狀態:0:未遍歷,1:已遍歷
int *vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
vertexStatusArr[i] = 0;
}
std::cout << "after init.." << std::endl;
displayGraph_L(edge);
//創建圖
createGraph_L(edge, 0, 3);
createGraph_L(edge, 0, 4);
createGraph_L(edge, 3, 1);
createGraph_L(edge, 3, 2);
createGraph_L(edge, 4, 1);
std::cout << "after create.." << std::endl;
displayGraph_L(edge);
// 深度優先遍歷
DFT_L(edge, vertexStatusArr);
// 釋放鄰接表占用的內存
delGraph_L(edge);
edge = nullptr;
free(vertexStatusArr);
vertexStatusArr = nullptr;
}
總結:代碼有很強的C的風格,抱歉;這里面主要看遍歷過程;
廣度優先遍歷
簡介
先看一幅有向圖
可以發現,其遍歷的順序為
0->3->4->1->2
其的概念就是左看看右看看,雨露均沾
偽代碼
非遞歸實現
1. 初始化隊列:visited[n] = 0
2. 訪問頂點:visited[v] = 1
3. 頂點v加入隊列
4. 循環:
while(隊列是否為空)
v = 隊列頭元素
w = v的第一個鄰接點
while(w存在)
if(如果w未訪問)
visited[w] = 1;
頂點w加入隊列
w = 頂點v的下一個鄰接點
上面的尋找下一個鄰接點,是尋找之前結點的下一個鄰接點,而不是當前的,否則就變成DFS了;
實現
.h文件
typedef struct bedge {
int vertex;
struct bedge* pre;
struct bedge* next;
}BEdge;
template <class T>
class Queue {
private:
T *arr;
int front;
int rear;
int size;
int length;
public:
Queue();
~Queue();
void Push(T value);
T Pop();
int Size();
int Length();
bool Empty();
bool Full();
};
class BFS {
private:
BEdge *front;
BEdge *rear;
public:
BFS();
~BFS();
void Test_M();
void Test_L();
void createGraph_M(int (*edge)[VERTEXNUM], int start, int end);
void displayGraph_M(int (*edge)[VERTEXNUM]);
void BFT_M(int (*edge)[VERTEXNUM], int *vertexStatusArr);
void BFTCore_M(int (*edge)[VERTEXNUM], int i, int *vertexStatusArr);
void createGraph_L(BEdge** edge, int start, int end);
void displayGraph_L(BEdge** edge);
void delGraph_L(BEdge** edge);
void BFT_L(BEdge** edge, int* vertextStatusArr);
void BFTCore_L(BEdge** edge, int i, int* vertexStatusArr);
};
void BFSTest_M();
void BFSTest_L();
.cpp文件
template <class T>
Queue<T>::Queue() {
size = 10;
arr = new T[size];
front = rear = length = 0;
}
template <class T>
Queue<T>::~Queue() {
delete [] arr;
}
template <class T>
void Queue<T>::Push(T value) {
// 大於原數組長度,創建新數組,賦值給新數組
if (length == size) {
int nsize = size * 2;
int * narr = new T(nsize);
int i = 0;
for (; i < length; i++) {
narr[(front + i) % nsize] = arr[(front + i) % size];
}
rear = (front + i) % nsize;
arr = narr;
size = nsize;
}
arr[rear] = value;
rear = (rear + 1) % size;
++length;
}
template <class T>
T Queue<T>::Pop() {
T temp = arr[front];
front = (front + 1) % size; // 原來的內存塊沒有做到真正的刪除
--length;
return temp;
}
template <class T>
int Queue<T>::Size() {
return size;
}
template <class T>
int Queue<T>::Length() {
return length;
}
template <class T>
bool Queue<T>::Empty() {
return length == 0;
}
template <class T>
bool Queue<T>::Full() {
return length == size;
}
BFS::BFS() {
}
BFS::~BFS() {
}
void BFS::createGraph_M(int (*edge)[VERTEXNUM], int start, int end) {
edge[start][end] = 1;
}
void BFS::displayGraph_M(int (*edge)[VERTEXNUM]) {
for (int i = 0; i < VERTEXNUM; i++) {
for (int j = 0; j < VERTEXNUM; j++) {
std::cout << edge[i][j];
}
std::cout << std::endl;
}
}
void BFS::BFT_M(int (*edge)[VERTEXNUM], int *vertexStatusArr) {
for (int i = 0; i < VERTEXNUM; i++) {
BFTCore_M(edge, i, vertexStatusArr);
}
std::cout << std::endl;
}
void BFS::BFTCore_M(int (*edge)[VERTEXNUM], int i, int *vertexStatusArr) {
Queue<int> queue;
queue.Push(i);
while (!queue.Empty()) {
int t = queue.Pop();
if (vertexStatusArr[t] == 0) {
std::cout << t << "->";
vertexStatusArr[t] = 1;
for (int i = 0; i < VERTEXNUM; i++) {
if (edge[t][i] == 1) {
queue.Push(i);
}
}
}
}
}
void BFS::Test_M() {
std::cout << "--------------鄰接矩陣表示-----------------" << std::endl;
//動態創建存放邊的二維數組
int (*edge)[VERTEXNUM] = (int (*)[VERTEXNUM])malloc(sizeof(int) * VERTEXNUM * VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
for (int j = 0; j < VERTEXNUM; j++) {
edge[i][j] = 0;
}
}
// 存放頂點的遍歷狀態;0:未遍歷,1:已遍歷
int *vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM*VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
vertexStatusArr[i] = 0;
}
std::cout << "after init.." << std::endl;
displayGraph_M(edge);
//創建圖
createGraph_M(edge, 0, 3);
createGraph_M(edge, 0, 4);
createGraph_M(edge, 3, 1);
createGraph_M(edge, 3, 2);
createGraph_M(edge, 4, 1);
std::cout << "after create.." << std::endl;
displayGraph_M(edge);
// 遍歷
std::cout << "traversal.." << std::endl;
BFT_M(edge, vertexStatusArr);
free(edge);
}
void BFS::createGraph_L(BEdge** edge, int start, int end) {
BEdge* nedge = (BEdge*)malloc(sizeof(BEdge));
nedge->vertex = end;
nedge->next = nullptr;
edge = edge + start;
while (*edge != nullptr) {
edge = &((*edge)->next);
}
*edge = nedge;
}
void BFS::displayGraph_L(BEdge** edge) {
BEdge* nedge;
int edgeCount = 0;
for (int i = 0; i < VERTEXNUM; i++) {
nedge = *(edge + i);
std::cout << i << ":";
while (nedge != nullptr) {
std::cout << nedge->vertex << ",";
nedge = nedge->next;
edgeCount++;
}
std::cout << std::endl;
}
std::cout <<"edge count is " << edgeCount << std::endl;
}
void BFS::delGraph_L(BEdge** edge) {
BEdge *p, *del;
for (int i = 0; i < VERTEXNUM; i++) {
p = *(edge + i);
while (p != nullptr) {
del = p;
p = p->next;
free(del);
}
edge[i] = nullptr;
}
free(edge);
}
void BFS::BFT_L(BEdge** edge, int* vertextStatusArr) {
for (int i = 0; i < VERTEXNUM; i++) {
BFTCore_L(edge, i, vertextStatusArr);
}
std::cout << std::endl;
}
void BFS::BFTCore_L(BEdge** edge, int i, int* vertexStatusArr) {
Queue<int> queue;
queue.Push(i);
while (!queue.Empty()) {
int t = queue.Pop();
if (vertexStatusArr[t] == 0) {
std::cout << t << "->";
vertexStatusArr[t] = 1;
BEdge* p = *(edge + t);
while (p != nullptr) {
queue.Push(p->vertex);
p = p->next;
}
}
}
}
void BFS::Test_L() {
std::cout << "--------------鄰接表表示-----------------" << std::endl;
// 動態創建存放邊的指針數組
BEdge **edge = (BEdge**)malloc(sizeof(BEdge*)*VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
edge[i] = nullptr;
}
// 存放頂點的遍歷狀態:0:未遍歷,1:已遍歷
int *vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
vertexStatusArr[i] = 0;
}
std::cout << "after init.." << std::endl;
displayGraph_L(edge);
//創建圖
createGraph_L(edge, 0, 3);
createGraph_L(edge, 0, 4);
createGraph_L(edge, 3, 1);
createGraph_L(edge, 3, 2);
createGraph_L(edge, 4, 1);
std::cout << "after create.." << std::endl;
displayGraph_L(edge);
// 深度優先遍歷
BFT_L(edge, vertexStatusArr);
// 釋放鄰接表占用的內存
delGraph_L(edge);
edge = nullptr;
free(vertexStatusArr);
vertexStatusArr = nullptr;
}
參考: