/*** C++: Dijkstra算法獲取最短路徑(鄰接表)** @author judyge* @date 2014/04/24*/#include <iomanip>
#include <iostream>
#include <vector>
using namespace std;// 示例類:邊的結構體(用來演示)
class EData
{public:char start; // 邊的起點char end; // 邊的終點int weight; // 邊的權重public:EData(){}EData(char s, char e, int w):start(s),end(e),weight(w){}
};// 鄰接表
class ListUDG
{
#define MAX 100
#define INF (~(0x1<<31)) // 最大值(即0X7FFFFFFF)private: // 內部類// 鄰接表中表對應的鏈表的頂點class ENode{int ivex; // 該邊所指向的頂點的位置int weight; // 該邊的權ENode *nextEdge; // 指向下一條弧的指針friend class ListUDG;};// 鄰接表中表的頂點class VNode{char data; // 頂點信息ENode *firstEdge; // 指向第一條依附該頂點的弧friend class ListUDG;};private: // 私有成員int mVexNum; // 圖的頂點的數目int mEdgNum; // 圖的邊的數目VNode mVexs[MAX];public:// 創建鄰接表對應的圖(自己輸入)ListUDG();// 創建鄰接表對應的圖(用已提供的數據)ListUDG(char vexs[], int vlen, EData *edges[], int elen);~ListUDG();// 深度優先搜索遍歷圖void DFS();// 廣度優先搜索(類似于樹的層次遍歷)void BFS();// 打印鄰接表圖void print();// prim最小生成樹void prim(int start);// 克魯斯卡爾(Kruskal)最小生成樹void kruskal();// Dijkstra最短路徑void dijkstra(int vs, int vexs[], int dist[]);private:// 讀取一個輸入字符char readChar();// 返回ch的位置int getPosition(char ch);// 深度優先搜索遍歷圖的遞歸實現void DFS(int i, int *visited);// 將node節點鏈接到list的最后void linkLast(ENode *list, ENode *node);// 獲取邊<start, end>的權值;若start和end不是連通的,則返回無窮大。int getWeight(int start, int end);// 獲取圖中的邊EData* getEdges();// 對邊按照權值大小進行排序(由小到大)void sortEdges(EData* edges, int elen);// 獲取i的終點int getEnd(int vends[], int i);
};/** 創建鄰接表對應的圖(自己輸入)*/
ListUDG::ListUDG()
{char c1, c2;int v, e;int i, p1, p2;int weight;ENode *node1, *node2;// 輸入"頂點數"和"邊數"cout << "input vertex number: ";cin >> mVexNum;cout << "input edge number: ";cin >> mEdgNum;if ( mVexNum < 1 || mEdgNum < 1 || (mEdgNum > (mVexNum * (mVexNum-1)))){cout << "input error: invalid parameters!" << endl;return ;}// 初始化"鄰接表"的頂點for(i=0; i<mVexNum; i++){cout << "vertex(" << i << "): ";mVexs[i].data = readChar();mVexs[i].firstEdge = NULL;}// 初始化"鄰接表"的邊for(i=0; i<mEdgNum; i++){// 讀取邊的起始頂點和結束頂點cout << "edge(" << i << "): ";c1 = readChar();c2 = readChar();cin >> weight;p1 = getPosition(c1);p2 = getPosition(c2);// 初始化node1node1 = new ENode();node1->ivex = p2;node1->weight = weight;// 將node1鏈接到"p1所在鏈表的末尾"if(mVexs[p1].firstEdge == NULL)mVexs[p1].firstEdge = node1;elselinkLast(mVexs[p1].firstEdge, node1);// 初始化node2node2 = new ENode();node2->ivex = p1;node2->weight = weight;// 將node2鏈接到"p2所在鏈表的末尾"if(mVexs[p2].firstEdge == NULL)mVexs[p2].firstEdge = node2;elselinkLast(mVexs[p2].firstEdge, node2);}
}/** 創建鄰接表對應的圖(用已提供的數據)*/
ListUDG::ListUDG(char vexs[], int vlen, EData *edges[], int elen)
{char c1, c2;int i, p1, p2;int weight;ENode *node1, *node2;// 初始化"頂點數"和"邊數"mVexNum = vlen;mEdgNum = elen;// 初始化"鄰接表"的頂點for(i=0; i<mVexNum; i++){mVexs[i].data = vexs[i];mVexs[i].firstEdge = NULL;}// 初始化"鄰接表"的邊for(i=0; i<mEdgNum; i++){// 讀取邊的起始頂點和結束頂點c1 = edges[i]->start;c2 = edges[i]->end;weight = edges[i]->weight;p1 = getPosition(c1);p2 = getPosition(c2);// 初始化node1node1 = new ENode();node1->ivex = p2;node1->weight = weight;// 將node1鏈接到"p1所在鏈表的末尾"if(mVexs[p1].firstEdge == NULL)mVexs[p1].firstEdge = node1;elselinkLast(mVexs[p1].firstEdge, node1);// 初始化node2node2 = new ENode();node2->ivex = p1;node2->weight = weight;// 將node2鏈接到"p2所在鏈表的末尾"if(mVexs[p2].firstEdge == NULL)mVexs[p2].firstEdge = node2;elselinkLast(mVexs[p2].firstEdge, node2);}
}/* * 析構函數*/
ListUDG::~ListUDG()
{
}/** 將node節點鏈接到list的最后*/
void ListUDG::linkLast(ENode *list, ENode *node)
{ENode *p = list;while(p->nextEdge)p = p->nextEdge;p->nextEdge = node;
}/** 返回ch的位置*/
int ListUDG::getPosition(char ch)
{int i;for(i=0; i<mVexNum; i++)if(mVexs[i].data==ch)return i;return -1;
}/** 讀取一個輸入字符*/
char ListUDG::readChar()
{char ch;do {cin >> ch;} while(!((ch>='a'&&ch<='z') || (ch>='A'&&ch<='Z')));return ch;
}/** 深度優先搜索遍歷圖的遞歸實現*/
void ListUDG::DFS(int i, int *visited)
{ENode *node;visited[i] = 1;cout << mVexs[i].data << " ";node = mVexs[i].firstEdge;while (node != NULL){if (!visited[node->ivex])DFS(node->ivex, visited);node = node->nextEdge;}
}/** 深度優先搜索遍歷圖*/
void ListUDG::DFS()
{int i;int visited[MAX]; // 頂點訪問標記// 初始化所有頂點都沒有被訪問for (i = 0; i < mVexNum; i++)visited[i] = 0;cout << "DFS: ";for (i = 0; i < mVexNum; i++){if (!visited[i])DFS(i, visited);}cout << endl;
}/** 廣度優先搜索(類似于樹的層次遍歷)*/
void ListUDG::BFS()
{int head = 0;int rear = 0;int queue[MAX]; // 輔組隊列int visited[MAX]; // 頂點訪問標記int i, j, k;ENode *node;for (i = 0; i < mVexNum; i++)visited[i] = 0;cout << "BFS: ";for (i = 0; i < mVexNum; i++){if (!visited[i]){visited[i] = 1;cout << mVexs[i].data << " ";queue[rear++] = i; // 入隊列}while (head != rear) {j = queue[head++]; // 出隊列node = mVexs[j].firstEdge;while (node != NULL){k = node->ivex;if (!visited[k]){visited[k] = 1;cout << mVexs[k].data << " ";queue[rear++] = k;}node = node->nextEdge;}}}cout << endl;
}/** 打印鄰接表圖*/
void ListUDG::print()
{int i,j;ENode *node;cout << "List Graph:" << endl;for (i = 0; i < mVexNum; i++){cout << i << "(" << mVexs[i].data << "): ";node = mVexs[i].firstEdge;while (node != NULL){cout << node->ivex << "(" << mVexs[node->ivex].data << ") ";node = node->nextEdge;}cout << endl;}
}/** 獲取邊<start, end>的權值;若start和end不是連通的,則返回無窮大。*/
int ListUDG::getWeight(int start, int end)
{ENode *node;if (start==end)return 0;node = mVexs[start].firstEdge;while (node!=NULL){if (end==node->ivex)return node->weight;node = node->nextEdge;}return INF;
}/** prim最小生成樹** 參數說明:* start -- 從圖中的第start個元素開始,生成最小樹*/
void ListUDG::prim(int start)
{int min,i,j,k,m,n,tmp,sum;int index=0; // prim最小樹的索引,即prims數組的索引char prims[MAX]; // prim最小樹的結果數組int weights[MAX]; // 頂點間邊的權值// prim最小生成樹中第一個數是"圖中第start個頂點",因為是從start開始的。prims[index++] = mVexs[start].data;// 初始化"頂點的權值數組",// 將每個頂點的權值初始化為"第start個頂點"到"該頂點"的權值。for (i = 0; i < mVexNum; i++ )weights[i] = getWeight(start, i);for (i = 0; i < mVexNum; i++){// 由于從start開始的,因此不需要再對第start個頂點進行處理。if(start == i)continue;j = 0;k = 0;min = INF;// 在未被加入到最小生成樹的頂點中,找出權值最小的頂點。while (j < mVexNum){// 若weights[j]=0,意味著"第j個節點已經被排序過"(或者說已經加入了最小生成樹中)。if (weights[j] != 0 && weights[j] < min){min = weights[j];k = j;}j++;}// 經過上面的處理后,在未被加入到最小生成樹的頂點中,權值最小的頂點是第k個頂點。// 將第k個頂點加入到最小生成樹的結果數組中prims[index++] = mVexs[k].data;// 將"第k個頂點的權值"標記為0,意味著第k個頂點已經排序過了(或者說已經加入了最小樹結果中)。weights[k] = 0;// 當第k個頂點被加入到最小生成樹的結果數組中之后,更新其它頂點的權值。for (j = 0 ; j < mVexNum; j++){// 獲取第k個頂點到第j個頂點的權值tmp = getWeight(k, j);// 當第j個節點沒有被處理,并且需要更新時才被更新。if (weights[j] != 0 && tmp < weights[j])weights[j] = tmp;}}// 計算最小生成樹的權值sum = 0;for (i = 1; i < index; i++){min = INF;// 獲取prims[i]在矩陣表中的位置n = getPosition(prims[i]);// 在vexs[0...i]中,找出到j的權值最小的頂點。for (j = 0; j < i; j++){m = getPosition(prims[j]);tmp = getWeight(m, n);if (tmp < min)min = tmp;}sum += min;}// 打印最小生成樹cout << "PRIM(" << mVexs[start].data <<")=" << sum << ": ";for (i = 0; i < index; i++)cout << prims[i] << " ";cout << endl;
}/* * 獲取圖中的邊*/
EData* ListUDG::getEdges()
{int i,j;int index=0;ENode *node;EData *edges;edges = new EData[mEdgNum];for (i=0; i < mVexNum; i++){node = mVexs[i].firstEdge;while (node != NULL){if (node->ivex > i){edges[index].start = mVexs[i].data; // 起點edges[index].end = mVexs[node->ivex].data; // 終點edges[index].weight = node->weight; // 權index++;}node = node->nextEdge;}}return edges;
}/* * 對邊按照權值大小進行排序(由小到大)*/
void ListUDG::sortEdges(EData* edges, int elen)
{int i,j;for (i=0; i<elen; i++){for (j=i+1; j<elen; j++){if (edges[i].weight > edges[j].weight){// 交換"邊i"和"邊j"swap(edges[i], edges[j]);}}}
}/** 獲取i的終點*/
int ListUDG::getEnd(int vends[], int i)
{while (vends[i] != 0)i = vends[i];return i;
}/** 克魯斯卡爾(Kruskal)最小生成樹*/
void ListUDG::kruskal()
{int i,m,n,p1,p2;int length;int index = 0; // rets數組的索引int vends[MAX]={0}; // 用于保存"已有最小生成樹"中每個頂點在該最小樹中的終點。EData rets[MAX]; // 結果數組,保存kruskal最小生成樹的邊EData *edges; // 圖對應的所有邊// 獲取"圖中所有的邊"edges = getEdges();// 將邊按照"權"的大小進行排序(從小到大)sortEdges(edges, mEdgNum);for (i=0; i<mEdgNum; i++){p1 = getPosition(edges[i].start); // 獲取第i條邊的"起點"的序號p2 = getPosition(edges[i].end); // 獲取第i條邊的"終點"的序號m = getEnd(vends, p1); // 獲取p1在"已有的最小生成樹"中的終點n = getEnd(vends, p2); // 獲取p2在"已有的最小生成樹"中的終點// 如果m!=n,意味著"邊i"與"已經添加到最小生成樹中的頂點"沒有形成環路if (m != n){vends[m] = n; // 設置m在"已有的最小生成樹"中的終點為nrets[index++] = edges[i]; // 保存結果}}delete[] edges;// 統計并打印"kruskal最小生成樹"的信息length = 0;for (i = 0; i < index; i++)length += rets[i].weight;cout << "Kruskal=" << length << ": ";for (i = 0; i < index; i++)cout << "(" << rets[i].start << "," << rets[i].end << ") ";cout << endl;
}/** Dijkstra最短路徑。* 即,統計圖中"頂點vs"到其它各個頂點的最短路徑。** 參數說明:* vs -- 起始頂點(start vertex)。即計算"頂點vs"到其它頂點的最短路徑。* prev -- 前驅頂點數組。即,prev[i]的值是"頂點vs"到"頂點i"的最短路徑所經歷的全部頂點中,位于"頂點i"之前的那個頂點。* dist -- 長度數組。即,dist[i]是"頂點vs"到"頂點i"的最短路徑的長度。*/
void ListUDG::dijkstra(int vs, int prev[], int dist[])
{int i,j,k;int min;int tmp;int flag[MAX]; // flag[i]=1表示"頂點vs"到"頂點i"的最短路徑已成功獲取。// 初始化for (i = 0; i < mVexNum; i++){flag[i] = 0; // 頂點i的最短路徑還沒獲取到。prev[i] = 0; // 頂點i的前驅頂點為0。dist[i] = getWeight(vs, i); // 頂點i的最短路徑為"頂點vs"到"頂點i"的權。}// 對"頂點vs"自身進行初始化flag[vs] = 1;dist[vs] = 0;// 遍歷mVexNum-1次;每次找出一個頂點的最短路徑。for (i = 1; i < mVexNum; i++){// 尋找當前最小的路徑;// 即,在未獲取最短路徑的頂點中,找到離vs最近的頂點(k)。min = INF;for (j = 0; j < mVexNum; j++){if (flag[j]==0 && dist[j]<min){min = dist[j];k = j;}}// 標記"頂點k"為已經獲取到最短路徑flag[k] = 1;// 修正當前最短路徑和前驅頂點// 即,當已經"頂點k的最短路徑"之后,更新"未獲取最短路徑的頂點的最短路徑和前驅頂點"。for (j = 0; j < mVexNum; j++){tmp = getWeight(k, j);tmp = (tmp==INF ? INF : (min + tmp)); // 防止溢出if (flag[j] == 0 && (tmp < dist[j]) ){dist[j] = tmp;prev[j] = k;}}}// 打印dijkstra最短路徑的結果cout << "dijkstra(" << mVexs[vs].data << "): " << endl;for (i = 0; i < mVexNum; i++)cout << " shortest(" << mVexs[vs].data << ", " << mVexs[i].data << ")=" << dist[i] << endl;
}int main()
{int prev[MAX] = {0};int dist[MAX] = {0};// 頂點char vexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};// 邊EData *edges[] = {// 起點 終點 權new EData('A', 'B', 12), new EData('A', 'F', 16), new EData('A', 'G', 14), new EData('B', 'C', 10), new EData('B', 'F', 7), new EData('C', 'D', 3), new EData('C', 'E', 5), new EData('C', 'F', 6), new EData('D', 'E', 4), new EData('E', 'F', 2), new EData('E', 'G', 8), new EData('F', 'G', 9), };int vlen = sizeof(vexs)/sizeof(vexs[0]);int elen = sizeof(edges)/sizeof(edges[0]);ListUDG* pG;// 自定義"圖"(輸入矩陣隊列)//pG = new ListUDG();// 采用已有的"圖"pG = new ListUDG(vexs, vlen, edges, elen);//pG->print(); // 打印圖//pG->DFS(); // 深度優先遍歷//pG->BFS(); // 廣度優先遍歷//pG->prim(0); // prim算法生成最小生成樹//pG->kruskal(); // Kruskal算法生成最小生成樹// dijkstra算法獲取"第4個頂點"到其它各個頂點的最短距離pG->dijkstra(3, prev, dist);return 0;
}
總結
以上是生活随笔為你收集整理的ACM模板--邻接表 无向图 Prim Kruskal Dijkstra的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。