算法—图(C++实现)

1.定义

class Edge;
class Node {
public:
	int value;//点的值
	int in;//入度
	int out;//出度
	list<Node*> nexts;//由该节点射出的点
	list<Edge*> edges;//由该节点射出的边
	Node(int val) {
		value = val;
		in = 0;out = 0;
	}
};
class Edge {//有向边,无向边可由有向边转化
public:
	int weight;//权重
	Node* from;
	Node* to;
	Edge(int wei,Node* fr,Node* t) {
		weight=wei;
		from = fr;
		to = t;
	}
};
class Graph {
public://int为编号,Node*为结点信息,比如入度、出度
	unordered_map<int, Node*> nodes;
	unordered_set<Edge*> edge;//边的信息
	Graph(){}
};

2.将表转成图 

问题描述:
/*(表示有向图)
[4,2,3],表示从2城市到3城市的距离为4
[9,5,2],表示从5城市到2城市的距离为9
[6,5,3].表示从5城市到3城市的距离为6
*/
 

Graph* createGraph(int arr[][3],int way) {//way表示几条路,也就是arr的行数
	if (way < 1)//没有路
		return NULL;
	Graph* graph = new Graph();
	for (int i = 0;i < way;i++) {
		int weight = arr[i][0];//获得三信息
		int from = arr[i][1];
		int to = arr[i][2];
		if (graph->nodes.find(from) != graph->nodes.end())//如果结点已经存在了就不加入了
			graph->nodes.insert(make_pair(from, new Node(from)));
		if (graph->nodes.find(to) != graph->nodes.end())
			graph->nodes.insert(make_pair(from, new Node(to)));
		Node* fromNode = graph->nodes[from];
		Node* toNode = graph->nodes[to];
		Edge* newEdge = new Edge(weight, fromNode, toNode);//创建新边
		fromNode->nexts.push_back(toNode);//起点出来的结点
		fromNode->out++;//起点的出度+1
		toNode->in++;//终点的入度+1
		fromNode->edges.push_back(newEdge);//起点出来的边
		graph->edge.insert(newEdge);//图中增加一下边
	}
	return graph;
}

 3.宽度优先遍历

方法:
/*
step:   1)利用队列实现
        2)从源节点开始依次按照宽度进队列,然后弹出来
        3)没弹出一个结点,把该节点所有没有进过队列的结点放进队列(用unordered_set)
        4)直到队列为空
*/ 

void BFS(Node* node) {
	if (node == NULL)
		return;
	queue<Node*> q;
	unordered_set<Node*> s;
	q.push(node);s.insert(node);
	while (!q.empty()) {
		Node* cur = q.front();//从队列中取元素
		q.pop();
		while (!cur->nexts.empty()) {
			Node* next = cur->nexts.front();
			if (s.find(next) == s.end()) {//如果没放过队列,再放
				q.push(next);s.insert(next);
			}
			cout<<next->value<<" ";
		}
		cout<<endl;
	}
}

4.深(广)度优先遍历 

方法: 
/*
step:   1)利用栈,先将头放进去,标记头直接打印
        2)从栈中弹出一个结点,找它的一个邻接点
        3)如果邻接点还未入栈,就再将刚才从栈中弹出来的那个压入栈
        4)再将next压入栈中,并标记一下并打印
        5)就不再看邻接点了,直接回到第2步弹出,2、3、4循环往复,直到栈空
*/

//只逮着一条路走到黑
void DFS(Node* node) {
	if (node == NULL)
		return;
	stack<Node*> stk;
	unordered_set<Node*> set;
	stk.push(node);//先将头放进去
	set.insert(node);//标记头
	cout << node->value << " ";//直接打印
	while (!stk.empty()) {
		Node* cur=stk.top();
		stk.pop();
		Node* next = cur->nexts.back();
		if (set.find(next) != set.end()) {//如果还未入栈
			//再将刚才弹出来的那个压入栈
			stk.push(cur);
			//再将next压入栈中,并标记一下
			stk.push(next);set.insert(next);
			//打印next
			cout << next->value << " ";
			//直接回到最出栈位置
			break;
		}
	}
	cout << endl;
}

 
5.拓扑排序

 方法:
/*
step:   1)先找到入度为0的结点
        2)再将与该节点有关的边删去,以及邻接点的入度-1
        3)循环往复
*/

list<Node*> sortedTopology(Graph* graph) {
	unordered_map<Node*, int> m;//int为结点的当前入度
	unordered_set<Node*> zeroInNode;//装入度为0的结点
	for (unordered_map<int, Node*>::const_iterator it = graph->nodes.begin();
		it != graph->nodes.end();it++){
		Node* cur = it->second;
		m.insert(make_pair(cur, cur->in));//从图中拿出来的结点,以及对应的入度
		if (cur->in == 0)//如果入度为0就塞进去
			zeroInNode.insert(cur);
	}
	list<Node*> result;//拓扑排序的结果储存在这里
	while (!zeroInNode.empty()) {//如果入度为0的结点没完就进行操作
		Node* cur = *zeroInNode.begin();//因为begin返回的是迭代器,所以要解引用
		result.push_back(cur);
		for (Node* next: cur->nexts) {
			m[next]--;
			if(m[next]==0)//在更改的过程中如果入度为0就加入到
				zeroInNode.insert(next);
		}
	}
	return result;
}

6.求最小生成树

1)K算法 

思路: 

 /*
K算法:依次遍历权重最小的边,如果不构成环就加上这条边
step:   1)先将所有点单独放入集合
        2)开始按照权重从小到大遍历,找到最小的边,如果两端点不在同一集合的情况下,
          将该边的两个端点的集合合并,如果在同一集合了,就跳过这条边
        3)不断遍历边,直到遍历完
*/

unordered_map<Node*, list<Node*>> setMap;//第二个参数是包含第一个参数的集合
//设置集合
void mySet(list<Node*>nodes) {
	for (Node* cur : nodes) {
		list<Node*>set;
		set.push_back(cur);
		setMap[cur] = set;//当前就只将自己单独放入一个集合
	}
}
//判断是否一条边的两个端点在同一个集合
bool isSameSet(Node* from, Node* to) {
	list<Node*>fromSet = setMap[from];
	list<Node*>toSet = setMap[to];
	return fromSet == toSet;//只要在同一个集合里面,地址就相同
}
//将from所在集合和to所在集合合并在一起
void merge(Node* from, Node* to) {
	list<Node*>fromSet = setMap[from];
	list<Node*>toSet = setMap[to];
	for (Node* curTo : toSet) {
		fromSet.push_back(curTo);
		setMap.insert(make_pair(curTo, fromSet));//此时from和to所在setMap属于同一内存地址
	}
}
//生成最小树:K算法
list<Node*> smallestTreeKruskal(Graph* graph) {
	//先将graph的边排序
	set<Edge*> ed;
	for (Edge* curEdge : graph->edge)
		ed.insert(curEdge);//ed会自动按照key值排序
	for (Edge* curEdge : ed) {
		Node* from = curEdge->from;//拿到边的两个端点
		Node* to = curEdge->to;
		if (!isSameSet(from, to)) {//不在同一容器就合并
			merge(from, to);
		}
	}
	return setMap.begin()->second;//返回点集,既有点值的信息,又有边的信息
}

 2)P算法 

方法: 
/*
step:   1)从点出发,随便选中一个点,将与该点有关的所有边全部标记一下,
          在标记过的边里选中权最小的边记录;
        2)然后再看那条边的另一端,再标记与之有关的边,循环往复;
        3)最后将独立的树连接到一起。
*/

//比较器
class EdgeComparator {
public:
	int compare(Edge* o1, Edge* o2) {//排序方式,从小往大
		return o1->weight - o2->weight;
	}
};
unordered_set<Edge*> primMST(Graph* graph) {
	//解锁的边进入小根堆
	priority_queue<Edge*> priorityQueue;
	unordered_set<Node*> set;//标记点
	unordered_set<Edge*> result;//依次挑选的边在result里面
	for (Node* node : graph->nodes.begin()->second) {//随便找一个点就行
		//node是开始点
		if (set.find(node) == set.end()) {//如果还没遍历过该点
			set.insert(node);
			for (Edge* edge : node->edges) {//由一个点解锁所有相连的边
				priorityQueue.push(edge);
			}
			while (!priorityQueue.empty()) {
				Edge* edge = priorityQueue.top();//弹出解锁边中的最小的边
				Node* toNode = edge->to;//可能是新的点
				if (set.find(toNode) != set.end()) {//如果不含有就是新的点
					set.insert(toNode);
					result.insert(edge);//将边装进结果里面
					for (Edge* nextEdge : toNode->edges)
						priorityQueue.push(nextEdge);//将新的点的相邻的边解锁
				}
			}
		}
	}
	return result;
}

 7.求从某点出发到所有点的距离

 方法:
/*
dijkstral算法:适用范围:若有环则环的权值之和必须不为负数
*/

unordered_map<Node*, int> dijkstral(Node* head) {
	//返回值的第一个参数:从head出发到达key,
	//返回值第二个参数:从head出发到达key的最小长度
	//如果在表中不含T,则说明从head到达T的距离为正无穷,不能到达
	unordered_map<Node*, int> distanceMap;
	distanceMap.insert(make_pair(head, 0));//自己到自己距离为0
	//锁定的结点放在selectNode中
	unordered_set<Node*>selectedNode;
	//从distanceMap中选择没有选过的最小距离的结点
	Node* minNode = geiMinDistanceAndUnselected(distanceMap, selectedNode);
	while (minNode != NULL) {
		int distance = distanceMap[minNode];
		for (Edge* edge : minNode->edges) {
			Node* toNode = edge->to;
			if (distanceMap.find(toNode) != distanceMap.end()) {
				distanceMap.insert(make_pair(toNode, distance + edge->weight));
			}
			//更新
			distanceMap.insert(make_pair(edge->to, 
				min(distanceMap[toNode], distance + edge->weight)));		
		}
		selectedNode.insert(minNode);
		minNode= geiMinDistanceAndUnselected(distanceMap, selectedNode);
	}
	return distanceMap;
}
//找最小路径对应的节点
Node* geiMinDistanceAndUnselected(unordered_map<Node*, int>distanceMap, 
	unordered_set<Node*> selectedNode) {
	Node* minNode;
	int minValue = INT_MAX;
	for (pair<Node*, int> cur : distanceMap) {//获得对组
		Node* node = cur.first;
		int distance = cur.second;
		if (selectedNode.find(node) != selectedNode.end() && minValue > distance) {
			minNode = node;
			minValue = distance;
		}	
	}
	return minNode;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值