【高阶数据结构】并查集和图

目录

1.数据结构--并查集

2.数据结构--图

1.图的基础概念

2.图的简单实现

2.1.邻接矩阵的图实现

2.2.邻接表的图实现

2.3.图的DFS和BFS

2.4.最小生成树

2.4.1.Kruskal(克鲁斯卡尔算法)

2.4.2.Prim(普里姆算法)

2.5.最短路径

2.5.1.Dijkstra(迪杰斯特拉算法)

2.5.2.Bellman-Ford(贝尔曼-福特算法)

2.5.3.Floyd-Warshall(弗洛伊德算法)


1.数据结构--并查集

概念:将n个不同的元素划分成一些不相交的集合

  1. 开始时,每个元素自成一个单元素集合;初始化
  2. 然后按一定的规律将归于同一组元素的集合合并;合并
  3. 在此过程中要反复用到查询某一个元素归属于那个集合的运算:查询                           适合于描述这类问题的抽象数据类型称为并查集(union-find set)

概念图:

实现:Count:遍历数组元素有多少个负数,就有几个集合

#pragma once
#include<iostream>
#include<vector>
#include<unordered_map>
using namespace std;template<class T>
class UnionFindSet {
public:int Find(T val){int index = _find_index[val];while (_v[index] >= 0)index = _v[index];return index;}bool Union(T x, T y){int xi = Find(x);int yi = Find(y);//是否已经联合了if (xi == yi)return false;_v[xi] += _v[yi];_v[yi] = xi;return true;}//有多少个集合int Count(){int count = 0;for (auto e : _v){if (e < 0)count++;}return count;}
public:UnionFindSet(const vector<T>& tmp){for (int i = 0; i < tmp.size(); i++)_find_index[tmp[i]] = i;_v.resize(tmp.size(), -1);}~UnionFindSet(){}
private://vector<T> _v;//哈希数组的数据和下标unordered_map<T, int> _find_index;
};

 测试:

#include<iostream>
#include"UnionFindSet.h"
#include<string>using namespace std;int main()
{vector<int> v{ 1,23,432,5345,8,712,44,534,645,73,862,3 };UnionFindSet<int> ufs(v);ufs.Union(1, 534);ufs.Union(1, 534);ufs.Union(73, 534);ufs.Union(1, 4);ufs.Union(23, 8);ufs.Union(862, 73);ufs.Union(3,3);cout << ufs.Count() << endl;return 0;
}

可以试试这个题:使用并查集做 

Leetcode:省份数量

class UnionFindSet{public:int Find(int x){//找到下标int index = _find_index[x];while(_v[index] >= 0){index = _v[index]  ;}return index;}bool Union(int x, int y){int xi = Find(x);int yi = Find(y);//已经联合if(xi == yi)return false;_v[xi] += _v[yi];_v[yi] = xi;return true;}int Count(){int count = 0;for(auto e : _v){if(e < 0)count++;}return count;}public:UnionFindSet(const vector<int>& v){_v.resize(v.size(), -1);for(int i = 0; i<v.size();i++)_find_index[i] = i;}private:vector<int> _v;unordered_map<int, int> _find_index;
};class Solution {
public:int findCircleNum(vector<vector<int>>& isConnected) {int n = isConnected.size();vector<int> v;for(int i =0; i<n; i++){v.push_back(i);}UnionFindSet ufs(v);for(int i = 0; i<n; i++){for(int j=0; j<n; j++){if(isConnected[i][j] == 1){ufs.Union(i,j);}}}return ufs.Count();}
};

2.数据结构--图

1.图的基础概念

图(graph):由顶点(vertex)集合和顶点之间的关系(edge)构成,即G = (V, E); 

  1. 顶点:图内的任意顶点;
  2. 边:连接两个顶点;
  3. 边的权值:连接两个顶点的边的值;

无向图和有向图

  1. 无向图:两个顶点之间的边无方向,两个边的权值用一个数值表示 ; 所以为强关系图:例如:qq和微信的互相为好友关系
  2. 有向图:两个顶点之间的边有方向,代表的是一个顶点到另一个顶点的权值  ; 所以为弱关系图:例如  抖音关注主播,主播不会同时关注你

完全图:有n个顶点的无向图中,若有n * (n-1)/2条边,即任意两个顶点之间有且仅有一条边, 则称此图为无向完全图,比如上图G1在n个顶点的有向图中,若有n * (n-1)条边,即任意两个 顶点之间有且仅有方向相反的边,则称此图为有向完全图,比如上图G4。  

 顶点的度:顶点v的度是指与它相关联的边的条数,记作deg(v);。在有向图中,顶点的度等于该顶点的入度与出度之和,其中顶点v的入度是以v为终点的有向边的条数,记作indev(v);顶点v的出度 是以v为起始点的有向边的条数,记作outdev(v)。因此:dev(v) = indev(v) + outdev(v)。注意:对于无向图,顶点的度等于该顶点的入度和出度,即dev(v) = indev(v) = outdev(v)。

简单路径与回路:若路径上各顶点v1,v2,v3,…,vm均不重复(不构成回路的路径),则称这样的路径为简单路径。若路径上第一个顶点v1和最后一个顶点vm重合,则称这样的路径为回路或环。 

生成树:一个连通图的最小连通子图(整个图连通 ,任一顶点都可以找到另一顶点)称作该图的生成树。有n个顶点的连通图的生成树有n个顶点 和n-1条边。 

2.图的简单实现

2.1.邻接矩阵的图实现

优势:使用邻接矩阵可以快速查找(时间复杂度O(1))两个顶点是否有边且边的权值;劣势:查找顶点的度时(时间复杂度为O(N));

设计理念:有n个顶点,创建一个n*n的二维矩阵来放置两个顶点边的权值,为空可以使用一个权值的最大值或者最小值;

#pragma once
#include<iostream>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<queue>
#include"UnionFindSet.h"using namespace std;//V顶点类型,W权值类型
namespace Matrix {template<class V, class W, bool Direction = false, W W_MAX = INT_MAX>class Graph {public:typedef Graph<V, W, Direction, W_MAX> Self;int Find(const V& v){auto it = _find_index.find(v);if (it == _find_index.end())return -1;return it->second;}bool AddEdge(const V& src, const V& des, const W& weight){int si = Find(src);int di = Find(des);//错误顶点if (si == -1 || di == -1)return false;_matrix[si][di] = weight;if (Direction == false)_matrix[di][si] = weight;return true;}void Print(){for (int i = 0; i < _matrix.size(); i++){for (int j = 0; j < _matrix.size(); j++){if (_matrix[i][j] == W_MAX)printf("%5c", '*');elseprintf("%5d", _matrix[i][j]);}cout << endl;}}public:Graph(const vector<V>& v){int n = v.size();_vertexs.resize(n);//初始化顶点集合和  顶点和下标的映射for (int i = 0; i < n; i++){_vertexs[i] = v[i];_find_index[v[i]] = i;}//初始邻接矩阵_matrix.resize(n, vector<W>(n, W_MAX));}Graph() = default;~Graph(){}private://顶点集合vector<V> _vertexs;//查找顶点下标unordered_map<V, int> _find_index;vector<vector<W>> _matrix;};
}
void test()
{vector<string> a{ "张三", "李四", "王五", "赵六", "周七" };Matrix::Graph<string, int, false> g1(a);g1.AddEdge("张三", "李四", 100);g1.AddEdge("张三", "王五", 200);g1.AddEdge("王五", "赵六", 30);g1.AddEdge("王五", "周七", 30);g1.Print();g1.Print();
}
int main()
{test();return 0 ;
}

2.2.邻接表的图实现

优势:查找顶点的度时(时间复杂度为O(1),添加一个计数),更节省空间;劣势:使用邻接表查找两个顶点是否有边且边的权值(最差情况:时间复杂度O(N)),插入效率低:需要遍历链表,看是否已存在

设计理念:有n个顶点,创建有n个元素的指针矩阵,插入一个新边 遍历链表看是否存在;

namespace List {template<class W>class Node {public:int _des;//权值W _weight;Node<W>* _next;Node(int des, W w):_des(des),_weight(w),_next(nullptr){}};template<class V, class W, bool Direction = false>class Graph{public:int Find(const V& v){auto it = _find_index.find(v);if (it == _find_index.end())return -1;return it->second;}bool AddEdge(const V& src, const V& des, const W& weight){//获取下标int si = Find(src);int di = Find(des);//不存在元素,fail;if (si == -1 || di == -1)return false;//邻接表添加边//数组指针是否为nullptrif (_table[si] == nullptr){//创建对象Node<W>* new_node = new Node<W>(di, weight);new_node->_next = _table[si];_table[si] = new_node;//无向图if (Direction == false) {Node<W>* new_node = new Node<W>(si, weight);new_node->_next = _table[di];_table[di] = new_node;}}else {//需要和邻接表内元素比较,是否已添加Node<W>* nd = _table[si];while (nd){if (nd->_des == di)return false;elsend = nd->_next;}//还未添加过Node<W>* new_node = new Node<W>(di, weight);new_node->_next = _table[si];_table[si] = new_node;//无向图if (Direction == false) {Node<W>* new_node = new Node<W>(si, weight);new_node->_next = _table[di];_table[di] = new_node;}}return true;}void Print(){for (int i = 0; i < _table.size(); i++){cout << _vertexs[i] << ": ";Node<W>* nd = _table[i];while (nd){cout << _vertexs[nd->_des] << " " << nd->_weight << "   ";nd = nd->_next;}cout << endl;}}public:Graph(const vector<V>& v){int n = v.size();_vertexs.reserve(n);for (int i = 0; i < n; i++){_vertexs.push_back(v[i]);_find_index[v[i]] = i;}_table.resize(n, nullptr);}private://顶点集合vector<V> _vertexs;//顶点和下标的哈希unordered_map<V, int> _find_index;//邻接表vector<Node<W>*> _table;};
}
void test()
{vector<string> a{ "张三", "李四", "王五", "赵六", "周七" };List::Graph<string, int, false> g1(a);g1.AddEdge("张三", "李四", 100);g1.AddEdge("张三", "王五", 200);g1.AddEdge("王五", "赵六", 30);g1.AddEdge("王五", "周七", 30);g1.Print();g1.Print();
}
int main()
{test();return 0 ;
}

2.3.图的DFS和BFS

我使用的是邻接表的图,来实现的,邻接矩阵也差不多,把代码插入List::graph类中就可以用了;

		void _DFS(int pos, vector<bool>& visited){visited[pos] = true;Node<W>* nd = _table[pos];while (nd){cout << _vertexs[pos] <<  _vertexs[nd->_des] << "  ";if (visited[nd->_des] == false)_DFS(nd->_des, visited);nd = nd->_next;}}void DFS(const V& v){int pos = _find_index[v];vector<bool> visited(_table.size(), false);_DFS(pos, visited);}void BFS(const V& v){int pos = _find_index[v];vector<bool> visited(_table.size(), false);queue<int> q;q.push(pos);while (!q.empty()){int size = q.size();while (size--){pos = q.front();visited[pos] = true;q.pop();Node<W>* nd = _table[pos];while (nd){cout << _vertexs[pos] << _vertexs[nd->_des] << "  ";if (visited[nd->_des] == false)q.push(nd->_des);nd = nd->_next;}}}}

2.4.最小生成树

后续5种算法都使用Matrix::Graph类

最小生成树通常使用的都是无向图;

  • 最小生成树:有n个顶点,使用n-1条边,使用所有顶点连通,那么肯定是不构成回路的(构成回路不可能所有连通);
2.4.1.Kruskal(克鲁斯卡尔算法)

贪心思想:

  1. 将所有边加入优先级队列;
  2. 拿出堆顶边(最小),使用并查集来判断是否构成回路;
  3. 每一条边都有两个顶点,将边的两个顶点使用并查集合并;
  4. 可能   不能构成最小生成树,所以结束条件为优先级队列不为空;
  5. 使用一个变量记录有多少边,最后为n-1条就是最小生成树;

并查集代码:在最上面

        //贪心思想,选权值最小的边,使用并查集判断是否构成回路W Kruskal(Self& minTree){//初始化生成树int n = _vertexs.size();minTree._vertexs = _vertexs;minTree._find_index = _find_index;minTree._matrix.resize(n, vector<W>(n, W_MAX) );//优先级队列保存边priority_queue<Edge<W>, vector<Edge<W>>, greater<Edge<W>>> pq;//添加所有边进优先级队列for (int i = 0; i < n; i++){for (int j = i + 1; j < n; j++){if (_matrix[i][j] != W_MAX)pq.push(Edge<W> (i, j, _matrix[i][j]) );}}//有n-1条边int count = 0;UnionFindSet<V> ufs(_vertexs);int totalW = 0;while (!pq.empty()){Edge<W> eg= pq.top();pq.pop();bool ret = ufs.Union(_vertexs[eg._src], _vertexs[eg._des]);if (ret == true){//不构成回路cout << _vertexs[eg._src] << ' ' << _vertexs[eg._des] << ' ' << eg._weight << endl;minTree.AddEdge(_vertexs[eg._src], _vertexs[eg._des], eg._weight);count++;totalW += eg._weight;}else {//cout << "构成环  " << _vertexs[eg._src] << ' ' << _vertexs[eg._des] << ' ' << eg._weight << endl;}//构成的总权值}if (count == n - 1)//能构成生成树return totalW;else//不能构成return W();}

测试代码:

void TestGraphMinTree()
{const char* ch = "abcdefghi";vector<char> v;for (int i = 0; i < strlen(ch); i++){v.push_back(ch[i]);}Matrix::Graph<char, int> g(v);g.AddEdge('a', 'b', 4);g.AddEdge('a', 'h', 8);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);Matrix::Graph<char, int> kminTree;cout << "Kruskal:" << g.Kruskal(kminTree) << endl;kminTree.Print();
}
 2.4.2.Prim(普里姆算法)

贪心思想:

  1. 确定一个顶点,将这个顶点的所有边加入优先级队列,这个顶点被设置为已使用;
  2. 拿出堆顶的边,看两个顶点是否已使用,已使用代表构环;
  3. 不构环将这个顶点的所有边,插入优先级队列,这个顶点被设置为已使用;
  4. 重复这个过程,我使用哈希表来判断顶点的,使用一个bool的数组应该更好;
  5. 使用一个变量记录有多少边,最后为n-1条就是最小生成树;
        //贪心思想,按已选择的点延伸,使用set保存已使用,不使用使用过的就一定不会构环//不使用并查集是因为已使用节点都是连通的W Prim(Self &minTree, const V& val){int n = _vertexs.size();//初始化mintreeminTree._vertexs = _vertexs;minTree._find_index = _find_index;minTree._matrix.resize(n, vector<W>(n, W_MAX));int pos = _find_index[val];//保存已使用unordered_set<V> visited;visited.insert(pos);priority_queue<Edge<W>, vector<Edge<W>>, greater<Edge<W>> > pq;//for (int j = 0; j < n; j++) {if(_matrix[pos][j] != W_MAX)pq.push(Edge<W>(pos, j, _matrix[pos][j]));}int count = 0;//边数量W TotalW = 0;//权值大小while (!pq.empty()){//获取当前最小Edge<W> dg = pq.top();pq.pop();int des = dg._des;if(visited.count(des))//存在,不可用cout << "构成环  " << _vertexs[dg._src] << ' ' << _vertexs[dg._des] << ' ' << dg._weight << endl;else {for (int j = 0; j < n; j++)//添加新边{if(_matrix[des][j] != W_MAX)pq.push(Edge<W>(des, j, _matrix[des][j]));}visited.insert(des);//已使用minTree._matrix[dg._src][dg._des] = dg._weight;//生成树添边TotalW += dg._weight;count++;cout << _vertexs[dg._src] << ' ' << _vertexs[dg._des] << ' ' << dg._weight << endl;}}if (count == n - 1)return TotalW;elsereturn W();}

测试代码: 

 

void TestGraphMinTree()
{const char* ch = "abcdefghi";vector<char> v;for (int i = 0; i < strlen(ch); i++){v.push_back(ch[i]);}Matrix::Graph<char, int> g(v);g.AddEdge('a', 'b', 4);g.AddEdge('a', 'h', 8);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);Matrix::Graph<char, int> kminTree;cout << "Prim:" << g.Prim(kminTree, 'a') << endl;kminTree.Print();
}

2.5.最短路径

最短路径通常使用的都是有向图;

2.5.1.Dijkstra(迪杰斯特拉算法)

贪心思想:此算法的缺点:只能用做不带负权的图(负权就是小于0),因为不带负权已确定的路径值不可能更小(一个数加正数不可能更小);优势:性能最好(时间:O(N^2),邻接矩阵)

  1. 根据传入的顶点,将这个将他的路径值设为0;
  2. 进行遍历找到路径值最小且未使用的顶点,顶点设为已使用
  3. 遍历这个min顶点的所有边,如果这个min顶点路径值+这条边的值  < 目标顶点的路径值,更新目标顶点的路径值和  它的父亲为 这个min顶点;
  4. 循环此过程;
void Dijkstra(const V& v, vector<W>& dest, vector<int>& parentPath ){int n = _vertexs.size();//起始点下标int src = _find_index[v];//和初始点的最短路径dest.resize(n, W_MAX);//顶点父亲下标parentPath.resize(n, -1);dest[src] = W();//起始点置为0vector<bool> visited(n, false);//执行n次for (int i = 0; i < n; i++){int minW = W_MAX, minI = src;//找到当前最小顶点且未使用for (int j = 0; j < n; j++){if (visited[j] == false && dest[j] < minW){minI = j;minW = dest[j];}}//设为已使用的值,不可能更小visited[minI] = true;//延伸是否能找到更小顶点权值for (int j = 0; j < n; j++){//满足未使用,两个顶点右边if (visited[j] == false && _matrix[minI][j] != W_MAX){//新路径值更小if (dest[minI] + _matrix[minI][j] < dest[j]){//设置路径值和父顶点下标dest[j] = dest[minI] + _matrix[minI][j];parentPath[j] = minI;}}}}}

测试代码:

void TestGraphDijkstra()
{const char* ch = "syztx";vector<char> v;for (int i = 0; i < strlen(ch); i++){v.push_back(ch[i]);}Matrix::Graph<char, int, true, INT_MAX > g(v);g.AddEdge('s', 't', 10);g.AddEdge('s', 'y', 5);g.AddEdge('y', 't', 3);g.AddEdge('y', 'x', 9);g.AddEdge('y', 'z', 2);g.AddEdge('z', 's', 7);g.AddEdge('z', 'x', 6);g.AddEdge('t', 'y', 2);g.AddEdge('t', 'x', 1);g.AddEdge('x', 'z', 4);vector<int> dest;vector<int> parentPath;g.Dijkstra('s', dest, parentPath);
}
2.5.2.Bellman-Ford(贝尔曼-福特算法)

暴力思想:将邻接矩阵遍历n-1次;时间复杂度:O(N^3),优势:解决带负权的图问题

  1. 将传入的顶点的路径值设为0;
  2. 遍历整个邻接矩阵,当前顶点不为初始值且两者有边,当前顶点路径值 + 边的权值  < 目标顶点路径值,更新目标顶点的路径值和父亲;
  3. 遍历n - 1次,每个顶点最多有n - 1条边(除自己),如果一次遍历没有顶点路径值变小,结束循环;
  4. 如果带负权回路,可以无限小,没有结果,return false;
        bool BellmanFord(const V& v, vector<W>& dest, vector<int>& parentPath){int n = _vertexs.size();int src = _find_index[v];//初始位置dest.resize(n, W_MAX);parentPath.resize(n, -1);//初始化初始位置dest[src] = 0;//为什么是n-1次:任意顶点到其他所有顶点最多n-1次,再多说明构成负权回路for (int k = 0; k < n - 1; k++){bool quit = true;//遍历权值邻接矩阵for (int i = 0; i < n; i++){for (int j = 0; j < n; j++){//有边且本身不为初始值if (_matrix[i][j] != W_MAX && dest[i] != W_MAX && dest[i] + _matrix[i][j] < dest[j]){//设置更小的路径值和父顶点下标dest[j] = dest[i] + _matrix[i][j];parentPath[j] = i;quit = false;}}}if (quit)break;}//判断是否构成负权回路for (int i = 0; i < n; i++){for (int j = 0; j < n; j++){if (_matrix[i][j] != W_MAX && dest[i] != W_MAX && dest[i] + _matrix[i][j] < dest[j])return false;}}return true;}

测试代码:

void TestGraphBellmanFord()
{const char* ch = "syztx";vector<char> v;for (int i = 0; i < strlen(ch); i++){v.push_back(ch[i]);}Matrix::Graph<char, int, true, INT_MAX> g(v);g.AddEdge('s', 't', 6);g.AddEdge('s', 'y', 7);g.AddEdge('y', 'z', 9);g.AddEdge('y', 'x', -3);g.AddEdge('z', 's', 2);g.AddEdge('z', 'x', 7);g.AddEdge('t', 'x', 5);g.AddEdge('t', 'y', 8);g.AddEdge('t', 'z', -4);g.AddEdge('x', 't', -2);vector<int> dist;vector<int> parentPath;if (g.BellmanFord('s', dist, parentPath)){}else{cout << "存在负权回路" << endl;}
}
2.5.3.Floyd-Warshall(弗洛伊德算法)

动态规划思想:时间复杂度:O(N^3);可以算出所有节点为起始点的最短路径;

  1. 初始化路径值1.自己初始化为0   2.两顶点存在边初始化为边的权值;
  2. 顶点i 到 j 存在一个顶点k,满足Edge(i, k) + Edge(k, j)  < Edge(i, j);说明有更小的路径值;
		void FloydWarShall(vector<vector<W>>& vvDest, vector<vector<int>>& vvParentPath){int n = _vertexs.size();vvDest.resize(n);vvParentPath.resize(n);//初始化for (int i = 0; i < n; i++){vvDest[i].resize(n, W_MAX);vvParentPath[i].resize(n, -1);}//直接相连的顶点添加入二维数组for (int i = 0; i < n; i++){for (int j = 0; j < n; j++){if (_matrix[i][j] != W_MAX){vvDest[i][j] = _matrix[i][j];vvParentPath[i][j] = i;}else{vvParentPath[i][j] = -1;}if (i == j) {vvParentPath[i][j] = -1;vvDest[i][j] = 0;}}}//动规思想:i 到 j之间存在一个k,并且i到k + k到j  < i 到 jfor (int k = 0; k < n; k++){for (int i = 0; i < n; i++){for (int j = 0; j < n; j++){if (vvDest[i][k] != W_MAX && vvDest[k][j] != W_MAX && vvDest[i][k] + vvDest[k][j] < vvDest[i][j]){vvDest[i][j] = vvDest[i][k] + vvDest[k][j];vvParentPath[i][j] = vvParentPath[k][j];}}}}}

测试代码:

void TestFloydWarShall()
{const char* ch = "12345";vector<char> v;for (int i = 0; i < strlen(ch); i++){v.push_back(ch[i]);}Matrix::Graph<char, int, true, INT_MAX> g(v);g.AddEdge('1', '2', 3);g.AddEdge('1', '3', 8);g.AddEdge('1', '5', -4);g.AddEdge('2', '4', 1);g.AddEdge('2', '5', 7);g.AddEdge('3', '2', 4);g.AddEdge('4', '1', 2);g.AddEdge('4', '3', -5);g.AddEdge('5', '4', 6);vector<vector<int>> vvDist;vector<vector<int>> vvParentPath;g.FloydWarShall(vvDist, vvParentPath);}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/122018.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

soc的复位reset/rst问题

本节不去讨论同步复位与异步复位以及异步复位的reset_release&#xff0c;这些问题可参考&#xff1a;芯片设计进阶之路——Reset深入理解——cy413026 本机主要回答一下几个问题。 1.片外的reset信号特别是按键reset怎么防止错误抖动的影响&#xff1f; 常见的处理方法包括两…

大数据-Storm流式框架(三)--Storm搭建教程

一、两种搭建方式 1、storm单节点搭建 2、完全分布式搭建 二、storm单节点搭建 准备 下载地址&#xff1a;Index of /dist/storm 1、环境准备&#xff1a; Java 6 Python 2.6.6 2、上传、解压安装包 3、在storm目录中创建logs目录 mkdir logs 启动 ./storm help …

解决cloudflare pages部署静态页面发生404错误的问题

cloudflare pages是一个非常方便的部署静态页面的sass工具。 但是很多人部署上去以后&#xff0c;访问服务会报404错误。什么原因&#xff1f; 原因如下图所示&#xff1a; 注意这个Build output directory, 这个是部署的关键&#xff01; 这个Build output directory目录的…

ETL工具Kettle

1 Kettle的基本概念 一个数据抽取过程&#xff0c;主要包括创建一个作业&#xff08;Job&#xff09;&#xff0c;每个作业由一个或多个作业项&#xff08;Job Entry&#xff09;和连接作业项的作业跳&#xff08;Job Hop&#xff09;组成。每个作业项可以是一个转换&#xff…

TSINGSEE青犀基于AI视频识别技术的平安校园安防视频监控方案

一、背景需求 因学校频频出治安事件&#xff0c;所以必须要加强学校的安防工作&#xff0c;目前来看&#xff0c;大部分校园都建设了视频监控来预防保障校园安全。但是传统的视频监控系统&#xff0c;主要通过设备来录像以及人员时时监控来进行。这种监管方式效率十分低下&…

使用canal实现数据实时同步

canal canal [kə’nl]&#xff0c;译意为水道/管道/沟渠&#xff0c;主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据订阅和消费 早期阿里巴巴因为杭州和美国双机房部署&#xff0c;存在跨机房同步的业务需求&#xff0c;实现方式主要是基于业务 trigger…

【API篇】八、Flink窗口函数

文章目录 1、增量聚合之ReduceFunction2、增量聚合之AggregateFunction3、全窗口函数full window functions4、增量聚合函数搭配全窗口函数5、会话窗口动态获取间隔值6、触发器和移除器7、补充 //窗口操作 stream.keyBy(<key selector>).window(<window assigner>)…

lesson2(补充)关于const成员函数

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 前言&#xff1a; 将const 修饰的 “ 成员函数 ” 称之为 const 成员函数 &#xff0c; const 修饰类成员函数&#xff0c;实际修饰该成员函数 隐含的 this 指针 &#xff0c;表明在该成员函数中不能对类的任何成员进行修改…

Vue+ElementUI项目打包部署到Ubuntu服务器中

1、修改config/index.js中的assetsPublicPath: /,修改为assetsPublicPath: ./ assetsPublicPath: ./2、在build/utils.js中增加publicPath: ../../ publicPath: ../../3、打开终端&#xff0c;在根目录下执行npm run build进行打包&#xff0c;打包成功后会生成dist npm run…

037-第三代软件开发-系统音量设置

第三代软件开发-系统音量设置 文章目录 第三代软件开发-系统音量设置项目介绍系统音量设置QML 实现C 实现 总结一下 关键字&#xff1a; Qt、 Qml、 volume、 声音、 GPT 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt Meta-Obj…

听GPT 讲Rust源代码--library/std(8)

题图来自Why is Rust programming language so popular?[1] File: rust/library/std/src/sys/sgx/abi/reloc.rs 在Rust源代码中&#xff0c;sgx/abi/reloc.rs文件的作用是定义了针对Intel Software Guard Extensions (SGX)的重定位相关结构和函数。 该文件中的Rela 结构定义了…

集群节点批量执行 shell 命令

1、SSH 工具本身支持多窗口 比如 MobaXterm&#xff1a; 2、编写脚本通过 ssh 在多台机器批量执行shell命令 创建 ssh_hosts 配置文件&#xff0c;定义需要批量执行的节点&#xff08;必须能够通过 ssh 免密登录&#xff0c;且存在同名用户&#xff09; vim ssh_hostsbig…

Oracle通过透明网关查询SQL Server 报错ORA-00904

Oracle通过透明网关查询SQL Server 报错ORA-00904 问题描述&#xff1a; 只有全表扫描SELECT * 时SQL语句可以正常执行 添加WHERE条件或指定列名查询&#xff0c;查询语句就报错 问题原因&#xff1a; 字段大小写和SQLSERVER中定义的不一致导致查询异常 解决办法&#xff1a; 给…

消息队列中间件面试笔记总结RabbitMQ,Kafka,RocketMQ

文章目录 (一) Rabbit MQRabbitMQ 核心概念消息队列的作用Exchange(交换器)Broker&#xff08;消息中间件的服务节点&#xff09;如何保证消息的可靠性如何保证 RabbitMQ 消息的顺序性如何保证 RabbitMQ 高可用的&#xff1f;如何解决消息队列的延时以及过期失效问题消息堆积问…

Web服务器与Http协议

Web服务器与Http协议 一.Web服务器 1.简介 Web服务器一般指网站服务器&#xff0c;也称之为WWW(World Wide Web)服务器Web服务器是指驻留于因特网上某种类型计算机的程序Web服务器不是硬件服务器&#xff0c;而是软件服务器。Web服务器其主要功能是提供网上信息浏览服务&…

p5.js 视频播放指南

本文简介 在刚接触 p5.js 时我以为这只是一个艺术方向的 canvas 库&#xff0c;没想到它还支持视频文件和视频流的播放。 本文简单讲讲如何使用 P5.js 播放视频。 播放视频文件 p5.js 除了可以使用 video 元素播放视频外&#xff0c;还支持使用 image 控件播放视频。 方式1&…

【机器学习可解释性】2.特征重要性排列

机器学习可解释性 1.模型洞察的价值2.特征重要性排列3.部分依赖图4.SHAP Value5.SHAP Value 高级使用 正文 前言 你的模型认为哪些特征最重要&#xff1f; 介绍 我们可能会对模型提出的最基本的问题之一是&#xff1a;哪些特征对预测的影响最大&#xff1f; 这个概念被称为…

【C++】命名空间

目录 1 命名空间的引入 2 命名空间的定义 3 标准命名空间std 头文件和std的关系 4 命名空间的使用 4.1 加命名空间名称及作用域限定符:: 4.2 使用using将命名空间中某个成员引入(最推荐) 4.3 使用using namespace 命名空间名称 5 总结 1 命名空间的引入 为了解决C语言中…

ffmpeg的下载和编译(vs2022)

感谢大佬的二创,直接提供了sln编译 ffmpeg二创地址 创建如下目录 build存放代码(build最好改成source,因为作者这么建议,编译完才发现) msvc存放第三方依赖的头文件,这里固定叫msvc,因为大佬的sln里查找的路径是这个,不嫌麻烦也可以自己改 下载代码和编译器 下载源码…

自动驾驶之—2D到3D升维

前言&#xff1a; 最近在学习自动驾驶方向的东西&#xff0c;简单整理一些学习笔记&#xff0c;学习过程中发现宝藏up 手写AI 3D卷积 3D卷积的作用&#xff1a;对于2DCNN&#xff0c;我们知道可以很好的处理单张图片中的信息&#xff0c;但是其对于视频这种由多帧图像组成的图…