最小生成树算法(Prim Kruskal)

news2024/12/27 0:46:32

目录

  • 最小生成树算法总览
  • 最小生成树的定义及性质
  • Prim(普利姆)算法
    • 1.朴素Prim算法
      • 算法步骤
    • 2.堆优化Prim算法
      • 算法步骤
    • 3.算法运用
        • Prim算法求最小生成树
          • 流程实现
          • 朴素Prim的代码实现
          • 堆优化Prim的代码实现
  • Kruskal(克鲁斯卡尔)算法
    • 1.算法步骤
    • 2.算法运用
      • Kruskal算法求最小生成树


最小生成树算法总览

​​​​​​​​最小生成树

最小生成树的定义及性质

最小生成树(Minimum Spanning Tree,简称MST)是图论中的一个概念。给定一个连通的无向图,最小生成树是指包含图中所有顶点的一棵树,且该树的所有边的权重之和最小。


最小生成树的基本定义和性质:

  1. 连通性:最小生成树必须包含图中的所有顶点,并且通过边将它们连接起来,确保整个图是连通的,即任意两个顶点之间都有路径。(一颗有 n 个顶点的生成树有且仅有 n−1 条边,如果生成树中再添加一条边,则必定成环。)
  2. 无环:最小生成树是一棵树,所以不能包含任何环(即回路)。
  3. 最小权重:最小生成树的边权重之和应当尽可能地小。在有多个满足条件的最小生成树时,它们的权重之和是相同的。

在这里插入图片描述

实际的场景:

最小生成树在现实生活和计算机科学中有广泛的实际运用场景。以下是一些常见的应用场景:

  1. 网络设计与通信:在通信网络、电信和计算机网络的设计中,最小生成树用于确定连接所有节点的最优路径,以确保数据传输的高效性和稳定性。

  2. 电力传输:在电力系统中,最小生成树可用于确定电力线路的布置,确保所有地区都能得到电力供应,同时最小化电力线路的长度和损耗。

  3. 交通规划:在城市交通规划中,最小生成树可以用来规划公交线路或道路网络,以实现最短路径和最小交通拥堵。

  4. 管道布置:在石油、天然气等管道网络的布置中,最小生成树可用于确定最优的管道布置,以最小化材料和成本的使用。

  5. 无线传感器网络:在无线传感器网络中,传感器节点需要有效地传输数据到基站,通过最小生成树可以构建出最优的通信路径,延长网络寿命。

  6. 图像分割:在计算机视觉领域,图像分割问题可以转化为最小生成树问题,用于将图像分成连通的区域,有助于图像处理和分析。

  7. 电路板设计:在电路板布线时,最小生成树可用于确定元件之间的最优连接方式,以最小化电路的面积和布线的复杂性。

  8. 聚类分析:在数据挖掘和机器学习中,最小生成树可以用于聚类分析,将相似的数据点连接在一起形成簇。


Prim(普利姆)算法

1.朴素Prim算法

朴素Prim算法(Naive Prim Algorithm),也称为简单Prim算法,是用于求解无向图的最小生成树的一种基本而直观的算法。该算法是以其发明者之一、计算机科学家Jarník的名字来命名的,也被称为Jarník算法。
时间复杂度: O ( n 2 ) O(n^2) O(n2)


算法步骤

  1. 选择一个起始节点作为最小生成树的起点。
  2. 将该起始节点加入最小生成树集合,并将其标记为已访问。
  3. 在所有与最小生成树集合相邻的边中,选择权重最小的边和它连接的未访问节点。
  4. 将该边和节点加入最小生成树集合,并将该节点标记为已访问。
  5. 重复步骤3和步骤4,直到最小生成树集合包含了图中的所有节点。

2.堆优化Prim算法

时间复杂度: O ( m log ⁡ n ) O(m \log n) O(mlogn)

算法步骤

  1. 初始化 d i s t dist dist 数组为 I N F INF INF,表示所有节点到集合的距离为无穷大。
  2. 创建一个小根堆,堆中的元素为( d i s t dist dist 值, 节点编号)。
  3. 堆中先插入 ( 0 , 1 ) (0, 1) (0,1) 表示节点1进入集合, d i s t dist dist 值为 0 0 0
  4. 每次从堆中取出 d i s t dist dist 值最小的元素 ( d , u ) (d, u) (d,u),将u加入集合。
  5. u u u 相邻的所有节点 v v v,更新 d i s t [ v ] = m i n ( d i s t [ v ] , g [ u ] [ v ] ) dist[v] = min(dist[v], g[u][v]) dist[v]=min(dist[v],g[u][v]),并更新堆中的相应元素。
  6. 重复步骤 4 、 5 4、5 45,直到所有节点都加入集合。
  7. 最后根据取出的 d i s t dist dist 值之和求得最小生成树权重。

3.算法运用

Prim算法求最小生成树

题目描述:
给定一个 n n n 个点 m m m 条边的无向图,图中可能存在重边和自环,边权可能为负数。

求最小生成树的树边权重之和,如果最小生成树不存在则输出 i m p o s s i b l e impossible impossible

给定一张边带权的无向图 G = ( V , E ) G=(V,E) G=(V,E),其中 V V V 表示图中点的集合, E E E 表示图中边的集合, n = ∣ V ∣ n=|V| n=V m = ∣ E ∣ m=|E| m=E

V V V 中的全部 n n n 个顶点和 E E E n − 1 n−1 n1 条边构成的无向连通子图被称为 G G G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G G G 的最小生成树。

输入格式:
第一行包含两个整数 n n n m m m

接下来 m m m 行,每行包含三个整数 u , v , w u,v,w u,v,w,表示点 u u u 和点 v v v 之间存在一条权值为 w w w 的边。

输出格式:
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

数据范围:
1 ≤ n ≤ 500 , 1 ≤ m ≤ 1 0 5 1≤n≤500,1≤m≤10^5 1n500,1m105,图中涉及边的边权的绝对值均不超过 10000 10000 10000

输入样例:

4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4

输出样例:

6
流程实现

我们将图中各个节点用数字 1 ∼ n 1∼n 1n 编号。
在这里插入图片描述

要将所有景点连通起来,并且边长之和最小,步骤如下:
①用一个 s t st st 数组表示节点是否已经连通。 s t [ i ] st[i] st[i] 为真,表示已经连通, s t [ i ] st[i] st[i] 为假,表示还没有连通。初始时, s t st st 各个元素为假。即所有点还没有连通。用一个 d i s t dist dist 数组保存各个点到连通部分的最短距离, d i s t [ i ] dist[i] dist[i] 表示 i 节点到连通部分的最短距离。初始时, d i s t dist dist 数组的各个元素为无穷大。用一个 pre 数组保存节点的是和谁连通的。 p r e [ i ] = k pre[i]=k pre[i]=k 表示节点 i i i 和节点 k k k 之间需要有一条边。初始时, p r e pre pre 的各个元素置为 −1

在这里插入图片描述

②从 1 1 1 号节点开始扩充连通的部分, 1 1 1 号节点与连通部分的最短距离为 0 0 0,即 d i s t [ i ] dist[i] dist[i] 值为 0 0 0
在这里插入图片描述

③遍历 d i s t dist dist 数组,找到一个还没有连通起来,但是距离连通部分最近的点,假设该节点的编号是 t t t t t t 节点就是下一个应该加入连通部分的节点, s t [ t ] st[t] st[t] 置为 t r u e true true。用青色点表示还没有连通起来的点,红色点表示连通起来的点。这里青色点中距离最小是 d i s t [ 1 ] dist[1] dist[1],因此 s t [ 1 ] st[1] st[1] 置为 t r u e true true

在这里插入图片描述

④遍历所有与 t t t 相连但没有加入到连通部分的点 j j j,如果 j j j 距离连通部分的距离大于 t ∼ j t∼j tj 之间的距离,即 d i s t [ j ] > g [ t ] [ j ] dist[j]>g[t][j] dist[j]>g[t][j] g [ t ] [ j ] g[t][j] g[t][j] t ∼ j t∼j tj 节点之间的距离),则更新 d i s t [ j ] dist[j] dist[j] g [ t ] [ j ] g[t][j] g[t][j]。这时候表示, j j j 到连通部分的最短方式是和 t t t 相连,因此更新 p r e [ j ] = t pre[j]=t pre[j]=t

与节点 1 1 1 相连的有 2 , 3 , 4 2, 3, 4 234 号节点。 1 − > 2 1−>2 1>2 的距离为 100 100 100,小于 d i s t [ 2 ] dist[2] dist[2] d i s t [ 2 ] dist[2] dist[2] 更新为 100 100 100 p r e [ 2 ] pre[2] pre[2] 更新为 1 1 1 1 − > 4 1−>4 1>4 的距离为 140 140 140,小于 d i s t [ 4 ] dist[4] dist[4] d i s t [ 4 ] dist[4] dist[4]更新为 140 140 140 p r e [ 4 ] pre[4] pre[4] 更新为 1 1 1 1 − > 3 1−>3 1>3 的距离为 150 150 150,小于 d i s t [ 3 ] dist[3] dist[3] d i s t [ 3 ] dist[3] dist[3] 更新为 150 150 150 p r e [ 3 ] pre[3] pre[3] 更新为 1 1 1

在这里插入图片描述

重复 3 − 4 3-4 34 步骤,直到所有节点的状态都被置为 1 1 1。这里青色点中距离最小的是 d i s t [ 2 ] dist[2] dist[2],因此 s t [ 2 ] st[2] st[2] 置为 1 1 1

在这里插入图片描述

与节点 2 2 2 相连的有 5 5 5 4 4 4号节点。 2 − > 5 2−>5 2>5 的距离为 80 80 80,小于 d i s t [ 5 ] dist[5] dist[5],dist[5] 更新为 80 80 80 p r e [ 5 ] pre[5] pre[5] 更新为 2 2 2 2 − > 4 2−>4 2>4 的距离为 80 80 80,小于 d i s t [ 4 ] dist[4] dist[4] d i s t [ 4 ] dist[4] dist[4] 更新为 80 80 80 p r e [ 4 ] pre[4] pre[4] 更新为 2 2 2

在这里插入图片描述

d i s t [ 4 ] dist[4] dist[4],更新 d i s t [ 3 ] dist[3] dist[3] d i s t [ 5 ] dist[5] dist[5] p r e [ 3 ] pre[3] pre[3] p r e [ 5 ] pre[5] pre[5]

在这里插入图片描述
在这里插入图片描述

d i s t [ 5 ] dist[5] dist[5],没有可更新的。

在这里插入图片描述

d i s t [ 3 ] dist[3] dist[3],没有可更新的。
在这里插入图片描述

6.此时 d i s t dist dist 数组中保存了各个节点需要修的路长,加起来就是。 p r e pre pre 数组中保存了需要选择的边。

在这里插入图片描述


朴素Prim的代码实现
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>

using namespace std;

const int N = 510;
int g[N][N], dist[N];
bool st[N];
int n, m;

int Prim()
{
	int res = 0;
	memset(dist, 0x3f, sizeof dist); // 将所有节点的距离初始化为一个很大的值(无穷大)。
	dist[1] = 0; // 从节点1开始算法。

	for (int i = 0; i < n; ++i) // 遍历所有节点。
	{
		int t = -1;
		// 找到还未加入集合的节点中距离最小的节点。
		for (int j = 1; j <= n; ++j)
		{
			if (!st[j] && (t == -1 || dist[t] > dist[j]))
				t = j;
		}
		st[t] = true; // 标记选中的节点为已访问。

		// 如果最小距离仍保持初始值,说明有些节点是不可达的,图是非连通的。
		if (dist[t] == 0x3f3f3f3f) return 0x3f3f3f3f;
		
		// 累加记得提前,防止负权自环。
		res += dist[t]; // 将当前节点的距离累加到结果中。

		// 更新其他节点到集合的最小距离,如果有更小的距离则更新。
		for (int j = 1; j <= n; ++j)
			dist[j] = min(dist[j], g[t][j]);
	}
	return res; // 返回最小生成树的权值和。
}

int main()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
	memset(g, 0x3f, sizeof g); // 初始化所有边的权值为一个很大的值(无穷大)。
	cin >> n >> m;
	for (int i = 0; i < m; ++i)
	{
		int u, v, w;
		cin >> u >> v >> w;
		g[u][v] = g[v][u] = w; // 更新边的权值。
	}
	int t = Prim(); // 执行Prim算法得到最小生成树的权值和。
	if (t == 0x3f3f3f3f) cout << "impossible" << endl; // 如果最小生成树不存在,则输出"impossible"。
	else cout << t << endl; // 输出最小生成树的权值和。
	return 0;
}


注意
累加记得提前,防止负权自环。

  1. Dijkstra 可迭代 n − 1 n-1 n1 次不同,Prim 需要迭代 n n n 次。
  2. 最小生成树是针对无向图的,所以在读入边的时候,需要赋值两次。
  3. 要先累加再更新,避免 t t t 有自环,影响答案的正确性。后更新不会影响后面的结果么?不会的,因为 d i s t [ i ] dist[i] dist[i] i i i 到集合 S S S 的距离,当 t t t 放入集合后,其 d i s t [ t ] dist[t] dist[t] 就已经没有意义了,再更新也不会影响答案的正确性。
  4. 需要特判一下第一次迭代,在我们没有做特殊处理时,第一次迭代中所有点到集合S的距离必然为无穷大,而且不会进行更新(也没有必要),所以不需要将这条边(第一次迭代时,找到的距离集合 S S S 最短的边)累加到答案中,也不能认定为图不连通。
  5. 如果需要设置起点为 i i i 的话,在初始化 d i s t dist dist 数组之后, d i s t [ i ] = 0 dist[i] = 0 dist[i]=0 即可,这样也可以省去每轮迭代中的两个 i f if if 判断。

附加

带路径输出的Prim算法

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>

using namespace std;


const int N = 510;
int g[N][N], dist[N], pre[N];
bool st[N];
int n, m;

int Prim()
{
	int res = 0;
	memset(pre, -1, sizeof pre);
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0;
	for (int i = 0; i < n; ++i)
	{
		int t = -1;
		for (int j = 1; j <= n; ++j)
		{
			if (!st[j] && (t == -1 || dist[t] > dist[j]))
				t = j;
		}
		st[t] = true;
		if (dist[t] == 0x3f3f3f3f) return 0x3f3f3f3f;
		res += dist[t];
		for (int j = 1; j <= n; ++j)
		{
			if (dist[j] > g[t][j])
			{
				dist[j] = g[t][j];
				pre[j] = t;
			}
		}
	}
	return res;
}
int main()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
	memset(g, 0x3f, sizeof g);
	cin >> n >> m;
	for (int i = 0; i < m; ++i)
	{
		int u, v, w;
		cin >> u >> v >> w;
		g[u][v] = g[v][u] = w;
	}
	int t = Prim();
	if (t == 0x3f3f3f3f) cout << "impossible" << endl;
	else cout << t << endl;
	for (int i = 1; i <= n; ++i) cout << pre[i] << ' ';
	cout << endl;
	return 0;
}

堆优化Prim的代码实现
#define _CRT_SECURE_NO_WARNINGS

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>

using namespace std;

const int N = 510, M = 1e5 + 10;
typedef pair<int, int> PII;
bool st[N]; // 标记节点是否已经加入最小生成树
int n, m, dist[N]; // dist数组用于记录每个节点到最小生成树的距离
int h[N], e[M], ne[M], idx, w[M]; // 邻接表存储图的边信息

void add(int a, int b, int c)
{
    e[idx] = b; // 存储边的另一个节点
    w[idx] = c; // 存储边的权值
    ne[idx] = h[a]; // 将边插入到节点a的邻接表头部
    h[a] = idx++; // 更新节点a的邻接表头指针
}

int Prim()
{
    int res = 0, cnt = 0; // res用于记录最小生成树的权值和,cnt用于记录已经选择的边数
    priority_queue<PII, vector<PII>, greater<PII>> heap; // 最小堆,用于选择最短边
    memset(dist, 0x3f, sizeof dist); // 初始化dist数组为无穷大
    heap.push({ 0, 1 }); // 将节点1加入最小堆,距离为0
    dist[1] = 0; // 节点1到最小生成树的距离为0

    while (heap.size())
    {
        auto t = heap.top(); // 取出最小堆中距离最小的节点
        heap.pop();
        int ver = t.second, destination = t.first; // ver为节点,destination为距离
        if (st[ver]) continue; // 如果节点已经在最小生成树中,跳过
        st[ver] = true; // 将节点标记为已经加入最小生成树
        res += destination; // 更新最小生成树的权值和
        cnt++; // 增加已选择的边数

        // 遍历节点ver的所有邻接边
        for (int i = h[ver]; i != -1; i = ne[i])
        {
            auto u = e[i]; // 邻接边的另一个节点
            if (dist[u] > w[i])
            {
                dist[u] = w[i]; // 更新节点u到最小生成树的距离
                heap.push({ dist[u], u }); // 将节点u加入最小堆
            }
        }
    }

    // 如果最小生成树的边数小于n-1,则图不连通,返回0x3f3f3f3f表示不可达
    if (cnt < n - 1) return 0x3f3f3f3f;

    return res; // 返回最小生成树的权值和
}

int main()
{
    cin.tie(0);
    ios::sync_with_stdio(false);
    memset(h, -1, sizeof h); // 初始化邻接表头指针为-1
    cin >> n >> m; // 输入节点数和边数

    for (int i = 0; i < m; ++i)
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c), add(b, a, c); // 添加无向图的边到邻接表中
    }

    int t = Prim(); // 计算最小生成树的权值和

    if (t == 0x3f3f3f3f)
        cout << "impossible" << endl; // 输出不可达
    else
        cout << t << endl; // 输出最小生成树的权值和

    return 0;
}


Kruskal(克鲁斯卡尔)算法

Kruskal算法是计算无向连通加权图的最小生成树的经典贪心算法。
时间复杂度: O ( m log ⁡ m ) O(m \log m) O(mlogm)

1.算法步骤

  1. 创建一个空的最小生成树 T T T
  2. 将图中的所有边按权重从小到大排序。【 O ( m log ⁡ m ) O(m \log m) O(mlogm) Kruskal算法时间复杂度的瓶颈】
  3. 从权重最小的边开始,如果当前边连接的两个节点不在 T T T 中,则将当前边加入 T T T,否则跳过当前边。【 O ( m ) O(m) O(m)
  4. 重复步骤 3 3 3,直到T包含图中的所有节点为止。
  5. 最后得到的 T T T 即为该图的最小生成树。

2.算法运用

Kruskal算法求最小生成树

题目描述:
给定一个 n n n 个点 m m m 条边的无向图,图中可能存在重边和自环,边权可能为负数。

求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

给定一张边带权的无向图 G = ( V , E ) G=(V,E) G=(V,E),其中 V V V 表示图中点的集合, E E E 表示图中边的集合, n = ∣ V ∣ n=|V| n=V m = ∣ E ∣ m=|E| m=E

V V V 中的全部 n n n 个顶点和 E E E n − 1 n−1 n1 条边构成的无向连通子图被称为 G G G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G G G 的最小生成树。

输入格式:
第一行包含两个整数 n n n m m m

接下来 m m m 行,每行包含三个整数 u , v , w u,v,w u,v,w,表示点 u u u 和点 v v v 之间存在一条权值为 w w w 的边。

输出格式:
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

数据范围:
1 ≤ n ≤ 1 0 5 , 1 ≤ m ≤ 2 ∗ 1 0 5 1≤n≤10^5,1≤m≤2*10^5 1n105,1m2105,图中涉及边的边权的绝对值均不超过 1000 1000 1000

输入样例:

4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4

输出样例:

6

代码实现:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <algorithm>
#include<cstring>

using namespace std;
const int N = 1e5 + 10, M = 2e5 + 10, INF = 0x3f3f3f3f;
int n, m, p[N];

struct Edge
{
    int a, b, w;
    bool operator<(const Edge& rhs) const
    {
        return w < rhs.w;
    }
} edges[M];

// 查找节点 x 的根节点(使用路径压缩优化)
int find(int x)
{
    if (p[x] != x)
        p[x] = find(p[x]);
    return p[x];
}

// Kruskal 算法计算最小生成树的权值和
int kruskal()
{
    // 按边权值从小到大排序
    sort(edges, edges + m);

    // 初始化每个节点的父节点为自身
    for (int i = 1; i <= n; ++i)
        p[i] = i;

    int res = 0; // 最小生成树的权值和
    int cnt = 0; // 已经选择的边数

    // 遍历每条边
    for (int i = 0; i < m; ++i)
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;

        // 查找节点 a 和节点 b 所在的连通分量的根节点
        a = find(a);
        b = find(b);

        if (a != b) // 如果 a 和 b 不在同一个连通分量中,即选择这条边
        {
            cnt++;
            res += w; // 更新最小生成树的权值和
            p[a] = b; // 将 a 所在的连通分量合并到 b 所在的连通分量中
        }
    }

    // 如果最小生成树的边数小于 n - 1,则说明图不连通,返回 INF
    if (cnt < n - 1)
        return INF;

    return res; // 返回最小生成树的权值和
}

int main()
{
    cin.tie(0);
    ios::sync_with_stdio(false);

    cin >> n >> m; // 输入节点数和边数

    for (int i = 0; i < m; ++i)
    {
        int a, b, w;
        cin >> a >> b >> w;
        edges[i] = {a, b, w}; // 保存边的信息
    }

    int t = kruskal(); // 计算最小生成树的权值和

    if (t == INF)
        cout << "impossible" << endl;
    else
        cout << t << endl; // 输出最小生成树的权值和
}

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

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

相关文章

IAR for STM8L标准库驱动ST7735 1.8‘‘LCD显示

IAR for STM8L标准库驱动ST7735 1.8’LCD显示 ✨STM8驱动ST7735 1.8’LCD屏幕的话&#xff0c;自己移植的话&#xff0c;可以参考stm32标准库驱动来移植&#xff0c;GPIO的操作方式和STM32标准库函数名都一致&#xff0c;移植起来改动量很少&#xff0c;这仅针对软件驱动方式。…

【Java基础教程】(四十六)IO篇 · 下:System类对IO的支持:错误输出、信息输出、系统输入,字符缓冲流、扫描流和对象序列化流~

Java基础教程之IO操作 下 &#x1f539;本节学习目标1️⃣ System类对 IO 的支持1.1 错误输出&#xff1a;System.err1.2 信息输出&#xff1a;System.out1.3 系统输入&#xff1a;System. in 2️⃣ 字符缓冲流&#xff1a;BufferedReader3️⃣ 扫描流&#xff1a;Scanner4️⃣…

【Latex】官方文档教你长公式对齐方法

具体请参见下面链接 Aligning equations with amsmath - Overleaf, Online LaTeX Editor 详细介绍了各种公式的对齐方式&#xff0c;特别是 长公式的换行方法 以及多公式排列情况&#xff1a;

ctfshow-web2

0x00 前言 CTF 加解密合集CTF Web合集 0x01 题目 最简单的SQL注入0x02 Write Up 这道题很多wp&#xff0c;但是为了完整性还是写一下 知道这里是sql注入&#xff0c;确认一下是什么类型的闭合 使用a or 11 #进行测试 可以知道闭合是单引号闭合&#xff0c;然后查一下显示的…

openGauss学习笔记-20 openGauss 简单数据管理-DISTINCT

文章目录 openGauss学习笔记-20 openGauss 简单数据管理-DISTINCT20.1 语法格式20.2 参数说明20.3 示例 openGauss学习笔记-20 openGauss 简单数据管理-DISTINCT DISTINCT关键字与SELECT语句一起使用&#xff0c;用于去除重复记录&#xff0c;只获取唯一的记录。 当一个表中有…

【python中级】 base64编码将图片数据转化为成字符串

【python中级】 base64编码将图片数据转化为成字符串 1、背景2、图片base64编码1、背景 在Qt编程的时候,有的地方比如logo需要加载图片,在程序打包之后,需要将图片拷贝进项目。 能不能将图片转化成一串字符串,使用该字符串代替图像地址。 即这样就可以字符串变量的形式编…

Terraform学习日记-AWS-EC2

terraform install https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli 这里我们使用 aws-linux-2022 作为执行环境 # sudo yum install -y yum-utils# sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/…

选择合适的图表,高效展现数据魅力

随着大数据时代的来临&#xff0c;数据的重要性愈发凸显&#xff0c;数据分析和可视化成为了决策和传递信息的重要手段。在数据可视化中&#xff0c;选择合适的图表是至关重要的一环&#xff0c;它能让数据更加生动、直观地呈现&#xff0c;为观众提供更有说服力的信息。本文将…

从娱乐产品到效率工具,ARknovv首款AR眼镜回归“AR本质”

如果说2022年是AR的元年&#xff0c;2023年则有望成为消费级AR眼镜的新拐点。 今年AR眼镜行业发展明显加快&#xff0c;且不断有大厂入局&#xff1a;今年2月小米发布无线AR眼镜探索版&#xff1b;3月荣耀也推出了一款全新的观影眼镜&#xff1b;而苹果在6月发布的MR头显Visio…

【java】2208. 将数组和减半的最少操作次数

给你一个正整数数组 nums 。每一次操作中&#xff0c;你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。&#xff08;注意&#xff0c;在后续操作中你可以对减半过的数继续执行操作&#xff09; 请你返回将 nums 数组和 至少 减少一半的 最少 操作数。 示例 1&#…

Android性能优化之游戏的Theme背景图

近期&#xff0c;对游戏的内存优化&#xff0c;通过内存快照发现&#xff0c;某个Activity的theme背景图 占用3M 多。考虑着手对齐进行优化。 问题 查看游戏中的内存快照&#xff0c;发现有一个图片bitmap 占用3M 多&#xff0c;设置在Activity的背景中&#xff1a; 查看Phon…

用户档案与用户画像:很容易混淆

用户档案与用户画像&#xff1a;很容易混淆 需求分析或设计需要梳理 趣讲大白话&#xff1a;搞清楚怎么来&#xff0c;才能用好 【趣讲信息科技236期】 **************************** User Profile&#xff0c;不知道谁提出来的。被译为用户档案或用户简要&#xff0c;它是基于…

一篇文章搞定《APP的启动流程》

一篇文章搞定《APP的启动流程》 前言冷启动、温启动、热启动启动中的重要成员简介zygote进程InstrumentationSystemServer进程ActivityManagerServiceBinderActivityThread 启动的步骤详解一、点击桌面图标二、创建进程三、初始化APP进程四、APP进程与System_server的绑定五、初…

《深度解析Docker与微服务架构:构建灵活可扩展的现代应用》

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

【SQL语句复习】第1-2章

SQL复习 学习目标&#xff1a;复习SQL语句 学习地址&#xff1a;https://linklearner.com/learn/detail/70 第一章 初始数据库 数据库是将大量数据保存起来&#xff0c;通过计算机加工而成的可以进行高效访问的数据集合。该数据集合称为数据库&#xff08;Database&#xf…

大学生用一周时间给麦当劳做了个App(Vue版)

背景 有个大学生粉丝最近私信联系我&#xff0c;说基于我之前开源的多语言项目做了个仿麦当劳的项目&#xff0c;虽然只是个样子货&#xff0c;但是收获颇多&#xff0c;希望把自己写的代码开源出来供大家一起学习进度。这个小伙伴确实是非常积极上进&#xff0c;很多大学生&a…

C盘空间不足:解决办法完整教程

当C盘空间不足时&#xff0c;你可以尝试以下几种解决方案&#xff1a; 1. 清理临时文件&#xff1a;使用Windows自带的磁盘清理工具&#xff0c;可以删除临时文件、回收站中的文件和其他不必要的系统文件&#xff0c;释放一些空间&#xff0c;推荐使用工具分区助手。 2. 卸载不…

数据库版本管理工具Flyway入门实战

From version control to continuous delivery, Flyway helps individuals, teams, and enterprises build on application delivery processes to automate database development. 1.引言 在项目开发中&#xff0c;一直在探索如何进行数据库的版本管理。关注的公众号推送了…

踩坑 视觉SLAM 十四讲第二版 ch8 编译及运行问题

1.fmt相关 CMakeLists.txt中&#xff1a;在后面加上 fmt target_link_libraries(optical_flow ${OpenCV_LIBS} fmt ) target_link_libraries(direct_method ${OpenCV_LIBS} ${Pangolin_LIBRARIES} fmt )2.不存在用户定义的从 "std::_Bind<void (OpticalFlowTracker::…

动态内存管理函数的使用与优化技巧(内存函数、柔性数组)

目录 前言 一、动态内存函数 为什么存在动态内存分配 动态内存函数介绍 malloc和free calloc realloc 常见的错误 经典笔试题目 二、C/C程序的内存开辟 三、柔性数组 柔性数组的特点&#xff1a; 柔性数组的使用 柔性数组的优势 前言 动态内存管理函数是C语言中非常重要的一部…