当前位置: 首页 > news >正文

数据结构(三) 排序/并查集/图

目录

1. 排序

2.并查集

3.图


1.排序:

1.1 概念:

        排序就是将数据按照某种规则进行排列, 具有某种顺序. 分为内排序和外排序.

内排序就是: 将数据放在内存中的排序; 外排序是: 数据太多无法在内存中排序的.

 1.2 插入排序:

        插入排序包含: 直接插入排序和希尔排序.

(1) 直接插入排序:

        (这里图是借用其他博主的), 直接插入排序就是将第i个数值进行和前i数值依次比较, i数值小就一直放到前面, 直到值比他更小或者比完. 时间复杂度是O(N^2). 稳定性: 稳定.

 

void InsertSort(int* a, int n)
{for(int i = 0; i < n - 1; i++){int end = i;int tmp = a[end+1];while(end >= 0){if(tmp < a[end]){a[end + 1] = a[end];end--;}else{break;}}a[end + 1] = tmp;}
}
 (2) 希尔排序:

        是采用gap进行分割前后数, 第i个数和i+gap个数进行比较如果a[i+gap]小于a[i]就交换.

gap算一趟, gap每次缩小1/2;  进行每趟调整. 时间复杂度是:O(NlogN); 稳定性: 不稳定.

void ShellSort(int* a, int n)
{int gap = n;while(gap > 1){gap = gap / 2;for(int i = 0; i < n - gap; i++){int end = i;int tmp = a[end + gap];while(end >= 0){if(tmp < a[end]){a[end + gap] = a[end];end -= gap; }else{break;}}a[end + gap] = tmp;}}
}

1.3 选择排序:

选择排序包括选择排序和堆排序:

(1) 选择排序:

                每趟找到比最小的数, 遍历全数列的那种, 然后进行交换i和最小数值的位置. 时间复杂度是O(N^2); 稳定性: 不稳定.

        还可以依次选两个数, 最大和最小, 放在左边和右边, 进行遍历选择.

void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}void SelectSort(int* a, int n)
{for(int i = 0; i < n; i++){int start = i;int min = start;while(start < n){if(a[start] < a[min])min = start;start++;}Swap(&a[i], &a[min]);}
}void SelectSort(int* a, int n)
{int left = 0;int right = n - 1;while(left < right){int minIndex = left;int maxIndex = left;for(int i = left; i <= right; i++){if(a[i] < a[minIndex])minIndex = i;if(a[i] > a[maxIndex])maxIndex = i;}Swap(&a[minIndex], &a[left]);if(left == maxIndex){maxIndex = minIndex;}Swap(&a[maxIndex], &a[right]);left++;right--;}
}
(2) 堆排序:

        具体看上一篇博客:数据结构(二)

void AdjustDown(int* a, int n, int parent)
{int child = parent * 2 + 1;while(child < n){if(child + 1 < n && a[child + 1] < a[child]){child++;}if(a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void StackSort(int* a, int n)
{for(int i = (n-1-1) / 2; i >= 0; i--){AdjustDown(a, n, i);}int end = n - 1;while(end){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}
}

 1.4 交换排序:

        交换排序包含冒泡排序和快速排序:

(1) 冒泡排序:

        相邻两个数进行比较, 大的就交换,这样到最后的就一定是最大的数, 下一次只要遍历到这个最大数前一个即可. 时间复杂度是: O(N^2) ; 稳定性: 稳定;

void BubbleSort(int* a, int n)
{int end = 0;for(end = n - 1; end >= 0; end--){int exchange = 0;for(int i = 0; i < end; i++){if(a[i] > a[i+1]){Swap(&a[i], &a[i+1]);exchange = 1;}}if(exchange = 0)break;}
}
 (2) 快速排序:

        时间复杂度就是O(NlogN), 稳定性: 不稳定.

 a.hoare版本

        最左边作为key进行比较的值, 其次就是left和right不断往中间走, right找到小于key的, left找到大于key的, 然后交换right和left; 将left和right相遇的点在进行分治法使用快速排序.

        
void QuickSort1(int* a, int begin, int end)
{if(begin >= end)return;int left = begin;int right = end;int keyi = left;while(left < right){while(left < right && a[right] >= a[keyi]){right--;}while(left < right && a[left] <= a[keyi]){left++;}if(left < right){Swap(&a[left], &a[right]);}}int meeti = left;Swap(&a[keyi], &a[meeti]);QuickSort1(a, begin, meeti-1);QuickSort1(a,  meeti+1, end);
}
b.挖坑法:

        和上面差别就是把key下标的值取出来了, 但是过程还是和上面一样.

void QuickSort2(int* a, int begin, int end)
{if(begin >= end)return;int left = begin;int right = end;int key = a[left];while(left < right){while(left < right && a[right] >= key){right--;} a[left] = a[right];while(left < right && a[left] <= key){left++;}a[right] = a[left];}int meeti = left;a[meeti] = key;QuickSort2(a, begin, meeti - 1);QuickSort2(a, meeti + 1, end);
}
 c. 前后指针法:

//三数取中;
int GetMidIndex(int* a, int left, int right)
{int mid = left + (right - left) / 2;if(a[mid] > a[left]){if(a[mid] < a[right])return mid;else if(a[left] > a[right])return left;elsereturn right;}
}void QuickSort3(int* a, int begin, int end)
{if(begin >= end)return;int minIndex = GetMidIndex(a, begin, end);Swap(&a[begin], &a[minIndex]);int prev = begin;int cur = begin + 1;int keyi = begin;while(cur <= end){if(a[cur] < a[keyi] && ++prev != cur){Swap(&a[prev], &a[cur]);}cur++;}int meeti = prev;Swap(&a[keyi], &a[meeti]);QuickSort3(a, begin, meeti-1);QuickSort3(a, meeti + 1, end);
}

1.5 归并排序:

        归并排序是采用分治的方法, 将数据对半分开, 使用额外的空间进行收集对半开的数组之间的比较大小的数据. 时间复杂度是O(NlogN); 稳定性: 不稳定.

void _MergeSort(int* a, int left, int right, int* tmp)
{if(left >= right)return;int mid = left + (right - left) / 2;_MergeSort(a, left, mid, tmp);_MergeSort(a, mid+1, right, tmp);int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;int i = left;while(begin1 <= end1 && begin2 <= end2){if(a[begin1] < a[begin2])tmp[i++] = a[begin1++];elsetmp[i++] = a[begin2++];}while(begin1 <= end1)tmp[i++] = a[begin1++];while(begin2 <= end2)tmp[i++] = a[begin2++];for(int j = left; j <= right; j++)a[j] = tmp[j];}void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if(tmp == nullptr){printf("malloc fail\n");exit(-1);}_MergeSort(a, 0, n - 1, tmp);free(tmp);
}

 1.6 计数排序:

        采用计数每个元素出现的次数, 以及最小值和最大值记录, 利用额外空间记录每个元素出现次数, 然后再将原来数组进行额外数组的替换.

void CountSort(int* a, int n)
{int min = a[0];int max = a[0];for(int i = 0; i < n; i++){if(a[i] < min)min = a[i];if(a[i] > max)max = a[i];}int range = max - min + 1;int* count = (int*)calloc(range, sizeof(int));if(count == nullptr){printf("malloc fail!");exit(-1);}for(int i = 0; i < n; i++){count[a[i] - min]++;}int i = 0;for(int j = 0; j < range; j++){while(count[j]--){a[i++] = j + min;}}free(count);
}

2. 并查集:

2.1 概念:

        由于不同元素但是又属于某种集合的数据, 存储使用到并查集合. 元素属于某种集合是按照某种规则来分类的. 需要查询某个元素, 需要找到对应集合去寻找.

        下标对应的就是集合编号, 里面的值对应这个元素属于哪个集合里的. 如果值为负数代表这个集合|拥有的元素数目|-1.

 2.2 并查集实现:

(1) 并查集结构:

        就是采用数组即可.

private:vector<int> _ufs;
(2) 初始化并查集:

        刚开始每个元素都是-1, 为根结点.

 //初始化并查集:刚开始都是-1.UnionFindSet(int n):_ufs(n, -1){}
(3) 查找元素的集合结点:

        遍历到负数的结点就是集合结点. 返回下标即可.

//查找元素所在集合:int FindRoot(int x){int parent = x;//遍历到值为负数就是根结点.while(_ufs[parent] >= 0){//不停迭代下标查询.parent = _ufs[parent];}return parent;}//递归方法查找;int _FindRoot(int x){return _ufs[x] < 0 ? x : _FindRoot(_ufs[x]);}
(4) 检查两个元素是否在一个集合:

        只要检查两个结点是否是同一个集合结点即可.

 //判断两个元素是否在同一个集合中.bool InSameSet(int x1, int x2){//检查两个元素根结点是否同一个即可.return FindRoot(x1)  == FindRoot(x2); }
(5) 两个结点合并:

        首先找到两个元素结点的集合结点, 如果在一个集合里面就不用插入了, 不是的话, 将parent1作为元素个数大的集合, parent2进行合并到parent1里面. 然后改变parent1值的个数以及parent2集合的新集合结点.

//合并两个元素所在集合.bool UnionSet(int x1, int x2){int parent1 = FindRoot(x1); int parent2 = FindRoot(x2);if(parent1 == parent2)return false;if(_ufs[parent1] > _ufs[parent2]){swap(parent1, parent2);}_ufs[parent1] += _ufs[parent2];_ufs[parent2] = parent1;return true;}
(6) 计算集合个数:
//查询集合里面的个数:int GetNum(){int count = 0;for(const int& val : _ufs){if(val < 0)count++;}return count;}
(7) 压缩路径:

        在查找数据的时候就进行压缩路径, 找到该元素的集合结点, 以及它的父结点, 然后进行将这个结点一条路的元素都直接插入到集合结点里面. 而且一般使用于数据量比较大的时候.

//查找元素所在集合://在查找集合结点的时候进行压缩.//+压缩路径:int FindRoot(int x){int root = x;//遍历到值为负数就是根结点.while(_ufs[root] >= 0){//不停迭代下标查询.root = _ufs[root];}while(_ufs[x] >= 0){int parent = _ufs[x];_ufs[x] = root;x = parent;}return root;}//递归方法查找 + 压缩;int _FindRoot(int x){//return _ufs[x] < 0 ? x : _FindRoot(_ufs[x]);int parent = x;if(_ufs[x] >= 0){parent = _FindRoot(_ufs[x]);_ufs[x] = parent;}}
(8) 元素编号和用户输入问题:

        用户一般不会输入数字编号, 可能会输入关键词, 这时候模板函数解决. 以及使用关键词和集合进行互相关联的方法, 就可以解决了.

#include <iostream>
#include <string>
#include <vector>
#include <utility>
#include <unordered_map>
using namespace std;
template<class T>
class UnionFindSet
{
public://初始化并查集:刚开始都是-1.UnionFindSet(const vector<T>& v):_ufs(v.size(), -1){for (int i = 0; i < v.size(); i++){_indexMap[v[i]] = i;}}//查找元素所在集合://在查找集合结点的时候进行压缩.//+压缩路径:int FindRoot(const T& x){int root = _indexMap[x];//遍历到值为负数就是根结点.while (_ufs[root] >= 0){//不停迭代下标查询.root = _ufs[root];}//一般数据量少不需要压缩.// while(_ufs[x] >= 0)// {//     int parent = _ufs[x];//     _ufs[x] = root;//     x = parent;// }return root;}//递归方法查找 + 压缩;// int _FindRoot(int x)// {//     //return _ufs[x] < 0 ? x : _FindRoot(_ufs[x]);//     int parent = x;//     if(_ufs[x] >= 0)//     {//         parent = _FindRoot(_ufs[x]);//         _ufs[x] = parent;//     }// }//判断两个元素是否在同一个集合中.bool InSameSet(const T& x1, const T& x2){//检查两个元素根结点是否同一个即可.return FindRoot(x1) == FindRoot(x2);}//合并两个元素所在集合.bool UnionSet(const T& x1, const T& x2){int parent1 = FindRoot(x1); int parent2 = FindRoot(x2);if (parent1 == parent2)return false;if (_ufs[parent1] > _ufs[parent2]){swap(parent1, parent2);}_ufs[parent1] += _ufs[parent2];_ufs[parent2] = parent1;return true;}//查询集合里面的个数:int GetNum(){int count = 0;for (const int& val : _ufs){if (val < 0)count++;}return count;}private:vector<int> _ufs;//原来标记数据T的处于哪个集合里面.unordered_map<T, int> _indexMap;
};int main() {vector<string> v = { "张三", "李四", "王五", "赵六", "田七", "周八", "吴九" };UnionFindSet<string> ufs(v);cout << ufs.GetNum() << endl; //7ufs.UnionSet("张三", "李四");ufs.UnionSet("王五", "赵六");cout << ufs.GetNum() << endl; //5ufs.UnionSet("张三", "赵六");cout << ufs.GetNum() << endl; //4return 0;
}

3. 图

3.1 概念:

        由顶点的集合与顶点之间的关系数据结构,  G = (V, E); V就是顶点集合, E是边的集合.

图分为有向图和无向图, 边是有方向的就是有向图.

概念:

       (1) 完全图: 无向完全图: 任意两个顶点之间有且只要一条边,  那么n个顶点就一共有

n*(n-1)/2; 有向完全图: 任意两个顶点之间有且仅有方向相反的边, n个顶点就是n*(n-1)条边.

       (2) 领接顶点: 一条边的两个相关连的顶点. 

       (3) 顶点的度: 与顶点相连的边的个数. 包含有入度(指向顶点的边)和出度(指出顶点的边).

       (4) 路径: 从顶点i到顶点j的一组边的集合.

       (5) 路径的长度: 不带权重的就是边的个数, 带权重就是边的权重的总和.

       (6) 简单路径和回路: 路径的结点都不会重复就是简单路径, 回路就是第一个顶点和最后一个结点重合就是回路.

       (8) 子图: 顶点和边包含于原图:

        (9) 连通图: 在无向图中任意一个结点都是相连的.

        (10) 强连通图: 在有向图中, 如果顶点i到顶点j有一条路径, 那么j到i也有.

        (11) 生成树: 连通图的最小连接子图. n个顶点那么就有n-1条边.

 3.2 图的结构:

        图由于只要描述顶点以及顶点之间的边即可, 保持顶点用数组即可, 但是边就需要临界矩阵了.

 3.3 邻接矩阵:

        表示两个顶点是否相连, 用0/1表示的. 邻接矩阵是二维数组. 先用一个一维数组进行保存顶点, 然后用二维数组进行保存边. 可以发现无向图的邻接矩阵是对称边界线的. 但是有向图就是不一定是对称的. 如果边有权重就用权重代替, 没有就是无穷代表.

 3.4 邻接矩阵实现:

(1) 邻接矩阵结构:

        使用_vertexs数组填写顶点的集合, V代表结点的关键词(没有具体类型使用到模板),  _vIndexMap填写的是顶点映射的下标(方便进行边关系的处理), _matrix就是一个二维的邻接矩阵(0/1表示关系).

namespace Matrix
{//V是记录结点关键词, W是记录结点关系, 邻接矩阵边初始都是最大值. Direction标志有向无向.template<class V, class W, W MAX_W = INT_MAX, bool Direction = false>class Graph{private:vector<V> _vertexs;//顶点集合;unordered_map<V, int> _vIndexMap; //顶点映射的下标;vector<vector<W>> _matrix; //邻界矩阵.};}
(2) 初始化:

        _vertexs初始化将传递来的顶点信息进行插入, _matrix是矩阵的大小的初始化. 以及_vIndexMap将顶点进行对应映射.

        Graph(const V* vertexs, int n)//初始化顶点集合以及邻接矩阵.:_vertexs(vertexs, vertexs + n),_matrix(n, vector<int>(n, MAX_W)){//建立顶点映射.sfor(int i = 0; i < n; i++){_vIndexMap[vertexs[i]] = i;}}
(3) 获取顶点下标:

        顶点的对应关系都是存储在_vIndexMap里面使用顶点关键词进行查询即可找到对应下标. 如果没有就是不存在这个结点.

        int getVertexIndex(const V& v){auto iter = _vIndexMap.find(v);if(iter != _vIndexMap.end()){return iter->second;}else{throw invalid_argument("不存在的结点");return -1;}}
(4) 连接顶点:

        首先要获得两个顶点的下标, 然后将这两个下标在_matrix邻接表中填写对应权重即可.

        //src和dest进行连接.void addEdge(const V& src, const V& dest, const W& weight){//先获取两个顶点的下标.int srci = getVertexIndex(src);int desti = getVertexIndex(dest);if(Direction == false){_matrix[desti][srci] = weight;}}
(5) 打印:

        首先打印顶点以及对应的下标信息, 其次就是打印邻接表_matrix的数据, 没有连接的用*表示, 连接打印其中权重.

        void print(){//记录结点数:int n = _vertexs.size();//先打印结点关键词对应映射关系:for(int i = 0; i < n; i++){cout << "[" << i "]->" << _vertexs[i] << endl;}cout << endl;cout << " ";for(int i = 0; i < 0; i++){printf("%4d", i);}cout << endl;for(int i = 0; i < n; i++){cout << i << " ";for(int j = 0; j < n; j++){//如果没有顶点相连接.打印*if(_matrix[i][j] == MAX_W){printf("%4c", '*');}else{//打印顶点之间的权重.printf("%4d", _matrix[i][j]);}}cout << endl;}cout << endl;}
(6) 邻接矩阵的优缺点:

        邻接矩阵可以很清楚看到两个顶点的连接情况, 但是不好统计一个顶的路径. 而且在边比较少的情况还有点浪费空间.

 3.5 邻接表:

        使用数组表示顶点集合, 链表表示边.

 3.6 邻接表实现:

(1) 结构:

        实现每个边需要存放目标顶点下标, 以及权重还有下个顶点指针. 图中包含顶点信息, 顶点映射, 以及邻接表(就是将顶点下标, 权重, 下一个顶点指针的指针数组).

namespace LinkTable
{template<class W>struct Edge{//int _srci; //源顶点下标;int _desti;//目标顶点下标.W _weight; //边权重;Edge<W>* _next; // 连接指针;Edge(int desti, const W& weight):_desti(desti),_weight(weight),_next(nullptr){}};template <class V, class W, W MAX_W = INT_MAX, bool Direction = false>class Graph{typedef Edge<W> Edge;private:vector<V> _vertexs;unordered_map<V, int> _vIndexMap;vector<Edge*> _LinkTable;};
} 
 (2) 初始化:

        将顶点数据进行拷贝以及初始化邻接表, 以及对应顶点映射关系.

Graph(const V* vertexs, int n):_vertexs(vertexs, vertexs + n),_LinkTable(n, nullptr){for(int i = 0; i < n; i++){_vIndexMap[vertexs[i]] = i;}}
(3) 找到顶点的下标:

        和邻接矩阵查找方式一样.

        int getVertexIndex(const V &v){auto iter = _vIndexMap.find(v);if (iter != _vIndexMap.end()){return iter->second;}else{throw invalid_argument("不存在的结点");return -1;}}
(4) 顶点连接:

        首先找到两个顶点的下标, new一个新结点, 这个邻接表对应srci对应下标赋值给下标, 以及也要给邻接表挂结点. 如果是无向图还需要将目标顶点进行挂在对应邻接表中.

        int addEdge(const V& src, const V& dest, const W& weight){int srci = getVertexIndex(src);int desti = getVertexIndex(dest);Edge* sdEdge = new Edge(desti, weight);sdEdge->_next = _LinkTable[srci];_LinkTable[srci] = sdEdge;if(Direction == false){Edge* dsEdge = new Edge(srci, weight);dsEdge->_next = _LinkTable[desti];_LinkTable[desti] = dsEdge;}}
(5) 打印:

        先打印顶点下标和顶点, 然后取出邻接表的元素进行结点的遍历查询打印.

        void print(){int n = _vertexs.size();for (int i = 0; i < n; i++){cout << "[" << i << "]->" << _vertexs[i];cout << endl;}cout << endl << endl;for (int i = 0; i < n; i++){Edge* cur = _LinkTable[i];cout << "[" << i << ":" << _vertexs[i] << "]->";while (cur){//先打印目标顶点下标, 目标顶点, 还有权重. cout << "[" << cur->_desti << ":" << _vertexs[cur->_desti] << ":" << cur->_weight << "]->";cur = cur->_next;}cout << "nullptr";cout << endl;}}

 3.7 图的遍历:

(1) 广度优先遍历:

        根据图的一层层进行遍历查询.

        先找到对应顶点的下标, 创建队列, 和数组标记遍历的顶点, 然后遍历顶点队列, 如果邻接矩阵相连并且没有遍历过就进行查询.

        void bfs(const V& src){//先找到对应的下标;int srci = getVertexIndex(src);queue<int> q;//原来标记是否遍历过的顶点.vector<bool> visited(_vertexs.size(), false);q.push(srci);visited[srci] = true;while(!q.empty()){int front = q.front();q.pop();cout << _vertexs[front] << " ";for(int i = 0; i < _vertexs.size(); i++){//判断相连并且没有遍历过.if(_matrix[front][i] != MAX_W && visited[i] == false){q.push(i);visited[i] = true;}}cout << endl;}}
 (2) 深度优先遍历:

        

        void _dfs(int srci, vector<bool>& visited){cout << _vertexs[srci] << " ";visited[srci] = true;for(int i = 0; i < _vertexs.size(); i++){if(_matrix[srci][i] != MAX_W && visited[i]== false){_dfs(i, visited);}}}void dfs(const V& src){int srci = getVertexIndex(src);vector<bool> visited(_vertexs.size(), false);_dfs(srci, visited);cout << endl;}

3.8 最小生成树:

        1. 只能使用图中的边来构造最小生成树 2. 只能使用恰好n-1条边来连接图中的n个顶点 3. 选用的n-1条边不能构成回路;

(1) Kruskal算法(克鲁斯卡尔算法)

        使用贪心算法, 先选择最短的路径进行连接顶点, 然后也要判断不能成环.

        记录边使用Edge来记录源顶点和目的顶点, 然后就是使用优先级队列将所有边插入, 进行建立小堆. 还要借助并查集进行检查是否为环, 首先两个边不在同一个集合, 其次将边顶点进行合并, 增加最小生成树的边, 以及最后是否选择边为n-1来判断可以成最小生成树不.

        struct Edge{int _srci;int _desti;W _weight;Edge(int srci, int desti, const W& weight):_srci(srci),_desti(desti),_weight(weight){}bool operator>(const Edge& edge) const{return _weight > edge._weight;}};//强制生成默认构造;Graph() = default;//最小生成树, 最后返回权重.W Kruskal(Graph<V, W, MAX_W, Direction>& minTree){int n = _vertexs.size();//设置最小生成树的顶点集合;minTree._vertexs = _vertexs;//设置最小生成树的顶点映射;minTree._vIndexMap = _vIndexMap;//设置最小生成树邻接矩阵的大小.minTree._matrix.resize(n, vector<W>(n, MAX_W));priority_queue<Edge, vector<Edge>, greater<Edge>> minHeap;//将所有边放到优先级队列中;for(int i = 0; i < n; i++){//只用遍历一般矩阵, 因为无向图的对称.for(int j = 0; j < i; j++){if(_matrix[i][j] != MAX_W){minHeap.push(Edge(i, j, _matrix[i][j]));}}}n个顶点的并查集;UnionFindSet ufs(n);int count = 0;//选择边的个数;W totalWeight = W();//总权重;while(!minHeap.empty() && count < n-1){//取出优先级队列的最小的边;Edge minEdge = minHeap.top();minHeap.pop();int srci = minEdge._srci;int desti = minEdge._desti;W weight = minEdge._weight;//两个边不属于一个集合.if(!ufs.InSameSet(srci, desti)){minTree.addEdge(srci, desti, weight);//合并两个顶点.ufs.UnionSet(srci, desti);count++;totalWeight += weight;cout << "选边: " << _vertexs[srci] << "->" << _vertexs[desti] << ":" << weight << endl;}else{cout << "成环: " << _vertexs[srci] << "->" << _vertexs[desti] << ":" << weight << endl;}}if(count == n - 1){cout << "构建最小生成树成功" << endl;return totalWeight;}else{cout << "无法构成最小生成树" << endl;return W();}}
 (2) Prim算法(普里姆算法)

        先选顶点在进行贪心遍历.

        首先设置最小生成树的顶点集合, 顶点映射, 邻接矩阵. 找到开始start顶点的下标, 使用forest进行记录顶点使用情况, 优先级队列minheap进行记录边的使用, 将所有边插入minHeap; 遍历优先级队列的边, 以及将顶点srci和desti进行连接, 标志一下这个位置以及选过了,, 接着就是判断是否进行使用n-1条边.

        W Prim(Graph<V, W, MAX_W, Direction>& minTree, const V& start){int n = _vertexs.size();//设置最小生成树的顶点集合;minTree._vertexs = _vertexs;//设置最小生成树的顶点映射;minTree._vIndexMap = _vIndexMap;//设置最小生成树邻接矩阵的大小.minTree._matrix.resize(n, vector<W>(n, MAX_W));int starti = getVertexIndex(start);vector<bool> forest(n, false);forest[starti] = true;priority_queue<Edge, vector<Edge>, greater<Edge>> minHeap;for(int i = 0; i < n; i++){if(_matrix[starti][i] != MAX_W){minHeap.push(Edge(starti, i, _matrix[starti][i]));}}int count = 0;W totalWeight = W();while(!minHeap.empty() && count < n - 1){Edge minEdge = minEdge.top();minHeap.pop();int srci = minEdge._srci, desti = minEdge._desti;W weight = minEdge._weight;if(forest[desti] == false){for(int i = 0; i < n; i++){if(_matrix[desti][i] != MAX_W && forest[i] == false){minHeap.push(Edge(desti, i, _matrix[desti][i]));}}minTree._addEdge(srci, desti, weight);forest[desti] = true;totalWeight += weight;cout << "选边: " << _vertexs[srci] << "->" << _vertexs[desti] << ":" << weight << endl;}else{cout << "成环" << _vertexs[srci] << "->" << _vertexs[desti] << ":" << weight << endl;}}if(count == n - 1){cout << "构建最小生成树成功" << endl;return totalWeight;}else{cout << "无法构建最小生成树" << endl;return W();}}

3.9 最短路径:

(1) Dijkstra算法(迪杰斯特拉算法)

 

         void Dijkstra(const V& src, vector<W>& dist, vector<int>& parentPath){int n = _vertexs.size();int srci = getVertexIndex(src);//各个顶点权重初始化;dist.resize(n, MAX_W);//顶点的前驱初始值.parentPath.resize(n, -1);dist[srci] = W();//源顶点权重设置;vector<bool> S(n, false);//使用过顶点数组;for(int i = 0; i < n; i++){//最小权重;W minW = MAX_W;//最小权重的顶点;int u = -1;for(int j = 0; j < n; j++){//顶点没有使用过并且它的权重小于最小权重;if(S[j] == false && dist[j] < minW){//改变最小权重和最小权重的结点;minW = dist[j];u = j;}}S[u] = true;//对连接出去的顶点进行松弛更新;for(int b = 0; v < n; v++){//如果u顶点到v顶点相连, 并且v顶点没有使用过, 其次就是最小权重数组dist的u顶点+权重u到v小于最小权重dist的v顶点;//就改变dist的v顶点的最小权重, 并且建立一下上一个顶点.if(S[v] == false && _matrix[u][v] != MAX_W && dist[u] + _matrix[u][v] < dist[v]){dist[v] = dist[u] + _matrix[u][v];parentPath[v] = u;}}}}void printShortPath(const V& src, const vector<W>& dist, const vector<int>& parentPath){int n = _vertexs.size();int srci = getVertexIndex(src);for(int i = 0; i < n; i++){vector<int> path;int cur = i;//cur从结点开始向前遍历找上一个顶点.while(cur != -1){path.push_back(cur);cur = parentPath[cur];}revese(path.begin(), path.end());//打印顶点元素, 以及路径的权重.for(int j = 0; j < path.size(); j++){cout << _vertexs[path[j]] << "->";}cout << "路径的权重: " << dist[i] << " " <<endl;}}
(2) Bellman-Ford算法(贝尔曼福特算法)

        先获得src顶点的下标, 将权重数组dist以及前结点数组parentpath进行初始化. 如果顶点i与顶点i和j之间的权重之和小于dist[i,j]就更新权重数组.以及前结点数组. 如果继续更新还可以找到最小路径就是负权重, 无法进行计算最小路径.

        void BellmanFord(const V& src, vector<W>& dist, vector<int>& parentPath){int n = _vertexs.size();int srci = getVertexIndex(src);dist.resize(n, MAX_W);parentPath.resize(n, -1);dist[srci] = W();for(int k = 0; k < n - 1; k++){bool update = false;for(int i = 0; i < n; i++){for(int j = 0; j < n; j++){if(_matrix[i][j] != MAX_W && dist[i] != MAX_W && dist[i] + _matrix[i][j] < dist[j]){dist[j] = dist[i] + _matrix[i][j];parentPath[j] = i;update = true;}}}if(update == false){break;}}//如果n-1轮还可以更新就是带有负权重.for(int i = 0; i < n; i++){for(int j = 0; j < n; j++){if(_matrix[i][j] != MAX_W && dist[i] + _matrix[i][j] < dist[j]){//带负权重无法计算出最小路径.return false;}}}return true;}
(3) Floyd-Warshall算法(弗洛伊德算法)

        vvDist进行顶点i到顶点j的二维数组, vvparentPath记录前一个顶点. 首先将顶点相连的权重全部插入vvDist里面, 其次就是当i==j就是顶点到自己的. 然后如果顶点i到顶点k以及顶点k到顶点j的权重小于顶点i到j就进行更新vvDist以及vvparentPath.

        void FloydWarshall(vector<vector<W>>& vvDist, vector<vector<int>>& vvparentPath){int n = _vertexs.size();vvDist.resize(n, vector<W>(n, MAX_W));vvparentPath.resize(n, vector<int>(n, -1));for(int i = 0; i < n; i++){for(int j = 0; j < n; j++){if(_matrix[i][j] != MAX_W){vvDist[i][j] = _matrix[i][j];vvparentPath[i][j] = i;}if(i == j){vvDist[i][j] = W();}}}for(int k = 0; k < n; k++){for(int i = 0; i < n; i++){for(int j = 0; j < n; j++){if(vvDist[i][k] != MAX_W && vvDist[k][j] != MAX_W && vvDist[i][k] + vvDist[k][j] < vvDist[i][j]){vvDist[i][j]= vvDist[i][k] + vvDist[k][j];vvparentPath[i][j] = vvparentPath[k][j];}}}}}

相关文章:

数据结构(三) 排序/并查集/图

目录 1. 排序 2.并查集 3.图 1.排序: 1.1 概念: 排序就是将数据按照某种规则进行排列, 具有某种顺序. 分为内排序和外排序. 内排序就是: 将数据放在内存中的排序; 外排序是: 数据太多无法在内存中排序的. 1.2 插入排序: 插入排序包含: 直接插入排序和希尔排序. (1) 直接插入…...

WPA Supplicant 技术详解

目录 前言 1. 简介 2. 源码获取 3. 代码架构 3.1 模块结构 3.2. 主要文件和目录 3.3. 顶层模块 3.4 模块之间的关系 4. 工作流程简要描述 启动 加载配置 初始化 认证 数据传输 5. 编译与安装 5.1 编译 5.1.1 libnl库与openssl库准备 5.1.2 修改配置文件 5.…...

Avalonia UI MVVM DataTemplate里绑定Command

Avalonia 模板里面绑定ViewModel跟WPF写法有些不同。需要单独绑定Command. WPF里面可以直接按照下面的方法绑定DataContext. <Button Content"Button" Command"{Binding DataContext.ClickCommand, RelativeSource{RelativeSource AncestorType{x:Type User…...

macOS如何进入 Application Support 目录(cd: string not in pwd: Application)

错误信息 cd: string not in pwd: Application 表示在当前目录下找不到名为 Application Support 的目录。可能的原因如下&#xff1a; 拼写错误或路径错误&#xff1a;确保你输入的目录名称正确。目录名称是区分大小写的&#xff0c;因此请确保使用正确的大小写。正确的目录名…...

【探索 Kali Linux】渗透测试与网络安全的终极操作系统

探索 Kali Linux&#xff1a;渗透测试与网络安全的终极操作系统 在网络安全领域&#xff0c;Kali Linux 无疑是最受欢迎的操作系统之一。无论是专业的渗透测试人员、安全研究人员&#xff0c;还是对网络安全感兴趣的初学者&#xff0c;Kali Linux 都提供了强大的工具和灵活的环…...

《SwinIR:使用Swin-Transformer图像恢复》学习笔记

paper&#xff1a;2108.10257 GitHub&#xff1a;GitHub - JingyunLiang/SwinIR&#xff1a; SwinIR&#xff1a; 使用 Swin Transformer 进行图像修复 &#xff08;官方仓库&#xff09; 目录 摘要 1、Introduction 2、Related Work 2.1 图像修复 2.2 视觉Transformer…...

AR智慧点巡检系统探究和技术方案设计

一、项目背景 随着工业生产规模的不断扩大和设备复杂度的提升&#xff0c;传统的人工点巡检方式效率低下、易出错&#xff0c;难以满足现代化企业对设备运行可靠性和安全性的要求。AR&#xff08;增强现实&#xff09;技术的发展为点巡检工作带来了新的解决方案&#xff0c;通…...

电路研究9.2——合宙Air780EP使用AT指令

这里正式研究AT指令的学习了&#xff0c;之前只是接触的AT指令&#xff0c;这里则是深入分析AT指令了。 软件的开发方式&#xff1a; AT&#xff1a;MCU 做主控&#xff0c;MCU 发 AT 命令给模组的开发方式&#xff0c;模组仅提供标准的 AT 固件&#xff0c; 所有的业务控制逻辑…...

OpenCV相机标定与3D重建(62)根据两个投影矩阵和对应的图像点来计算3D空间中点的坐标函数triangulatePoints()的使用

加粗样式- 操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 这个函数通过使用立体相机对3维点的观测&#xff0c;重建这些点的三维坐标&#xff08;以齐次坐标表示&#xff09;。 cv::triangula…...

基于ollama,langchain,springboot从零搭建知识库四【设计通用rag系统】

需求&#xff1a; 1&#xff1a;可以自定义管理大模型&#xff0c;可自行选择ollama&#xff0c;openai&#xff0c;千问等大模型 2&#xff1a;自定义向量数据库&#xff0c;支持pgvector&#xff0c;elasticsearch&#xff0c;milvus&#xff08;这三个目前比较常用&#xff…...

【Go面试】工作经验篇 (持续整合)

这里写目录标题 什么是逃逸分析服务端怎么接受客户端上传的文件说一下对gin框架的理解gin有哪些常用中间件gin怎么用swagger写接口文档nginx一般是用来做什么如果调用方法经常超时怎么办gin中怎么和mysql通信从mysql调数据到redis,如何同步延时双删redis ,mysql都不存在用户请求…...

“腾讯、钉钉、飞书” 会议开源平替,免费功能强大

在数字化时代&#xff0c;远程办公和线上协作越来越火。然而&#xff0c;市面上的视频会议工具要么贵得离谱&#xff0c;要么功能受限&#xff0c;甚至还有些在数据安全和隐私保护上让人不放心。 今天开源君给大家安利一个超棒的开源项目 - Jitsi Meet&#xff0c;这可是我在网…...

怎样使用树莓派自己搭建一套ADS-B信号接收系统

0 我们知道&#xff0c;ADS-B全称广播式自动相关监视系统&#xff0c;其实就是飞机发出的广播信号&#xff0c;用明码来对外发送自己的位置、高度、速度、航向等信息&#xff0c;是公开信息。连续接收到一架飞机发出的ADS-B信息后&#xff0c;可以通过其坐标点来描绘出飞机的航…...

终极的复杂,是简单

软件仿真拥有最佳的信号可见性和调试灵活性,能够高效捕获很多显而易见的常见错误,被大多数工程师熟练使用。 空间领域应用的一套数据处理系统(Data Handling System),采用抗辐FPGA作为主处理器,片上资源只包含10752个寄存器,软仿也是个挺花时间的事。 Few ms might take …...

粒子群算法 笔记 数学建模

引入: 如何找到全局最大值&#xff1a;如果只是贪心的话&#xff0c;容易被局部最大解锁定 方法有&#xff1a;盲目搜索&#xff0c;启发式搜索 盲目搜索&#xff1a;枚举法和蒙特卡洛模拟&#xff0c;但是样例太多花费巨量时间 所以启发式算法就来了&#xff0c;通过经验和规…...

Vue.js 嵌套路由和动态路由

Vue.js 嵌套路由和动态路由 在 Vue.js 开发中&#xff0c;Vue Router 是官方提供的路由管理器&#xff0c;用于构建单页应用&#xff08;SPA&#xff09;。它支持嵌套路由和动态路由&#xff0c;帮助开发者构建复杂的应用结构。 嵌套路由 嵌套路由允许在路由配置中定义子路由…...

Docker导入镜像

使用命令行进行处理&#xff1a; docker load < onething1_wxedge.tar如下图所示 查看状态 docker images...

C# OpenCV机器视觉:红外体温检测

在一个骄阳似火的夏日&#xff0c;全球却被一场突如其来的疫情阴霾笼罩。阿强所在的小镇&#xff0c;平日里熙熙攘攘的街道变得冷冷清清&#xff0c;人们戴着口罩&#xff0c;行色匆匆&#xff0c;眼神中满是对病毒的恐惧。阿强作为镇上小有名气的科技达人&#xff0c;看着这一…...

STM32项目分享:智能厨房安全检测系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 PCB图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; STM32智能厨房安全检测系统 &#xff08;资料分…...

docker 安装 redis 详解

在平常的开发工作中&#xff0c;我们经常会用到 redis&#xff0c;那么 docker 下应该如何安装 redis 呢&#xff1f;简单来说&#xff1a;第一步&#xff1a;拉取redis镜像&#xff1b;第二步&#xff1a;设置 redis.conf 配置文件&#xff1b;第三步&#xff1a;编写 docker-…...

《探秘鸿蒙Next:人工智能助力元宇宙高效渲染新征程》

在元宇宙的宏大愿景中&#xff0c;高效的渲染技术是构建沉浸式虚拟世界的关键。鸿蒙Next凭借与人工智能的深度融合&#xff0c;为元宇宙的渲染带来了全新的解决方案和无限可能。 智能场景分析与优化 人工智能能够对元宇宙场景进行智能分析。鸿蒙Next可以利用AI技术对场景中的…...

nginx分发请求超时切换服务

nginx的upstream模块实现超时自动切换服务 upstream testfail {server 192.168.1.218 max_fails1 fail_timeout10s;server 192.168.1.129 max_fails1 fail_timeout10s;} max_fails代表失败尝试次数&#xff0c;达到设置的次数则视为该服务不可用&#xff0c; fail_timeout代…...

vulfocus/fastjson-cnvd_2017_02833复现

漏洞概述 Fastjson 是阿里巴巴开发的一个高性能的 Java 库&#xff0c;用于将 Java 对象转换成 JSON 格式&#xff08;序列化&#xff09;&#xff0c;以及将 JSON 字符串转换回 Java 对象&#xff08;反序列化&#xff09;。 fastjson在解析json的过程中,支持使用type字段来指…...

.Net Core微服务入门全纪录(五)——Ocelot-API网关(下)

系列文章目录 1、.Net Core微服务入门系列&#xff08;一&#xff09;——项目搭建 2、.Net Core微服务入门全纪录&#xff08;二&#xff09;——Consul-服务注册与发现&#xff08;上&#xff09; 3、.Net Core微服务入门全纪录&#xff08;三&#xff09;——Consul-服务注…...

OpenCV imread函数读取图像__实例详解

OpenCV imread函数读取图像__实例详解 本文目录&#xff1a; 零、时光宝盒 一、imread函数定义 二、imread函数支持的文件格式 三、imread函数flags参数详解 &#xff08;3.1&#xff09;、Flags-1时&#xff0c;样返回加载的图像&#xff08;使用alpha通道&#xff0c;否…...

GPSd定时检测保活TCP GPS源

为了在 TCP GPS 源丢失连接时自动重新连接&#xff0c;可以编写一个监控脚本&#xff0c;定期检查 gpspipe 输出中的 TCP 源数据是否存在。如果检测到丢失&#xff0c;则使用 gpsdctl 或直接命令重新添加 TCP 源。 1、工具 检查并安装必要工具&#xff0c;本例需要使用 gpspi…...

得物App亮相第七届进博会,科技赋能打造消费新热点

在2024年11月5日至11月10日举办的第七届进博会舞台上&#xff0c;上海交易团虹口分团表现亮眼&#xff0c;其中得物作为来自虹口品质电商的践行者&#xff0c;备受众多参观者关注。 上海得物信息集团有限公司自2015年于上海虹口创立以来&#xff0c;始终坚守“满足年轻人对美好…...

单片机内存管理剖析

一、概述 在单片机系统中&#xff0c;内存资源通常是有限的&#xff0c;因此高效的内存管理至关重要。合理地分配和使用内存可以提高系统的性能和稳定性&#xff0c;避免内存泄漏和碎片化问题。单片机的内存主要包括程序存储器&#xff08;如 Flash&#xff09;和数据存储器&a…...

用Python绘制一只懒羊羊

目录 一、准备工作 二、Turtle库简介 三、绘制懒羊羊的步骤 1. 导入Turtle库并设置画布 2. 绘制头部 3. 绘制眼睛 4. 绘制嘴巴 5. 绘制身体 6. 绘制四肢 7. 完成绘制 五、运行代码与结果展示 六、总结 在这个趣味盎然的技术实践中,我们将使用Python和Turtle图形…...

Python 预训练:打通视觉与大语言模型应用壁垒——Python预训练视觉和大语言模型

大语言模型是一种由包含数百亿甚至更多参数的深度神经网络构建的语言模型&#xff0c;通常使用自监督学习方法通过大量无标签文本进行训练&#xff0c;是深度学习之后的又一大人工智能技术革命。 大语言模型的发展主要经历了基础模型阶段(2018 年到2021年)、能力探索阶段(2019年…...

神经网络梯度爆炸的原因及解决方案

在深度学习中&#xff0c;梯度爆炸&#xff08;gradient exploding&#xff09;是一种常见的训练问题&#xff0c;尤其是在深层神经网络中。梯度爆炸指的是在反向传播过程中&#xff0c;梯度值呈指数级增长&#xff0c;导致网络权重的大幅更新&#xff0c;从而使得网络变得不稳…...

WPS不登录无法使用基本功能的解决方案

前言 WPS不登录无法使用基本功能的原因通常是为了同步数据、提供更多高级功能或满足软件授权要求。‌然而&#xff0c;一些用户可能出于隐私或便捷性的考虑&#xff0c;不愿意登录账号。在这种情况下&#xff0c;WPS可能会限制未登录用户的使用权限&#xff0c;导致工具栏变灰…...

蓝桥杯lesson3---string的使用

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” string的概念 string字符串是一种更加高级的封装&#xff0c;string字符串中包含了大量的方法&#xff0c;这些方法使得字符串的操作变得更加简单&#xff0c;string的使用&…...

Java设计模式 三 工厂方法模式 (Factory Method Pattern)

工厂方法模式 (Factory Method Pattern) 是一种常见的创建型设计模式&#xff0c;旨在通过定义一个接口来创建对象&#xff0c;而将实例化对象的具体类延迟到子类中。工厂方法模式允许客户端通过工厂方法来创建对象&#xff0c;而不需要直接调用构造函数&#xff0c;这样可以减…...

日志收集Day005

1.filebeat的input类型之filestream实战案例: 在7.16版本中已经弃用log类型,之后需要使用filebeat,与log不同&#xff0c;filebeat的message无需设置就是顶级字段 1.1简单使用&#xff1a; filebeat.inputs: - type: filestreamenabled: truepaths:- /tmp/myfilestream01.lo…...

java开发,IDEA转战VSCODE配置(mac)

一、基本java开发环境配置 前提&#xff1a;已经安装了jdk、maven、vscode&#xff0c;且配置了环境变量 1、安装java相关的插件 2、安装spring相关的插件 3、vscode配置maven环境 打开 VsCode -> 首选项 -> 设置&#xff0c;也可以在setting.json文件中直接编辑&…...

Effective C++读书笔记——item23(用非成员,非友元函数取代成员函数)

一、主要观点&#xff1a; 在某些情况下&#xff0c;使用 non-member、non-friend 函数来替换 member 函数可以增强封装性和可扩展性&#xff0c;提供更好的软件设计。 二、详细解释&#xff1a; 封装性&#xff1a; 类成员函数的封装性考量&#xff1a;成员函数可以访问类的…...

(3)STM32 USB设备开发-USB存储设备

例程&#xff1a;STM32USBdevice: 基于STM32的USB设备例子程序 - Gitee.com 本篇为使用芯片内部flash作为USB存储设备的例程&#xff0c;没有知识&#xff0c;全是实操&#xff0c;按照步骤就能获得一个STM32的U盘。本例子是在野火F103MINI开发板上验证的&#xff0c;如果代码…...

考研408笔记之数据结构(五)——图

数据结构&#xff08;五&#xff09;——图 1. 图的基本概念 1.1 图的定义 1.2 有向图和无向图 在有向图中&#xff0c;使用圆括号表示一条边&#xff0c;圆括号里元素位置互换没有影响。 在无向图中&#xff0c;使用尖括号表示一条边&#xff0c;尖括号里元素位置互换则表示…...

【博客之星】年度总结:在云影与墨香中探寻成长的足迹

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、年度回顾 1、创作历程 2、个人成长 3、个人生活与博客事业 二、技术总结 1、赛道选择 2、技术工具 3、实战项目 三、前景与展望 1、云原生未来…...

springboot 调用 c++生成的so库文件

一、创建c文件 SoTest.h #pragma once class SoTest {int Add(int a,int b); };SoTest.cpp #include "SoTest.h"int SoTest::Add(int a, int b) {return a b; }二、创建so文件 /home/ubuntu/projects/SoTest/bin/x64/Debug/libSoTest.so 三、java代码 Maven依…...

简识JVM栈帧中的操作数栈

在JVM&#xff08;Java虚拟机&#xff09;中&#xff0c;栈帧&#xff08;Stack Frame&#xff09;是方法执行时的数据结构&#xff0c;用于存储局部变量、操作数栈、方法返回地址等信息。 其中&#xff0c;操作数栈&#xff08;Operand Stack&#xff09;是栈帧中的一个重要组…...

在 Kubernetes 上快速安装 KubeSphere v4.1.2

目录标题 安装文档配置repo安装使用插件 安装文档 在 Kubernetes 上快速安装 KubeSphere 配置repo export https_proxy10.10.x.x:7890 helm repo add stable https://charts.helm.sh/stable helm repo update安装 helm upgrade --install -n kubesphere-system --create-name…...

腾讯 Hunyuan3D-2: 高分辨率3D 资产生成

腾讯 Hunyuan3D-2&#xff1a;高分辨率 3D 资产生成的突破 前言 在当今数字化时代&#xff0c;3D 资产生成技术正变得越来越重要。无论是游戏开发、影视制作还是虚拟现实领域&#xff0c;高质量的 3D 模型和纹理都是创造沉浸式体验的关键。然而&#xff0c;传统的 3D 资产制作…...

论文阅读--Qwen22.5技术报告

Qwen2 1 引言 所有模型都是在超过7 trillion token&#xff08;7万亿&#xff09;的高质量、大规模数据集上预训练的 2 Tokenizer & Model 2.1 Tokenizer 沿用Qwen&#xff08;Bai等人&#xff0c;2023a&#xff09;的做法&#xff0c;我们采用了基于字节级字节对编码…...

python如何导出数据到excel文件

python导出数据到excel文件的方法&#xff1a; 1、调用Workbook()对象中的add_sheet()方法 wb xlwt.Workbook() ws wb.add_sheet(A Test Sheet) 2、通过add_sheet()方法中的write()函数将数据写入到excel中&#xff0c;然后使用save()函数保存excel文件 ws.write(0, 0, 1234…...

pyhton学习笔记(三)

目录 1.变量 2.变量的命名规则 3.常用函数汇总 4.常用数据类型汇总 5.算术运算符 6.比较运算符和逻辑运算符 7.常见的三种格式化输出方法 8.分支语句 1.变量 变量就是可以变化的量&#xff0c;可以理解为是一个存储东西的盒子&#xff0c;盒子里面可以放一些程序里需要…...

时间类型数据处理:基于Python的datetime库和pandas库

一、datetime库常用方法 日期的数据类型主要有两种&#xff1a;一是包含时间的datetime类型&#xff0c;二是不包含时间的date类型。这里的时间指具体的时、分、秒、甚至毫秒。 1、自定义日期、时间、获取本地时间、获取本地日期、获取年份、月份、月号、小时、分钟、秒、星期…...

CPU 缓存基础知识

并发编程首先需要简单了解下现代CPU相关知识。通过一些简单的图&#xff0c;简单的代码&#xff0c;来认识CPU以及一些常见的问题。 目录 CPU存储与缓存的引入常见的三级缓存结构缓存一致性协议MESI协议缓存行 cache line 通过代码实例认识缓存行的重要性 CPU指令的乱序执行通过…...

vue3 中如何监听 props 中的值的变化

在 Vue 3 中&#xff0c;你可以使用 watch 函数来监听组件的 props 值的变化。watch 函数允许你观察一个或多个响应式数据源&#xff0c;并在这些数据源发生变化时执行回调函数。 以下是一个示例&#xff0c;展示了如何在 Vue 3 中使用 watch 来监听 props 中的值的变化&#…...