namespace link_table
{
/*
邻接表的实现类似于哈希桶
下标与顶点的下标一致
挂起的是 "边"
*/
template<class W>
struct Edge
{
int _srci;//起点
int _dsti;//终点
W _w; //权值
Edge<W>* _next;//连接到邻接表中去
Edge(int srci, int dsti, const W& w)
:_srci(srci),_dsti(dsti),_w(w)
,_next(nullptr)
{ }
bool operator>(const Edge& e)const
{
return _w > e._w;
}
};
//邻接表
template<class V, class W, bool Direction = false>
class graph
{
typedef Edge<W> Edge;
typedef graph<V, W, Direction> self;
public:
graph(){}
//手动初始化
graph(const V* a, size_t n)
{
_vertexes.reserve(n);
for (int i = 0; i < n; ++i)
{
_vertexes.push_back(a[i]);
_index[a[i]] = i;
}
//n行n列全部初始化为最大值
_tables.resize(n);
}
int GetIndexOfVertexes(const V& v)
{
//find比count高效,
//find找到立即返回,count需要找完所有数据
auto it = _index.find(v);
if (it == _index.end())
throw invalid_argument("无效参数");
return it->second;
}
void _AddEdge(int srci, int dsti, const W& w)
{
//构造边
Edge* eg = new Edge(srci, dsti, w);
//头插
eg->_next = _tables[srci];
_tables[srci] = eg;
if (Direction == false)
{
//构造边
Edge* eg = new Edge(dsti, srci, w);
//头插
eg->_next = _tables[dsti];
_tables[dsti] = eg;
}
}
void AddEdge(const V& src, const V& dst, const W& w)
{
//得到起点和终点的下标
int srci = GetIndexOfVertexes(src);
int dsti = GetIndexOfVertexes(dst);
_AddEdge(srci, dsti, w);
}
void Print()
{
//打印 下标及其对应的顶点值
for (int i = 0; i < _vertexes.size(); ++i)
{
cout << '[' << i << "] -> " << _vertexes[i] << endl;
}
cout << endl;
//打印邻接表
for (int i = 0; i < _tables.size(); ++i)
{
//打印起点
cout << "[ " << _vertexes[i] << " : " << i << " ] -> ";
//起点对应的边
Edge* cur = _tables[i];
while (cur)
{
cout << "[ " << _vertexes[cur->_dsti] << " : "
<< cur->_dsti << " : " << cur->_w << " ]" << " -> ";
cur = cur->_next;
}
cout << "nullptr" << endl;
}
}
//广度优先遍历
//从一个顶点出发,访问它所连接的未被访问过的顶点
void BFS(const V& src)
{
int n = _vertexes.size();
int srci = GetIndexOfVertexes(src);
//队列和标记数组
queue<int> q;
vector<bool> visited(n, false);
//进队列就标记,防止同一元素多次入队列
q.push(srci);
visited[srci] = true;
//每层的个数
int levelsize = 1;
//开始遍历
while (!q.empty())
{
//一层一层的出
for (int i = 0; i < levelsize; ++i)
{ //访问队头元素
int front = q.front();
q.pop();
cout << front << " : " << _vertexes[front] << ' ';
//所连接的未被访问过的顶点入队列
Edge* cur = _tables[front];
while (cur)
{
if (visited[cur->_dsti] == false)
{
q.push(cur->_dsti);
visited[cur->_dsti] = true;
}
cur = cur->_next;
}
}
cout << endl;
levelsize = q.size();
}
}
//栈溢出风险
//void DFS(const V& src)
//{
// int n = _vertexes.size();
// int srci = GetIndexOfVertexes(src);
// vector<bool> visited(n, false);
// _DFS(srci, visited);
//}
//void _DFS(int srci, vector<bool>& visited)
//{
// //立即访问并标记
// cout << srci << " : " << _vertexes[srci] << endl;
// visited[srci] = true;
// //访问下一个相连但未访问过
// Edge* cur = _tables[srci];
// while (cur)
// {
// if (visited[cur->_dsti] == false)
// _DFS(cur->_dsti, visited);
// cur = cur->_next;
// }
//}
void DFS(const V& src)
{
int n = _vertexes.size();
int srci = GetIndexOfVertexes(src);
//将与当前顶点相连的第一条边入栈
stack<Edge*> st;
Edge* cur = _tables[srci];
while (cur)
{
if (visited[top->_dsti] == false)
break;
cur = cur->_next;
}
if(cur)
st.push(cur);
//标记数组
vector<bool> visited(n, false);
//访问当前顶点
visited[srci] = true;
cout << srci << ":" << _vertexes[srci] << endl;
while (!st.empty())
{
//访问栈顶元素
Edge* top = st.top();
if(visited[top->_dsti] == false)//这条边未被访问过
{
visited[top->_dsti] = true;
cout << top->_dsti << ":" << _vertexes[top->_dsti] << endl;
cur = _tables[top->_dsti];//相连的边中去
}
else//这条边被访问过
{
cur = top->_next;//其他边
st.pop();
}
//遍历邻接表,与其连接的未被访问过的顶点
while (cur)
{
if (visited[cur->_dsti] == false)
break;
cur = cur->_next;
}
//未被访问过
if (cur)
{
st.push(cur);
}
}
}
//全局寻找边
W Kruskal(self& minTree)
{
int n = _vertexes.size();
minTree._index = _index;
minTree._vertexes = _vertexes;
minTree._tables.resize(n, nullptr);
//优先级队列存储边
priority_queue<Edge, vector<Edge>, greater<Edge>> minpq;
for (int i = 0; i < _tables.size(); ++i)
{
//无向图的邻接表中,边存了双份
Edge* cur = _tables[i];
while (cur)
{
minpq.push(*cur);
cur = cur->_next;
}
}
UnionFindSet ufs(n);
//权值、边数
W totalw = W();
int size = 0;
//开始选边
while (!minpq.empty())
{
//选出最小边
Edge min = minpq.top();
minpq.pop();
//判环
if (ufs.InSet(min._srci, min._dsti))
{
cout << "构成环:" << _vertexes[min._srci] << "->" << _vertexes[min._dsti]
<< ":" << min._w << endl;
continue;
}
//进入最小生成树
minTree._AddEdge(min._srci, min._dsti, min._w);
cout << _vertexes[min._srci] << "->" << _vertexes[min._dsti]
<< ":" << min._w << endl;
//进入最小生成树的顶点在同一个集合
ufs.Union(min._srci, min._dsti);
++size;
totalw += min._w;
if (size == n - 1)
break;
}
if (size == n - 1)
return totalw;
else return W();
}
//普利姆算法的实现刚好弥补了边进两次的缺陷
W Prim(self& minTree, const V& src)
{
int srci = GetIndexOfVertexes(src);
int n = _vertexes.size();
minTree._index = _index;
minTree._vertexes = _vertexes;
minTree._tables.resize(n, nullptr);
//在最小生成树中的顶点
vector<bool> in(n, false);
in[srci] = true;
//优先级队列存储边
priority_queue<Edge, vector<Edge>, greater<Edge>> minpq;
//从当前节点连接出去的边
//无向图的邻接表中,边存了双份
Edge* cur = _tables[srci];
while (cur)
{
minpq.push(*cur);
cur = cur->_next;
}
//权值、边数
W totalw = W();
int size = 0;
//开始选边
while (!minpq.empty())
{
//选出最小边
Edge min = minpq.top();
minpq.pop();
//判环
if (in[min._dsti] == true)
{
cout << "构成环:" << _vertexes[min._srci] << "->" << _vertexes[min._dsti]
<< ":" << min._w << endl;
continue;
}
//进入最小生成树
minTree._AddEdge(min._srci, min._dsti, min._w);
cout << _vertexes[min._srci] << "->" << _vertexes[min._dsti]
<< ":" << min._w << endl;
//进入最小生成树的顶点
in[min._dsti] = true;
++size;
totalw += min._w;
if (size == n - 1)
break;
//先做上述判断,再持续进边
Edge* cur = _tables[min._dsti];
while (cur)
{
//边的顶点不再最小生成树内
if(in[cur->_dsti] == false)
minpq.push(*cur);
cur = cur->_next;
}
}
if (size == n - 1)
return totalw;
else return W();
}
private:
vector<V> _vertexes;//顶点集合
unordered_map<V, int> _index;//顶点映射下标
vector<Edge*> _tables;//邻接表 存放边的集合
};
void TestGraph()
{
string a[] = { "张三", "李四", "王五", "赵六" };
graph<string, int> g1(a, 4);
g1.AddEdge("张三", "李四", 100);
g1.AddEdge("张三", "王五", 200);
g1.AddEdge("王五", "赵六", 30);
g1.Print();
}
void TestGraphDBFS()
{
string a[] = { "张三", "李四", "王五", "赵六", "周七" };
graph<string, int> g1(a, sizeof(a) / sizeof(string));
g1.AddEdge("张三", "李四", 100);
g1.AddEdge("张三", "王五", 200);
g1.AddEdge("王五", "赵六", 30);
g1.AddEdge("王五", "周七", 30);
g1.BFS("张三");
cout << endl;
g1.DFS("张三");
}
void TestGraphMinTree()
{
const char* str = "abcdefghi";
graph<char, int> g(str, strlen(str));
g.AddEdge('a', 'b', 4);
g.AddEdge('a', 'h', 8);
//g.AddEdge('a', 'h', 9);
g.AddEdge('b', 'c', 8);
g.AddEdge('b', 'h', 11);
g.AddEdge('c', 'i', 2);
g.AddEdge('c', 'f', 4);
g.AddEdge('c', 'd', 7);
g.AddEdge('d', 'f', 14);
g.AddEdge('d', 'e', 9);
g.AddEdge('e', 'f', 10);
g.AddEdge('f', 'g', 2);
g.AddEdge('g', 'h', 1);
g.AddEdge('g', 'i', 6);
g.AddEdge('h', 'i', 7);
graph<char, int> kminTree;
cout << "Kruskal:" << g.Kruskal(kminTree) << endl;
//kminTree.Print();
cout << endl;
graph<char, int> pminTree;
cout << "Prim:" << g.Prim(pminTree, 'a') << endl;
//pminTree.Print();
}
}
void DFS(const V& src)函数实现的广度优先遍历合理吗