1. 程式人生 > 實用技巧 >圖論演算法的實現:(Dijstra Bellman-Ford DFS BFS)

圖論演算法的實現:(Dijstra Bellman-Ford DFS BFS)

#ifndef HEAD_H_
#define HEAD_H__
#include <iostream>
#include <array>
#include <vector>
#include <queue>
#include <cassert>
#include <string>
#include <fstream>
#include<numeric>
#include<cmath>
#include<algorithm>
template <typename T>
using grap = std::vector<std::vector<T>>;
template <typename T>
class Graph
{
protected:
	enum color
	{
		White,
		black,
		gray
	};
	struct Node
	{
		//T data;
		struct Node* next;
		struct Node* parents;
		std::size_t index; //頂點編號
		int color;		   //頂點得顏色,記錄訪問標誌
		double distance;	   //距離指標記號
		int full_time;
		Node(std::size_t i) : index(i)
		{
			next = nullptr;
			parents = nullptr;
			color = White;
			distance = 0;
			full_time = 0; //深度優先遍歷之用
		}
	};
	using node = struct Node;
	using pnode = struct Node*;
	std::size_t size;
	std::vector<pnode> Matrix;
	std::vector<std::vector<T>> Weight; //權重矩陣
	void insert(pnode& phead, pnode pnew);
	pnode root; //廣度優先樹的根節點
	std::vector<std::string>area;
	int find_index(std::string area_);
	//列印路徑

private:
	//bool success();				  //檢驗鄰接表是否建立成
	//自定義排序函式(氣泡排序規則)
	void Sort(std::vector<pnode>& Array);
	
	grap<T> graph;				  //鄰接矩陣
	void Initialize_Graph();	  //初始化圖
	void DFS_VISIT(pnode vertex); //DFS輔助函式
	void convert_to_list();		  //將鄰接矩陣轉換為鄰接表的形式
	void printMinDistance(std::size_t i,bool f);
	//Bellman-Ford演算法的部分
	//建立邊的儲存的資料結構
	struct edge {
		std::size_t from;
		std::size_t to;
		T weight;
		edge(std::size_t f, std::size_t t,T w) :from(f), to(t),weight(w) {}
	};
	//使用鄰接表建立最小路徑樹
	//case1 建立邊的儲存
	std::vector<edge>E;
	void build_edge();
	//路徑鬆弛函式(Bellman_ford演算法的鬆弛函式)
	bool RELAX(edge e);
	
	bool Bellman_Ford(std::size_t i);
	//狄杰特斯拉演算法的鬆弛函式
	void RELAX(pnode& u, pnode& v);
	//演算法的具體步驟
	void Dijsktra(std::size_t i);
	void print_path(std::size_t i, std::size_t j);//列印路徑的函式
	
public:

	Graph() = default;
	Graph(grap<T> g) : graph(g) { convert_to_list(); };
	Graph(grap<T>g, grap<T>w) :graph(g),Weight(w) {
		convert_to_list();
	};
	Graph(std::string path);
	//過載一個從檔案讀入鄰接矩陣和權重矩陣的建構函式
	Graph(std::string path1, std::string path2);
	void check_AToB(std::string area_);
	void print_Graph();
	void BFS(std::size_t i,bool print); //廣度優先遍歷
	void DFS();				 //時間複雜度為\Theta(|V|+|E|)
	void MIN_RODD(std::size_t index,std::size_t mode,bool road);
	
};
template <typename T>
void Graph<T>::convert_to_list()
{
	size = graph.size();
	//初始化鄰接矩陣
	for (std::size_t t = 0; t < size; t++)
	{
		Matrix.push_back(new Node(t)); //從0開始編號
	}
	//根據鄰接矩陣構造鄰接表

	for (std::size_t i = 0; i < size; i++)
	{
		for (std::size_t j = 0; j < size; j++)
		{
			if (graph[i][j] == 1)
			{
				//i-->j存在邊得話
				pnode pnew = new Node(j);
				insert(Matrix[i], pnew);
			}
		}
	}
	//assert(success() == true);
}
template<typename T>
void Graph<T>::Sort(std::vector<pnode>& Array)
{
	for (size_t i = 0; i < Array.size(); i++)
	{
		for (size_t j = Array.size() - 1; j >= i + 1; j--)
		{
			if (Array[j]->distance >= Array[j - 1]->distance)
			{
				std::swap(Array[j], Array[j - 1]);
			}
		}
	}
}
template<typename T>
void Graph<T>::Initialize_Graph()
{
	//初始化鄰接表即可
	for (auto s : Matrix)
	{
		pnode pcur = s;
		while (pcur != nullptr)
		{
			pcur->parents = nullptr;
			pcur->full_time = 0;
			pcur->distance = 0;
			pcur->color = White;
			pcur = pcur->next;
		}
	}
}
template <typename T>
void Graph<T>::printMinDistance(std::size_t i,bool f)
{
	if (f == false) {
		for (auto m : Matrix)
		{
			std::cout << m->index << "-->" << i << "min:" << m->distance << std::endl;
		}
	}
	else if (f == true)
	{
		//按照檔案中的區域列印
		for (auto m : Matrix)
		{
			std::cout << area[m->index] << "-->" << area[i] << "min:" << m->distance << std::endl;
		}
	}
}
template<typename T>
void Graph<T>::build_edge()
{
	//根據鄰接表建立邊的儲存
	for (size_t i = 0; i < graph.size(); i++)
	{
		for (size_t j = 0; j < graph[i].size(); j++)
		{
			if (graph[i][j] == 1)
			{
				auto e = edge(i, j, Weight[i][j]);
				E.push_back(e);
			}
		}
	}
}
template<typename T>
bool Graph<T>::RELAX(edge e)
{
	if (Matrix[e.to]->distance > (Matrix[e.from]->distance + e.weight))
	{
		Matrix[e.to]->distance = (Matrix[e.from]->distance + e.weight);
		Matrix[e.to]->parents = Matrix[e.from];
		return true;
	}
	return false;
	
}
template<typename T>
void Graph<T>::RELAX(pnode& u, pnode& v)
{
	if (Matrix[v->index]->distance > (Matrix[u->index]->distance + Weight[u->index][v->index]))
	{
		Matrix[v->index]->distance = (Matrix[u->index]->distance + Weight[u->index][v->index]);
		Matrix[v->index]->parents = Matrix[u->index];
	}
}
template<typename T>
void Graph<T>::Dijsktra(std::size_t i)
{
	//初始化圖

	for (size_t j = 0; j < Matrix.size(); j++)
	{
		if (j != i)
		{
			Matrix[j]->distance = INFINITY;
			Matrix[j]->parents = nullptr;
		}
		if (j == i)
		{
			Matrix[j]->distance = 0;
			Matrix[j]->parents = nullptr;
		}

	}
	std::vector<pnode>S;//swap
	auto f = [](pnode p1, pnode p2)
	{
		if (p1->distance >= p2->distance)
		{
			return true;
		}
		return false;
	};
	std::vector<pnode>Matrix1 = Matrix;
	while (!Matrix1.empty())
	{
		Sort(Matrix1);
		auto index = Matrix1.size() - 1;
		auto pcur1 = Matrix1[index];
		auto pcur2 = Matrix1[index]->next;
		Matrix1.pop_back();
		S.push_back(pcur1);
		while (pcur2!=nullptr)
		{
			RELAX(pcur1, pcur2);
			pcur2 = pcur2->next;
		}
		
	}

}
template<typename T>
Graph<T>::Graph(std::string path1, std::string path2)
{
	//讀入鄰接矩陣
	std::string P = path1;
	std::ifstream FILE(P, std::ios::in);
	std::string line;

	int flag1 = 0;
	int flag2 = 0;
	std::vector<std::vector<int>>Matrix;
	while (std::getline(FILE, line))
	{
		std::istringstream L(line);
		std::string n;
		if (flag1 == 0)
		{
			while (std::getline(L, n, ','))
			{

				area.push_back(n);//加入地區
			}
			flag1++;
		}
		else
		{
			flag2 = 0;
			std::vector<int>temp;
			while (std::getline(L, n, ','))
			{
				if (flag2 != 0)
				{
					//讀入鄰接矩陣即可
					temp.push_back(std::stoi(n));
				}
				flag2++;
			}
			Matrix.push_back(temp);
		}
	}
	graph = Matrix;
	convert_to_list();
	FILE.close();//關閉檔案
	
	//讀入權重矩陣
	
	std::ifstream FILE2(path2, std::ios::in);
	std::string line2;

	int flag3 = 0;
	std::vector<std::vector<int>>G;
	while (std::getline(FILE2, line2))
	{


		std::vector<int>p;
		std::stringstream ss(line2);
		std::string n;
		flag3 = 0;
		while (std::getline(ss, n, ',')) {
			if (flag3 != 0) { p.push_back(std::stoi(n)); }
			flag3++;
		}
		G.push_back(p);

	}
	//開始填補空缺的部分
	auto size = G.size();
	for (std::size_t i = 0; i < G.size(); i++)
	{
		if (G[i].size() != size)
		{
			auto size2 = G[i].size();
			for (size_t j = 0; j < size - size2; j++)
			{
				G[i].push_back(0);
			}
		}
	}
	Weight = G;
}
template<typename T>
void Graph<T>::check_AToB(std::string area_)
{
	int limit;//限制的最小路徑
	auto index = find_index(area_);
	assert(index != -1);
	if (index != -1)
	{
		BFS(index,false);
	}

	//列印節點
	std::cout << "Please enter a minimum limit path length:";
	std::cin >> limit;
	std::vector<int>p;
	for (auto s:Matrix)
	{
		p.push_back(s->distance);
	}
	if ((std::accumulate(p.begin(), p.end(), 0)) >(limit * p.size()))
	{
		std::cout << "not exist" << std::endl;
	}
	else
	{
		std::cout << "exists" << std::endl;
		printMinDistance(index,true);
	}
	
}
template <typename T>
void Graph<T>::DFS()
{
	Initialize_Graph();
	for (auto s : Matrix)
	{
		if (s->color == White)
		{
			DFS_VISIT(s);
		}
	}
	std::cout << std::endl;
	auto check = [](std::vector<pnode> Matrix) {
		auto len = 0;
		for (auto s : Matrix)
		{
			if (s->color == black)
			{
				len++;
			}
		}
		if (len == Matrix.size())
		{
			return true;
		}
		else
		{
			return false;
		}
	};
	assert(check(Matrix) == true); //檢驗DFS得正確性是否成立
}
template<typename T>
void Graph<T>::MIN_RODD(std::size_t index, std::size_t mode,bool road)
{

	/*
		par1: index確定從哪一個頂點作為起始的頂點開始列印
		par2: mode確定選擇哪一個演算法作為最短路徑演算法
		par3: 是否列印最短路徑(之前必須初始化area容器)
	*/
	assert(mode == 1 || mode == 2);//mode=1呼叫FORD演算法 mode=2呼叫Dijstra演算法
	if (mode == 1)
	{
		Bellman_Ford(index);
	}
	else if(mode == 2)
	{
		Dijsktra(index);
	}

	//列印路徑
	if (road == true)
	{
		//printMinDistance(index, true);
		for (size_t i = 0; i < area.size(); i++)
		{
			if (i != index)

			{
				print_path(index,i);
			}
		}
		
	}
}
template<typename T>
bool Graph<T>::Bellman_Ford(std::size_t i)
{
	//初始化
	build_edge();
	for (size_t j = 0; j < Matrix.size(); j++)
	{
		if (j != i)
		{
			Matrix[j]->distance = INFINITY;
			Matrix[j]->parents = nullptr;
		}
		if (j == i)
		{
			Matrix[j]->distance = 0;
			Matrix[j]->parents = nullptr;
		}

	}
	//演算法的部分
	bool update;
	for (size_t k = 1; k <Matrix.size(); k++)
	{
		for (std::size_t k2 = 0; k2 < E.size(); k2++)
		{
			//進行鬆弛
			RELAX(E[k2]);
		}
		

	}
	//檢查演算法的正確性
	for (size_t p = 0; p <E.size() ; p++)
	{
		auto index_u = E[i].from;
		auto index_v = E[i].to;
		if (Matrix[index_v]->distance > (Matrix[index_u]->distance + Weight[index_u][index_v]))
		{
			return false;
		}
	}
	return true;
}
template <typename T>
void Graph<T>::DFS_VISIT(pnode vertex)
{
	vertex->color = gray;
	Matrix[vertex->index]->color = gray;
	std::cout << vertex->index << "\t";
	pnode pcur = vertex->next;
	while (pcur != nullptr)
	{
		if (pcur->color == White)
		{
			Matrix[pcur->index]->parents = vertex;
			DFS_VISIT(pcur);
		}
	}
	vertex->color = black;
	Matrix[vertex->index]->color = black;
}
template <typename T>
void Graph<T>::BFS(std::size_t i,bool print)
{
	
	Initialize_Graph();
	std::size_t sum = Matrix.size(); //圖得總共得節點得個數有多少

	pnode first = Matrix[i]; //選擇起始得節點
	root = first;			 //設定廣度優先樹的根節點
	first->color = gray;
	std::queue<pnode> Q;
	Q.push(first);
	//開始遍歷
	std::cout << first->index << "\t";
	std::size_t run_size = 1;
	while (!Q.empty())
	{
		pnode e = Q.front();
		Q.pop();
		pnode pcur = e; //前驅
		e = e->next;

		while (e != nullptr)
		{
			auto index = e->index;
			if (Matrix[index]->color == White)
			{
				Matrix[index]->color = gray;
				std::cout << Matrix[index]->index << "\t";
				Matrix[index]->distance = Matrix[pcur->index]->distance + 1;
				Q.push(Matrix[index]);
				run_size++;
			}
			e = e->next;
		}
	}
	std::cout << std::endl;
	if (print == true)
	{
		printMinDistance(i,false);
	}
	
	assert(run_size == sum);
}
template<typename T>
Graph<T>::Graph(std::string path)
{
	std::string P = path;
	std::ifstream FILE(P, std::ios::in);
	std::string line;
	
	int flag1 = 0;
	int flag2 = 0;
	std::vector<std::vector<int>>Matrix;
	while (std::getline(FILE, line))
	{
		std::istringstream L(line);
		std::string n;
		if (flag1 == 0)
		{
			while (std::getline(L, n, ','))
			{

				area.push_back(n);//加入地區
			}
			flag1++;
		}
		else
		{
			flag2 = 0;
			std::vector<int>temp;
			while (std::getline(L, n, ','))
			{
				if (flag2 != 0)
				{
					//讀入鄰接矩陣即可
					temp.push_back(std::stoi(n));
				}
				flag2++;
			}
			Matrix.push_back(temp);
		}
	}
	graph = Matrix;
	convert_to_list();
}
template <typename T>
void Graph<T>::print_Graph()
{

	for (auto s : Matrix)
	{
		while (s != nullptr)
		{

			if (s->next == nullptr)
			{
				std::cout << s->index;
			}
			else
			{
				std::cout << s->index << "->";
			}
			s = s->next;
		}
		std::cout << std::endl;
	}
	std::cout << "-------------Adj-Matrix---------------" << std::endl;
	for (auto s : graph)
	{
		for (auto s1 : s)
		{
			std::cout << s1 << "\t";
		}
		std::cout << std::endl;
	}
	std::cout << "---------------Weight-Matrix-------------" << std::endl;
	for (auto w : Weight)
	{
		for (auto w1 : w)
		{
			std::cout << w1 << "\t";
		}
		std::cout << std::endl;
	}
}
template <typename T>
void Graph<T>::insert(pnode &phead, pnode pnew)
{
	assert(phead != nullptr);
	pnode pcur = phead;
	while (pcur->next != nullptr)
	{
		pcur = pcur->next;
	}
	pcur->next = pnew;
	
}
template<typename T>
int Graph<T>::find_index(std::string area_)
{
	for (int i = 0; i < area.size(); i++)
	{
		if (area[i] == area_)
		{
			return i;
		}
	}
	return -1;
}

template<typename T>
void Graph<T>::print_path(std::size_t i, std::size_t j)
{
	//i是源節點 j是目的節點
	pnode pcur = Matrix[j];
	//儲存到檔案
	std::ofstream FILE("out.txt", std::ios::app);
	while (pcur!=Matrix[i]&&pcur!=nullptr)
	{
		//std::cout << pcur->index << "->";
		std::cout << area[pcur->index] << "->";
		FILE<< area[pcur->index] << "->";
		pcur = pcur->parents;
	}
	/*for (auto s : Matrix) {
		std::cout << s->distance << std::endl;
	}*/
	std::cout<<area[Matrix[i]->index]<< std::endl;
	FILE << area[Matrix[i]->index] << "\n";
	FILE.close();
}

#endif