图的基本算法

图的基本概念及图演算法 文章目录一,图的定义的基本术语二,邻接(Adjacency)2.1 列表表示(Adjacency-List)2.2 矩阵表示(Adjacency-Matrix)三,图的遍历3.1 深度优先搜索3.1.1 深度优先搜索遍历算法的实现3.1.2 DFS 算法效率分析3.1.3 非连通图的遍历3.2 广度优先搜索 回顾:数据的逻辑结构 一,图的定义的基本术语 图: G =(V,E) V:顶点(数据元素)的有穷非空集合; E:边的有穷集合。 无向图(Undirected graph): 每条边的没有方向,eg 阅读详情

图就是边和点的集合
任何图都可以看做是有向图
图的两种表示方法:邻接表和邻接矩阵
最常用的表示方法还有用二维数组表示:每一行第一个表示边的权重,第二个表示from结点的值,第三个表示to结点的值

点的结构

class Node {
public:

	int value;//结点的值
	int in;//入度
	int out;//出度
	list<Node*>nexts;//邻居结点(只包括由本身指向的结点)
	list<Edge*>edges;//从本身指出的边
	Node(int value) :value(value) {
		in = 0;
		out = 0;
	}
};

边的结构

class Edge {
public:
	int weight;//边的权重
	Node* from;
	Node* to;
	Edge(int weight, Node* from, Node* to) {
		this->weight = weight;
		this->from = from;
		this->to = to;
	}

};

完整代码如下

#include<iostream>
#include<list>
#include<algorithm>
#include<unordered_map>
#include<unordered_set>
using namespace std;
class Edge;
class Node {
public:
	int value;
	int in;//入度
	int out;//出度
	list<Node*>nexts;//邻居结点(只包括由本身指向的结点)
	list<Edge*>edges;//从本身指出的边
	Node(int value) :value(value) {
		in = 0;
		out = 0;
	}
};
class Edge {
public:
	int weight;//边的权重
	Node* from;
	Node* to;
	Edge(int weight, Node* from, Node* to) {
		this->weight = weight;
		this->from = from;
		this->to = to;
	}

};
class Graph {
public:
	unordered_map<int, Node*>nodes;//点的集合(通过key找到特定的结点,key就是结点上的值)
	unordered_set<Edge*>edges;//边的集合

};

void creatGraph(vector<vector<int>>graph,Graph& gra) {//由二维数组构造图
	for (int i = 0; i < graph.size(); i++) {
		int weight = graph[i][0];
		if (!gra.nodes.count(graph[i][1])) {//没有from结点
				Node* from = new Node(graph[i][1]);
				gra.nodes.emplace(graph[i][1], from);
		}
		if (!gra.nodes.count(graph[i][2])) {//没有to结点
			Node* to = new Node(graph[i][2]);
			gra.nodes.emplace(graph[i][2], to);
		}
		Node* from = gra.nodes[graph[i][1]];
		Node* to = gra.nodes[graph[i][2]];
		Edge* edge = new Edge(graph[i][0], from, to);
		from->out++;
		from->nexts.push_back(to);
		from->edges.push_back(edge);
		to->in++;
		gra.edges.emplace(edge);
	}
}

int main()
{
	Graph gra;
	vector<vector<int>>graph = { {1,1,3},{2,3,2},{3,2,1} };//N*3的二维数组,每一行[边的权重,from结点到值,to结点的值],所以N就代表N条边
	creatGraph(graph, gra);
	
	
	
	return 0;
}

遍历方式:

  1. 宽度优先遍历
    数据结构:队列和set
    用set记录访问过的结点,避免同一个结点多次访问
void bfs(Node* start) {
	if (start == nullptr) {
		return;
	}
	queue<Node*>q;
	unordered_set<Node*>set;
	q.push(start);
	set.emplace(start);
	while (!q.empty()) {
		Node* cur = q.front();//当前队首元素
		cout << cur->value << endl;
		q.pop();
		for (Node* next : cur->nexts) {
			if (set.find(next) == set.end()) {//如果next结点之前没有遍历过,就加入队列
				q.push(next);
				set.emplace(next);
			}
		}
	}
}
  1. 深度优先遍历

递归实现

void dfs(Node* start,unordered_set<Node*>& set) {
	cout << start->value << endl;
	set.emplace(start);
	for (Node* next : start->nexts) {
		if (set.find(next) == set.end()) {//找到第一个没有访问过的邻居结点
			dfs(next, set);
		}
	}
}

迭代

在任何时刻,栈里放的是当前走过的结点(从底向上)

unordered_set<Node*>set;
	stack<Node*>st;
	st.push(gra.nodes[1]);
	cout << gra.nodes[1]->value << endl;
	set.emplace(gra.nodes[1]);
	while (!st.empty()) {
		Node* cur = st.top();
		st.pop();
		for (Node* next : cur->nexts) {
			if (set.find(next) == set.end()) {
				cout << next->value << endl;
				st.push(cur);//因为回退还会回到cur结点
				st.push(next);
				set.emplace(next);
				break;//找到一个邻居结点就重新找下一个结点
			}
		}
	}

拓扑序:有向无环图
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序。
应用:拓扑排序常用来确定一个依赖关系集中,事物发生的顺序。例如,在日常工作中,可能会将项目拆分成A、B、C、D四个子部分来完成,但A依赖于B和D,C依赖于D。为了计算这个项目进行的顺序,可对这个关系集进行拓扑排序,得出一个线性的序列,则排在前面的任务就是需要先完成的任务。
注意:这里得到的排序并不是唯一的!就好像你早上穿衣服可以先穿上衣也可以先穿裤子,只要里面的衣服在外面的衣服之前穿就行。

实现一:利用入度,每次选择入度为0的点,然后消除该结点的影响,重复该操作,直到没有入度为0的结点。

list<Node*> DAG(Graph graph) {
	list<Node*>result;
	queue<Node*>q;//只要入度为0的点才会放入队列,因此队列的结点都是不依赖其他结点的
	unordered_map<Node*, int>inMap;//key为某个结点,value为结点的入度(加快查询速度)
	for (pair<const int,Node*> next : graph.nodes) {
		inMap.emplace(next.second, next.second->in);
		if (next.second->in == 0) {//初始将入度为0的点加入队列,如果没有入度为0的点,就没有拓扑排序
			q.push(next.second);
		}
	}

	while (!q.empty()) {
		Node* cur = q.front();
		result.push_back(cur);
		q.pop();
		for (Node* next : cur->nexts) {
			inMap[next]--;//邻居结点入度减一;
			if (inMap[next] == 0) {//入度为0的点加入队列
				q.push(next);
			}
		}
	}
	return result;
}

实现二:利用点次,点次大的结点在前

127 · 拓扑排序
先解释一下本题的图表示
{1,2,4#2,1,4#3,5#4,1,2#5,3}
用#来分割各个结点,这是内部已经写好了,不需要管怎么分割的
上例可以看出一共有5个结点,1,2,3,4,5
1结点的直接邻居有2,4
2结点的直接邻居有1,4
……
它这种其实就是邻接表法表示

分析:如果X结点能走过的结点个数>Y结点能走过的结点个数,那么拓扑排序X一定在Y前面
因此:先计算所有结点的点次,然后将结点按点次从大到小排,这个顺序就是拓扑序

priority_queue的队头元素:top()

/**
 * Definition for Directed graph.
 * struct DirectedGraphNode {
 *     int label;
 *     vector<DirectedGraphNode *> neighbors;
 *     DirectedGraphNode(int x) : label(x) {};
 * };
 */
class Record{//点次
public:
    DirectedGraphNode* node;
    long nodes;//node结点能走过几个结点
    Record(DirectedGraphNode*node,long nodes){
        this->node=node;
        this->nodes=nodes;
    }
};

class Solution {
public:
    /**
     * @param graph: A list of Directed graph node
     * @return: Any topological order for the given graph.
     */
    //当前来到cur点,返回cur可以走过的所有的点次
    //缓存:order,key:cur的点次算过了,value:该结点的点次
    Record* f(DirectedGraphNode* cur,unordered_map<DirectedGraphNode*,Record*>&order){
        if(order.count(cur)){//cur结点的点次算过了就直接返回
            return order[cur];
        }
        //计算cur的点次
        long nodes=0;
        for(DirectedGraphNode* next:cur->neighbors){
            nodes+=f(next,order)->nodes;
        }
        Record* ans=new Record(cur,nodes+1);//再加上本身就是该结点对应的点次
        order.emplace(cur,ans);//在缓存上填写cur的点次
        return ans;
    }
    static bool cmp(Record* &r1,Record* &r2){
        return r1->nodes > r2->nodes;//按点次从高到低排
    }
    vector<DirectedGraphNode*> topSort(vector<DirectedGraphNode*> graph) {
        // write your code here
        unordered_map<DirectedGraphNode*,Record*>order;
        for(DirectedGraphNode* cur:graph){
            f(cur,order);//计算每一个结点的点次,order里存的就是所有点的点次
        }
       
        vector<DirectedGraphNode*>result;//存放结果
        vector<Record*>recordAll;//所有结点的点次
        for(pair<const DirectedGraphNode*,Record*> cur:order){//拿出所有点的点次
            recordAll.push_back(cur.second);
        }
        sort(recordAll.begin(),recordAll.end(),cmp);//将点次按从大到小排序,即为拓扑序
        for(Record* r:recordAll){
            result.push_back(r->node);
        }

        return result;
    }
};

实现三:深度法,深度大的结点排前面

分析:和点次法步骤一致,也是先计算所有结点的最大深度,深度大的结点排前面。

/**
 * Definition for Directed graph.
 * struct DirectedGraphNode {
 *     int label;
 *     vector<DirectedGraphNode *> neighbors;
 *     DirectedGraphNode(int x) : label(x) {};
 * };
 */
class MaxDepth{
public:
    DirectedGraphNode* node;
    long maxDepth;
    MaxDepth(DirectedGraphNode* node,long maxDepth){
        this->node=node;
        this->maxDepth=maxDepth;
    }
};

class Solution {
public:
    /**
     * @param graph: A list of Directed graph node
     * @return: Any topological order for the given graph.
     */
    static bool cmp(MaxDepth* md1,MaxDepth* md2){
        return md1->maxDepth>md2->maxDepth;
    }
    MaxDepth* f(DirectedGraphNode* cur,unordered_map<DirectedGraphNode*,MaxDepth*>& order){
        if(order.count(cur)){
            return order[cur];
        }
        //计算cur的最大深度
        long maxdep=0;
        for(DirectedGraphNode* next:cur->neighbors){
            maxdep=maxdep>f(next,order)->maxDepth?maxdep:f(next,order)->maxDepth;
        }
        MaxDepth* mdp=new MaxDepth(cur,maxdep+1);
        order.emplace(cur,mdp);
        return mdp;
    }
    vector<DirectedGraphNode*> topSort(vector<DirectedGraphNode*> graph) {
        // write your code here
        unordered_map<DirectedGraphNode*,MaxDepth*>order;
        for(DirectedGraphNode* cur:graph){
            f(cur,order);//计算每一个结点的最大深度
        }
        vector<DirectedGraphNode*>result;
        vector<MaxDepth*>maxAll;//存放所有点的最大深度
        for(pair<const DirectedGraphNode*,MaxDepth*> md:order){
            maxAll.push_back(md.second);
        }
        sort(maxAll.begin(),maxAll.end(),cmp);
        for(MaxDepth* cur:maxAll){
            result.push_back(cur->node);
        }
        return result;
    }
};

最小生成树:边的权重之和最小且无环的连通图。
最小生成树一般是针对无向图而言的。
方法1:Kruskal(适用于边小于点)
最初一个结点一个集合,然后按边的权重从小到大排序,先选权重小的边连通的两个结点,如果这两个结点不在一个集合,就把它们放在结果集中,同时合并两个结点所在的集合。最后判断集合总数是否为1,只有集合个数为1才存在最小生成树。
需要的数据结构:优先队列;选择最小的边
并查集:判断两个结点是否在同一个集合里
这里的图结构是最初的结构,并查集用的最初的结构

vector<Edge*>Kruskal() {
		vector<Edge*>result;
		priority_queue<Edge*,vector<Edge*>,MyCompare>pq;
		unordered_set<int>nodes;
		for (pair<const int, Node*> cur : this->nodes) {//统计所有的结点
			nodes.emplace(cur.second->val);
		}
		for (Edge* edge : this->edges) {//将所有的边入队
			pq.push(edge);
		}
		vector<int>value(nodes.begin(), nodes.end());
		UnionSet unionset(value);
		while (!pq.empty()) {
			Edge* cur = pq.top();
			pq.pop();
			Node* from = cur->from;
			Node* to = cur->to;
			if (!unionset.isSameSet(from->val, to->val)) {
				unionset.unionSet(from->val, to->val);
				result.push_back(cur);
			}
		}
		if (unionset.sizeMap.size() == 1) {//只有最终集合的个数为1个时才存在最小生成树
			return result;

		}
		else {
			return {};
		}


	}

方法2:prim(适用于边大于点)
随机选择一个结点,并将该结点连接的边加入队列,然后选择最小边然后从队列弹出并解锁该边指向的结点,并将该结点连接的边加入队列,再从队列中选择最小的边,如果该边指向的结点没有被解锁过就要这个点(如果该点被解锁了,要这个点就会产生环),同时将该结点连接的边加入队列,直到所有的点都被解锁。
需要的数据结构:优先队列:选择最小的边
set:判断结点是否被解锁

vector<Edge*>prim() {
		vector<Edge*>result;
		priority_queue<Edge*, vector<Edge*>, MyCompare>pq;
		unordered_set<Node*>nodes;//保存解锁的结点
		for (pair<const int,Node* >cur: this->nodes) {
			Node* start = cur.second;//选择一个结点出发
			nodes.emplace(start);//解锁该结点
			for (Edge* edge : start->edges) {
				pq.push(edge);
			}
			while (!pq.empty()) {
				Edge* cur = pq.top();
				pq.pop();
				Node* toNode = cur->to;//该边指向的结点
				if (nodes.find(toNode) == nodes.end()) {//判断指向的这个结点是否被解锁,只有没被解锁才要这个结点
					nodes.emplace(toNode);//解锁该结点
					result.push_back(cur);//收集结果
					for (Edge* edge : toNode->edges) {//将该结点指出的边加入队列
						pq.push(edge);
					}
				}
			}
			//break;//如果整个图是由多棵树也即森林组成,每次选择的结点产生的结果是不同的
		}
		
		
		if (nodes.size() == this->nodes.size()) {//最后所有的结点都被解锁才存在最小生成树
			return result;
		}
		else {
			return {};
		}
	}

629 · 最小生成树
分析:
此题用方法1方便
准备一个优先队列,按cost从小到大排序
初始化并查集,每一个城市一个集合
每次选择队列队首元素,如果这两个城市不在同一个集合,就放入结果集,同时连接他们。

class UnionSet {
public:
	unordered_map<string, string>parents;//key的父亲是value
	unordered_map<string, int>sizeMap;//代表结点所在集合的元素个数
	UnionSet(set<string>& cities) {

		for (set<string>::iterator it = cities.begin(); it != cities.end(); it++) {
			string cur = *it;
			parents[cur] = cur;
			sizeMap[cur] = 1;
		}

	}
	string findFather(string& cur) {
		stack<string>paths;
		while (cur != parents[cur]) {
			paths.push(cur);
			cur = parents[cur];
		}
		while (!paths.empty()) {
			string i = paths.top();
			paths.pop();
			parents[i] = cur;
		}
		return cur;
	}
	bool isSameSet(string i, string j) {
		return findFather(i) == findFather(j);
	}
	void unionSet(string& i, string& j) {
		string iHead = findFather(i);
		string jHead = findFather(j);
		if (iHead != jHead) {
			if (sizeMap[iHead] >= sizeMap[jHead]) {
				parents[jHead] = iHead;
				sizeMap[iHead] += sizeMap[jHead];
				sizeMap.erase(jHead);
			}
			else {
				parents[iHead] = jHead;
				sizeMap[jHead] += sizeMap[iHead];
				sizeMap.erase(iHead);
			}
		}
	}
};

class Mycompare {
public:
	bool operator()(Connection& c1, Connection& c2) {//小顶堆(cost小的在上面)
		if(c1.cost>c2.cost){
            return true;
        }
        else if(c1.cost==c2.cost && c1.city1>c2.city1){
            return true;
        }
        else if(c1.cost==c2.cost && c1.city1==c2.city1 && c1.city2>c2.city2){
            return true;
        }
        else{
            return false;
        }
	}
};
class Solution {
public:
	/**
	 * @param connections given a list of connections include two cities and cost
	 * @return a list of connections from results
	 */
	vector<Connection> lowestCost(vector<Connection>& connections) {
		// Write your code here
		vector<Connection>result;
		priority_queue<Connection, vector<Connection>, Mycompare>pq;//按cost从小到大放入优先队列中
		set<string>cities;
		for (Connection c : connections) {
			cities.emplace(c.city1);
			cities.emplace(c.city2);
			pq.push(c);
		}
		UnionSet unionset(cities);
		while (!pq.empty()) {//如果队首的两个城市不在同一个集合,就将他们放入结果中,同时连接他们
			Connection cur = pq.top();
			pq.pop();
			string city1 = cur.city1;
			string city2 = cur.city2;
			if (!unionset.isSameSet(city1, city2)) {
				unionset.unionSet(city1, city2);
				result.push_back(cur);
			}
		}
        if(unionset.sizeMap.size()==1){//所有的城市都联通当且仅当所有的城市都在同一个集合
            return result;
        }
        else{
            return {};
        }
       
		
	}
};

迪杰斯特拉算法:单源最短路径(有向无负权重图)
迪杰斯特拉不允许环的累计和为负。
图结构依然采用的是开始的结构
初始形式的代码

Node* getMinDistanceAndUnselectedNode(unordered_map<Node*, int>& distanceMap, unordered_set<Node*>& selectedNodes) {//返回当前从from出发距离最近的结点,也就是distanceMap中int最小对应的结点
		Node* minNode = NULL;
		for (pair<Node*, int>p : distanceMap) {
			Node* node = p.first;
			if (!selectedNodes.count(node)) {//node不在selectedNode中
				if (minNode == NULL) {
					minNode = node;
				}
				else {
					minNode = distanceMap[minNode] <= distanceMap[node] ? minNode : node;//更更新最小距离的结点
				}

			}
		}
		return minNode;
	}
	//从from结点开始到各个结点的最短路径
	void dijiesitela(Node* from, unordered_map<Node*, int>& distanceMap) {//从frrom结点出发,距离放在distanceMap中
		distanceMap.emplace(from, 0);
		unordered_set<Node*>selectedNodes;//打过对号的结点(已选过的结点)
		Node* minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);//从from出发距离最小且为打对号的结点
		while (minNode != NULL) {
			//此时minNode作为跳转点,到达与minNode连通且未打对号的结点的最小距离
			
			int distance = distanceMap[minNode];//获取最小距离
			for (Edge* edge : minNode->edges) {//与minNode连通的所有边都计算
				Node* toNode = edge->to;//从minNode经edge到达的结点
				if (!distanceMap.count(toNode)) {//初始时规定从from到各个点的距离是无穷大,所以如果distanceMap里没有toNode的记录,说明当前from到toNode的最小距离是无穷大
					distanceMap.emplace(toNode, distance + edge->weight);//minNode作为跳转点到toNode的距离
					

				}
				else {
					
					distanceMap[toNode] = min(distanceMap[toNode], distance + edge->weight);//如果经过minNode到达toNode的距离比之前算过的小就更新,不可以重复添加键值相同的键值对
					
				}
			}
			selectedNodes.emplace(minNode);//minNode已选过了,下次不能再选minNode了
			minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);//找下一个从from出发距离最小且为打对号的结点
		}
	}

该算法的改进需要重写堆,这方面还没弄懂,以后再补充改进写法

814 · 无向图中的最短路径

/**
 * Definition for undirected graph.
 * struct UndirectedGraphNode {
 *     int label;
 *     vector<UndirectedGraphNode *> neighbors;
 *     UndirectedGraphNode(int x) : label(x) {};
 * };
 */

class Solution {
public:
    /**
     * @param graph: a list of Undirected graph node
     * @param A: nodeA
     * @param B: nodeB
     * @return:  the length of the shortest path
     */
	UndirectedGraphNode* getMinNode(unordered_map<UndirectedGraphNode*,int>& distanceMap,unordered_set<UndirectedGraphNode*>& selectedNode){
		UndirectedGraphNode* minNode=NULL;
		for(pair<UndirectedGraphNode*,int>p:distanceMap){
			UndirectedGraphNode* curNode=p.first;
			if(!selectedNode.count(curNode)){
				if(minNode==NULL){
					minNode=curNode;
				}else{
					minNode=distanceMap[minNode]<=distanceMap[curNode]?minNode:curNode;
				}
			}
		}
		return minNode;
	}
	void dijiesi(UndirectedGraphNode* from,unordered_map<UndirectedGraphNode*,int>& distanceMap){
		unordered_set<UndirectedGraphNode*>selectedNode;
		distanceMap.emplace(from,0);
		UndirectedGraphNode* minNode=getMinNode(distanceMap,selectedNode);
		while(minNode!=NULL){
			int distance=distanceMap[minNode];
			for(UndirectedGraphNode* neighbor:minNode->neighbors){
				if(!distanceMap.count(neighbor)){
					distanceMap[neighbor]=distance+1;
				}else{
					distanceMap[neighbor]=min(distanceMap[neighbor],distance+1);
				}
			}

			selectedNode.emplace(minNode);
			minNode=getMinNode(distanceMap,selectedNode);
		}
	}
    int shortestPath(vector<UndirectedGraphNode*> graph, UndirectedGraphNode* A, UndirectedGraphNode* B) {
        // Write your code here
	   unordered_map<UndirectedGraphNode*,int>distanceMap;
	   dijiesi(A,distanceMap);
	   return distanceMap[B];
    }
};
22、图的基本算法与应用 本文全面探讨了图的基本算法及其应用,包括图的表示方法、遍历算法(DFS和BFS)、最短路径算法(Dijkstra和Bellman-Ford)、最小生成树算法(Prim和Kruskal)、最大流和最小割问题、图的连通性、二分图匹配、图的着色问题、旅行商问题等。此外,还介绍了图算法在社交网络分析、路径规划、物流配送等实际场景中的应用,并讨论了算法的优化策略和复杂图结构的处理方法。文章最后展望了图算法在大规模图处理、动态图算法和图神经网络等未来研究方向。 阅读详情

相关推荐

手把手移植st7701s驱动:MT6763+安卓8.0屏幕适配保姆级教程

本文详细介绍了在MT6763平台上移植ST7701S驱动的完整流程,涵盖从LK层到Kernel层的适配方案,特别针对安卓8.0系统的特殊要求。通过MIPI转RGB屏幕适配的实战教程,帮助开发者解决常见显示问题,包括无显示、色彩异常和屏幕错位等,并提供高级调试技巧如彩条测试模式和闪屏解决方案。

weixin_29186249的博客 156

基本的图算法

基本的图算法主要是两个方面:图的表示和图的搜索。我们主要通过邻接链表和邻接矩阵对图进行表示,但是在图算法更重要的是图的搜索,图的搜索指的是系统化的跟随图中的边来访问图中的每个节点,我们可以通过图的搜索算法发现图的结构。或者换个方面想图的算法就是通过图的搜索得到图的结构,所以图的算法就是对基本的搜索加以优化,因此图的搜索技巧是整个图算法的核心。 图的表示 对于一个图,我们有两种标准表示方法表示。一种表示法将图作为邻接链表的组合,另一种表示法则将图作为邻接矩阵来看待,两种方法既可以表示有向图也可以表示无向图

zhousiyuan0515的博客 3613

特征选择(模型输入参数的分析选择)方法汇总

特征选择是模式识别和机器学习领域的重要研究课题之一。为了提高准确率, 人们往往最大限度地提取特征信息。然而, 过大的特征向量维数不仅导致计算成本的增加, 复杂运算也随之带来分类识别率的下降。因此, 通过合适的特征选择算法, 去除无关特征和冗余特征, 获得有助于分类的最优特征子集, 对提高识别性能和降低计算成本具有重要意义。 特征选择主要有两个功能: 减少特征数量、降维,使模型泛化能力更强,减少过拟合 增强对特征和特征值之间的理解 参考资料: 优快云《几种常用的特征选择方法》 知乎《机器学习中,.

David's Tweet 1万+

图的常见算法

一、图结构 图有很多种存储方式,主要有邻接表和邻接矩阵两种。邻接表以点集为单位,邻接矩阵以边集为单位。 //图的大结构 public class Graph { public HashMap<Integer,Node> nodes; //点集 public HashSet<Edge> edges; //边集 public Graph(){ nodes = new HashMap<>(); edges = new HashSet&l

qq_20846769的博客 1632

毫米波与太赫兹:十射线模型建模

Pt=1,只考虑地面和前后两面墙的反射,反射系数为0.7。用matlab设计程序并实现该功能。,总计考虑10条路径,求出接收功率随。

SupermarketX的博客 785

保姆级教程:用OpenCV搞定鱼眼双目相机的标定与测距(附完整C++代码)

本文详细介绍了使用OpenCV进行鱼眼双目相机标定与测距的全流程,包括环境搭建、数据采集、单目标定、双目标定、立体校正和视差计算等核心步骤。通过完整的C++代码示例和参数调优指南,帮助开发者快速实现高精度的双目测距系统,适用于机器人导航、VR全景拍摄等领域。

weixin_42573113的博客 387

图及其常见算法

图及其常见算法

weixin_45565886的博客 933

ABAP RAP 行为建模深入学习:从 Behavior Definition 到 Behavior Implementation 的完整实践指南

ABAP RAP开发指南:业务行为定义与实现 本文介绍SAP RAP框架中业务对象行为的开发方法: 分层设计 Behavior Definition声明业务能力(增删改查、校验、动作等) Behavior Implementation在ABAP类中编写具体逻辑 核心组件 使用BDL定义业务行为 通过FOR BEHAVIOR OF绑定行为池类(ABP) 在本地类实现校验/确定/动作逻辑 关键特性 支持managed/unmanaged两种实现模式 提供草稿表、并发控制等企业级功能 通过EML以别名访问实体 开

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 411

图的算法汇总

以下是深度优先搜索(DFS)、广度优先搜索(BFS)和迪杰斯特拉算法(Dijkstra's algorithm)分别用 C++、Java 和 Go 语言实现的示例代码。

m0_57836225的博客 1264

Python爬虫教程:微博热点话题抓取与社交媒体分析

微博是中国最大的社交媒体平台之一,拥有海量的用户和丰富的讨论话题。通过爬取微博的热门话题和讨论内容,我们可以深入分析用户的兴趣、舆论走势、社会情绪等。通过爬取微博的热点话题和讨论内容,我们可以获得关于当前热门话题的宝贵数据,并基于这些数据进行多种社交媒体分析。本文将通过Python爬虫技术,详细介绍如何抓取微博上的热门话题、话题讨论内容,并进行社交媒体分析。希望本文为你提供了一个全面的微博数据抓取与分析的指南,帮助你在社交媒体数据分析的道路上迈出坚实的第一步。微博上的一些页面内容是静态的,可以直接通过。

2201_76125261的博客 589

【气候极端指数】MATLAB计算各种气候极端指数

简介各类气候极端指数,及其MATLAB计算代码

WW、forever的博客 4917

图的十大经典算法

图的遍历是指从图中的某一个结点出发,按照某种搜索方法沿着图中的边对图中的所有结点访问一次且仅访问一次。注意到树是一种特殊的图,所以树的遍历实际上也可以看作是一种特殊的图的遍历。图的遍历根据访问结点的顺序,主要分为有两种算法:广度优先搜索(Breadth-First-Search)和深度优先搜索(Depth-First-Search)。 广度优先搜索(BFS) 算法思想 首先访问起始结点v,接着由v...

石中路的博客 2万+

TC3XX - MCAL知识点(四十二):AURIX™ TC3xx实现多相电机需要多核配合使用的场景与实操分析

本文探讨了多相电机控制系统中的关键技术问题,重点分析了多核处理器在电机控制中的应用。针对十二相电机等复杂应用场景,提出了同步采样、多核协同等解决方案。文章详细阐述了相电流同步采样原理及EBTresos配置方法,分析了ADC硬件触发机制和多核配合策略。通过Core0和Core1的协同工作设计,解决了位置信息采集与FOC算法执行的时序问题,并提供了代码实现示例。实验验证了采用ATOM0_7同时触发两个核ADC中断的可行性,为多相电机控制系统的实现提供了技术参考。

梦想技术家 496

图的基本算法实现

<br /> <br />图的基本算法实现<br /> <br />图的基本算法实现主要包括图的存储、图的遍历,求解图的最小生成树,以及求解最短路径等。<br /> <br />图的存储:<br />对于图的存储有多种方式,最常用的是邻接矩阵存储、邻接表存储,除此外还有十字邻接表存储,邻接多重表存储等;<br /> <br />邻接矩阵存储很简单,对于无权图,用0或1来代表两点是否邻接;如果是是有权图,那就用权值来代表两点邻接,不相邻接的则直接置为无穷大,其数据结构表示如下:<br />#define MA

PJYang的专栏 3098

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

【数据结构】图和基本算法

从0开始了解图,图的概念,定义,存储结构,BFS和DFS,最小生成树算法(Kruskal和Prim),最短路径算法(Dijkstra,BellmanFord,FloydWarshall)

weixin_63249832的博客 1293

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

算法课程笔记——图的基本与遍历

算法课程笔记——图的基本与遍历。

2302_79123319的博客 244

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

第十二周--图基本算法库

/* *Copyright (c) 2015 烟台大学计算机与控制工程学院 *All right reserved. *标题:数据结构实践——图基本算法库 *作者:杨珺 *date:2015年11月16日 *版本:V1.0.1 *操作系统:XP *运行环境:VC6.0 *问题描述:图的存储结构主要包括邻接矩阵和邻接表,本算法库提供存储结构的定义,以及用于构造图存

Yangjun19113的博客 670
上一篇: 堆的基础学习
下一篇: 排序算法总结
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值