图-数据结构
作者:互联网
常见表达图的形式有,邻接表:数组+链表
邻接矩阵:二维数组
从这组成结构上看,邻接表适合表达稀疏图,邻接矩阵适合表达稠密图
当然表达图结构的方式不止只有上面两种结构。
下面自己定义图的结构,由以下部分组成:
1.图的结构 a.顶点,b.边 ,c.图 d.图生成器
2.图的经典算法:a,深度优先搜索 ,b.广度优先搜索 ,c.拓扑排序 ,d.最小生成树p算法 e.单源最短路径dijkstra算法
// 图.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//2021.7.21
//尽量根据实际情况舍弃一些多余的结构,比如顶点,边,图数据类型,只保存对实际情况有用的数据,使空间复杂度尽可能的小
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include <string>
#include <cstring>
#include <queue>
#include <set>
#include <stack>
#include <map>
using namespace std;
//前置声明
class Node;
class Edge;
class Grape;
class Grapegenerator; //图生成器
//点
class Node {
public:
int value; //值
int in; //入度
int out; //出度
//int key;
vector<Node>* nexts; //发散出去的点
vector<Edge>* edges; //发散出去的边
//构造函数
Node(int val)
{
this->value = val;
in = 0; //入度
out = 0; //出度
nexts = new vector<Node>(); //由该点发散出来的点集
edges = new vector<Edge>(); //由该点发散出来的边集
}
bool operator < (const Node &a) const
{
return value < a.value; //图这个数据结构中,value 就是key,不存在两个值相同的顶点
}
//析构函数这可以不写 不可乱写,只要图不消亡就不能清内存 可以在图生成器的类中做析构,将所有new出来的数据清
/*~Node()
{
delete nexts;
delete edges;
}*/
};
/*
bool operator == (const Node& a,const Node &b)
{
return a.value == b.value && a.in == b.in && a.out == b.out && a.nexts == b.nexts && a.edges == b.edges;
}
struct cmpp {
int operator ()(const Node &a)const
{
int zhi = a.value;
return std::hash<int>()(zhi);
}
};*/
//边
class Edge {
public:
int weight; //权重
Node *from; //从哪个点出发
Node *to; //去哪个点结束
//构造函数
Edge(int wgt, Node *from, Node *to) :weight(wgt), from(from), to(to) {}
/*bool operator <(Edge &a)const
{
return weight > a.weight;
}*/
};
struct cmp {
bool operator ()(Edge &a,Edge &b)
{
return a.weight > b.weight;
}
};
//图
class Grape {
public:
unordered_map<int, Node>* nodes;
unordered_map<string,Edge>* edges; //点集和边集
//构造函数
Grape()
{
nodes = new unordered_map<int, Node>(); //每个点有个key作为其编号
edges = new unordered_map<string,Edge>(); //每条边有个key 作为其编号
}
~Grape()
{
for (auto i : *nodes)
{
delete i.second.nexts;
}
delete nodes;
delete edges;
}
};
class Grapegenerator {
public:
Grapegenerator(const vector<vector<int>> &inmtx) //以一个二维数组作为输入 根据实际需要可以对图做剪枝(只保留所需要的信息)
{
mygrape = new Grape();
for (int i = 0; i < inmtx.size(); i++)
{
int weight = inmtx[i][0];
int from = inmtx[i][1];
int to = inmtx[i][2]; //获取一组点的信息
if (mygrape->nodes->find(from) == mygrape->nodes->end())
{
mygrape->nodes->emplace(from, Node(from));
}
if (mygrape->nodes->find(to) == mygrape->nodes->end())
{
mygrape->nodes->emplace(to, Node(to)); //图的点集
}
Node *fromNode = &(mygrape->nodes->at(from));
Node *toNode = &(mygrape->nodes->at(to));
Edge newEdge = Edge(weight, fromNode, toNode);
fromNode->out++;
toNode->in++;
fromNode->nexts->emplace_back(*toNode);
fromNode->edges->emplace_back(newEdge);
string edge_key = to_string(from); // keg_edge
edge_key.push_back('-');
edge_key += to_string(to);
//cout << edge_key << endl;
mygrape->edges->emplace(edge_key, newEdge);//给边做个标号 //图的边集
}
}
~Grapegenerator()
{
delete mygrape;
}
//图的深度优先遍历 需要一个出发节点
void dfs()
{
Node *node = &mygrape->nodes->at(1); //选定一个出发点
if (node == nullptr)
{
return;
}
stack<Node*> mystack;
set<Node> hstable;
mystack.push(node);
hstable.emplace(*node);
cout << node->value << endl;
while (!mystack.empty())
{
Node *temp = mystack.top();
mystack.pop();
for (int i = 0; i < temp->nexts->size(); i++)
{
if (hstable.find(temp->nexts->at(i)) == hstable.end())
{
mystack.push(temp);
mystack.push(&temp->nexts->at(i));
hstable.emplace(temp->nexts->at(i));
cout << temp->nexts->at(i).value << endl;
break; //退出进入下一个节点 栈中实际上是保存了深度优先遍历的路径
}
}
}
}
//图的宽度优先遍历 需要一个出发节点 每一层的顺序不重要,但是层的关系要体现出来 队列
void bfs()
{
Node *node = &(mygrape->nodes->at(1));
if (node == nullptr) //无效数据直接退出
{
return;
}
queue<Node *> myqueue; //队列
set<Node> hstable; //备忘录
myqueue.emplace(node);
hstable.emplace(*node);
while (!myqueue.empty())
{
int n = myqueue.size();
vector<int> vec;
for (int j = 0; j < n; j++)
{
Node *temp = myqueue.front();
myqueue.pop();
//cout << temp->value << endl;
vec.emplace_back(temp->value);
for (int i = 0; i < temp->nexts->size(); i++)
{
if (hstable.find(temp->nexts->at(i)) == hstable.end()) //查备忘录
{
hstable.emplace(temp->nexts->at(i));
myqueue.push(&(temp->nexts->at(i)));
}
}
}
for (auto i : vec)
{
cout << i << ' ';
}
cout << endl;
}
}
//拓朴排序 顶点之间的依赖关系 有向图 有入度为0的顶点,没有环
vector<int> tpsort()
{
set<Node> hstable;
queue<Node> myqueue;
for (auto i : *(mygrape->nodes))
{
hstable.emplace(i.second);
if (i.second.in == 0)
{
myqueue.push(i.second);
}
}
vector<int> ans;
while (!myqueue.empty())
{
Node temp = myqueue.front();
myqueue.pop();
ans.emplace_back(temp.value); //压入值
for (Node i : *(temp.nexts))
{ //这里要注意,这里很奇怪,直接修改指针指向的内容会有问题
Node next = *hstable.find(i);
hstable.erase(hstable.find(i)); //清除之前的那个顶点
next.in--;
hstable.emplace(next); //压入改变in之后的顶点
if (hstable.find(i)->in == 0)
{
myqueue.push(i);
}
}
}
return ans;
}
//最小生成树p算法 k算法需要不断的合并集合,所以不推荐
int ptree()
{
priority_queue<Edge,vector<Edge>,cmp> s_heal; //以边的weight 由小权重开始排 小根堆
//vector<Edge> ans; //存放结果
int ans = 0;
set<Node> hstable; //备忘录
for (auto i:*(mygrape->nodes)) //任何一个起点
{
hstable.emplace(i.second);
for (Edge j:*i.second.edges) //由一个点解锁一票边
{
s_heal.emplace(j);
}
while (!s_heal.empty())
{
Edge temp = s_heal.top(); //取出权值最小的边
s_heal.pop();
if (hstable.find(*temp.to) == hstable.end()) //查备忘录
{
//ans.emplace_back(temp);
ans += temp.weight;
hstable.emplace(*temp.to);
for (Edge next : *temp.to->edges)
{
s_heal.emplace(next);
}
}
}
break;
}
return ans;
}
//迪杰特斯拉算法 dijkstra 适合没有权值为负数的边的图
//用最小的点去遍历能得到最优的结果
Node getminandunusepoint(map<Node,int> &a,set<Node> &b)
{
int minval = INT_MAX;
Node ans = Node(0); //临时对象
for (auto i : a)
{
Node node = i.first;
int distance = i.second;
if (b.find(node) == b.end() && distance<minval)
{
ans = node;
minval = distance;
}
}
return ans;
}
map<Node, int> dijkstra()
{
Node node = mygrape->nodes->at(1); //起始点
map<Node, int> distance;
distance.emplace(node,0); //自己到自己距离为0
set<Node> hstable; //存放已经摸过的点
Node temp = getminandunusepoint(distance,hstable); //获取最小可用的顶点
while (temp.value != 0 ) //0一般不为出现,因为我们的图设定从1开始的
{
int dis = distance[temp];
for (Edge i : *(temp.edges)) //放入多个点
{
Node toNode = *i.to;
if (distance.find(toNode) == distance.end()) //还没加入备忘录中
{
distance.emplace(toNode,i.weight+dis);
}
else { //已经加入备忘录中的
distance[toNode] = min(distance[toNode],i.weight+dis);
}
}
hstable.emplace(temp); //不可以用了
temp = getminandunusepoint(distance,hstable); //更新点
}
return distance;
}
private:
Grape* mygrape;
};
int main()
{
cout << "********************开始*********************" << endl;
cout << "图中有几个点" << endl;
cout << "权重 from to" << endl;
vector<vector<int>> test;
vector<int> temp;
int n;
cin >> n;
for (int i = 0;i<n;i++)
{
for (int j = 0;j<3;j++)
{
int mid;
cin >> mid;
temp.emplace_back(mid);
}
test.emplace_back(temp);
temp.clear();
}
Grapegenerator zj = Grapegenerator(test);
//zj.bfs();
//zj.dfs();
/*vector<int> temp1;
temp1 = zj.tpsort();
for (int i:temp1)
{
cout << i << endl;
}*/
/*vector<Edge> myedge;
myedge = zj.ptree();
for (Edge i:myedge)
{
cout << i.weight << endl;
}*/
map<Node,int> ans;
ans = zj.dijkstra();
for (auto i : ans)
{
cout << i.first.value << ':' << i.second << endl;
}
return 0;
}
标签:Node,emplace,temp,int,hstable,nexts,数据结构 来源: https://blog.csdn.net/weixin_41710007/article/details/119010715