图论算法:最短路径算法详解【c语言版】(无权最短路径、Dijkstra算法)

news2024/11/23 23:19:16

别忘了请点个赞+收藏+关注支持一下博主喵!!!

图论算法:最短路径算法详解

        在图论中,最短路径问题是寻找图中两点之间具有最小总权重的路径。这个问题在许多实际应用中都有重要的作用,比如网络路由、城市交通规划等。本文将探讨几种不同的最短路径算法,包括无权最短路径、Dijkstra算法、带负边值的图以及无圈图(DAG)的最短路径算法。

        输入是一个赋权图:与每条边 (vi, vj) 相联系的是穿越该边的开销(或称为值)ci,j 。一条路径v1v2……vN的值是,这叫作赋权路径长(weighted path length)。而无权路径长只是路径上的边数,即 N-1。

单源最短路径问题(Single-Source Shortest-Path Problem):

给定一个赋权图 G=(V, E) 和一个特定顶点 s 作为输入,找出从 s 到 G 中每一个其他顶点的最短赋权路径。

例如,在下图一个有向图中,从 v1 到 v6 的最短赋权路径的值为6,路径为 v1 -> v4 -> v7 -> v6 ;在这两个顶点间的最短无权路径长为2。

1. 无权最短路径

图3 表示一个无权图G,使用某个顶点 s 作为输入参数,我们想要找出从 s 到所有其他顶点的最短路径。显然,这是赋权最短路径问题的特殊情形,因为可以为所有的边都赋以权1。

设我们选择 s 为 v3,此时可立即得到 s 到 v3 的最短路径长为0,将其标记,如图4 所示。

然后开始寻找所有从 s 出发距离为1 的顶点,这可以通过考查邻接到 s 的那些顶点找到,即 v1 和 v6 ,将它表示在图5 中;然后找出从 s 出发最短路径恰为2 的顶点,找出所有邻接到 v1 和 v6 的顶点,这次搜索告诉我们,到 v2 和 v4 的最短路径长为2。

这种搜索图的方法就是广度优先搜索(breadth-first search)。该方法按层处理顶点:距开始点最近的那些顶点首先被求值,而最远的那些顶点最后被求值,这很像树的层序遍历(level-order traversal)。

图8 显示出该算法要用到的表的初始配置,记录了该算法的进行过程。对于每个顶点,我们跟踪3 条信息。首先,把从 s 开始到顶点的距离放到 dv 栏中,开始时除 s 外所有的顶点都不可达。pv 栏中的项为簿记变量,它将使我们显示出实际的路径。known 栏中的项在顶点被处理后置为 true。当一个顶点被标记为 known 时,我们就有了不会再找到更便宜的路径的保证,因此对该顶点的处理实质上已经完成。

代码实现1:

如下我们使用广度优先搜索(BFS)来实现。

#include <stdio.h>
#include <stdlib.h>
#define MAX 100
#define INF 1000000

typedef struct {
    int vertex;
    int distance;
} Node;

void BFS(int adjMatrix[][MAX], int start, int n) {
    int queue[MAX];
    int front = -1, rear = -1;
    int visited[MAX] = {0};
    Node nodes[MAX];

    // 初始化起始节点
    nodes[start].vertex = start;
    nodes[start].distance = 0;
    visited[start] = 1;
    queue[++rear] = start;

    while (front != rear) {
        int current = queue[++front];
        for (int i = 0; i < n; i++) {
            if (adjMatrix[current][i] == 1 && !visited[i]) {
                nodes[i].vertex = i;
                nodes[i].distance = nodes[current].distance + 1;
                visited[i] = 1;
                queue[++rear] = i;
            }
        }
    }

    // 输出所有节点的距离
    for (int i = 0; i < n; i++) {
        printf("Node %d is %d steps away\n", i, nodes[i].distance);
    }
}

int main() {
    int n = 6; // 节点数量
    int adjMatrix[MAX][MAX] = {0}; // 邻接矩阵初始化

    // 假设有一个无向图
    adjMatrix[0][1] = adjMatrix[1][0] = 1;
    adjMatrix[0][2] = adjMatrix[2][0] = 1;
    adjMatrix[1][3] = adjMatrix[3][1] = 1;
    adjMatrix[2][3] = adjMatrix[3][2] = 1;
    adjMatrix[2][4] = adjMatrix[4][2] = 1;
    adjMatrix[3][5] = adjMatrix[5][3] = 1;

    BFS(adjMatrix, 0, n); // 从节点0开始BFS

    return 0;
}

由于双层嵌套的 for 循环,因此该算法的运行时间为 O(|V|2)。一个明显的低效之处在于,尽管所有的顶点早已成为 known 了,但外层循环还是要继续,直到 NUM_VERTICES -1 为止。我们可以用类似于对拓扑排序所做的那样来排除这种低效性。在任一时刻,只存在两种类型其 dv ≠ ∞ 的 unknown 顶点。一些顶点的 dv =currDist,而其余的则有 dv = currDist + 1。这种想法可以通过使用一个队列而被进一步精化。其中数据变化如下图所示。

 代码实现2:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h> 

#define NUM_VERTICES 100 // 定义图中的顶点数量

typedef struct {
    int dist;       // 从源顶点的距离
    int known;      // 是否已经确定最短路径
    int path;       // 路径上的前驱顶点
} Vertex;

typedef struct Edge {
    int dest;       // 目标顶点索引
    struct Edge *next; // 指向邻接表中下一个边的指针
} Edge;

// 函数声明
void unweighted(Vertex *vertices, Edge *adjList[], int s);
void addEdge(Edge *adjList[], int src, int dest);

int main() {
    Vertex vertices[NUM_VERTICES];
    Edge *adjList[NUM_VERTICES] = {NULL}; // 图的邻接表

    // 初始化图并添加边
    // 示例: addEdge(adjList, 0, 1); // 添加一条从顶点 0 到顶点 1 的边

    int startVertex = 0; // 起始顶点
    unweighted(vertices, adjList, startVertex);

    // 打印结果或按需使用
    for (int i = 0; i < NUM_VERTICES; i++) {
        printf("顶点 %d: 距离 = %d, 前驱 = %d\n", i, vertices[i].dist, vertices[i].path);
    }

    return 0;
}

void unweighted(Vertex *vertices, Edge *adjList[], int s) {
    // 初始化所有顶点
    for (int i = 0; i < NUM_VERTICES; i++) {
        vertices[i].dist = INT_MAX; // 初始距离设为无穷大
        vertices[i].known = 0;      // 初始状态为未知
        vertices[i].path = -1;      // 初始前驱顶点为 -1
    }

    // 设置起始顶点的距离为 0
    vertices[s].dist = 0;

    // 按照距离递增的顺序处理顶点
    for (int currDist = 0; currDist < NUM_VERTICES; currDist++) {
        for (int i = 0; i < NUM_VERTICES; i++) {
            if (!vertices[i].known && vertices[i].dist == currDist) {
                vertices[i].known = 1; // 标记顶点为已知
                // 更新相邻顶点的距离
                for (Edge *e = adjList[i]; e != NULL; e = e->next) {
                    int w = e->dest;
                    if (vertices[w].dist == INT_MAX) {
                        vertices[w].dist = currDist + 1; // 更新距离
                        vertices[w].path = i;            // 更新前驱顶点
                    }
                }
            }
        }
    }
}

void addEdge(Edge *adjList[], int src, int dest) {
    Edge *newEdge = (Edge *)malloc(sizeof(Edge)); // 分配新边的内存
    newEdge->dest = dest;                          // 设置目标顶点
    newEdge->next = adjList[src];                 // 将新边插入到邻接表头部
    adjList[src] = newEdge;                       // 更新邻接表
}
  1. Vertex 结构体:表示图中的顶点,包含距离 (dist)、是否已知 (known) 和前驱顶点 (path)。
  2. Edge 结构体:表示图中的边,包含目标顶点索引 (dest) 和指向下一个边的指针 (next)。
  3. unweighted 函数:实现无权图的单源最短路径算法。
  4. addEdge 函数:向图中添加边。
  5. main 函数:初始化图并调用 unweighted 函数计算从起始顶点到其他所有顶点的最短路径,然后打印结果。

 2. Dijkstra算法

Dijkstra算法用于解决有权图中的单源最短路径问题。该算法通过维护一个优先队列来选择当前距离最小的未访问节点作为下一个访问节点,从而逐步构建从源节点到其他所有节点的最短路径树。

Dijkstra 算法按阶段进行,正像无权最短路径算法一样。在每个阶段,Dijkstra 算法选择一个顶点 v,它在所有 unknown 顶点中具有最小的 dv,同时算法声明从 s 到 v 的最短路径是 known 的。阶段的其余部分由 dw 值的更新工作组成。

Dijkstra算法原理

  1. 初始化

    • 将源节点的距离设为0,其他所有节点的距离设为无穷大。
    • 创建一个集合来记录已经确定最短路径的节点。
  2. 迭代过程

    • 从尚未确定最短路径的节点中选择一个距离最小的节点,标记为已确定最短路径。
    • 更新该节点的所有邻居节点的距离。如果通过当前节点到达邻居节点的距离比已知的距离更短,则更新邻居节点的距离和前驱节点。
  3. 终止条件

    • 当所有节点都已确定最短路径时,算法结束。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h> // 包含 INT_MAX,等同于无穷大

#define NUM_VERTICES 100 // 定义图中的顶点数量

typedef struct {
    int dist;       // 从源顶点的距离
    int known;      // 是否已经确定最短路径
    int path;       // 路径上的前驱顶点
} Vertex;

// 邻接矩阵表示图
int graph[NUM_VERTICES][NUM_VERTICES];

// 初始化图
void initializeGraph(int n) {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (i == j) {
                graph[i][j] = 0; // 自环距离为0
            } else {
                graph[i][j] = INT_MAX; // 未连接的边距离设为无穷大
            }
        }
    }
}

// 添加边
void addEdge(int u, int v, int weight) {
    graph[u][v] = weight;
    // 如果是无向图,还需要添加反向边
    // graph[v][u] = weight;
}

// Dijkstra算法
void dijkstra(Vertex *vertices, int n, int start) {
    // 初始化所有顶点
    for (int i = 0; i < n; i++) {
        vertices[i].dist = INT_MAX; // 初始距离设为无穷大
        vertices[i].known = 0;      // 初始状态为未知
        vertices[i].path = -1;      // 初始前驱顶点为 -1
    }

    // 设置起始顶点的距离为 0
    vertices[start].dist = 0;

    // 主循环:直到所有顶点都已确定最短路径
    while (1) {
        int minDist = INT_MAX;
        int u = -1;

        // 选择一个距离最小且未确定最短路径的顶点
        for (int i = 0; i < n; i++) {
            if (!vertices[i].known && vertices[i].dist < minDist) {
                minDist = vertices[i].dist;
                u = i;
            }
        }

        // 如果找不到这样的顶点,算法结束
        if (u == -1) {
            break;
        }

        // 标记该顶点为已确定最短路径
        vertices[u].known = 1;

        // 更新该顶点的所有邻居节点的距离
        for (int v = 0; v < n; v++) {
            if (graph[u][v] != INT_MAX && !vertices[v].known) { // 如果存在边且邻居节点未确定最短路径
                int newDist = vertices[u].dist + graph[u][v];
                if (newDist < vertices[v].dist) {
                    vertices[v].dist = newDist; // 更新距离
                    vertices[v].path = u;       // 更新前驱顶点
                }
            }
        }
    }
}

// 打印最短路径
void printShortestPaths(Vertex *vertices, int n, int start) {
    for (int i = 0; i < n; i++) {
        if (i == start) {
            continue; // 跳过起始顶点
        }
        printf("从顶点 %d 到顶点 %d 的最短路径为: ", start, i);
        if (vertices[i].dist == INT_MAX) {
            printf("不可达\n");
        } else {
            printf("%d ", vertices[i].dist);
            int path = i;
            while (path != -1) {
                printf("%d <- ", path);
                path = vertices[path].path;
            }
            printf("\b\b\b\n"); // 删除最后一个 "<-"
        }
    }
}

int main() {
    int n = 5; // 顶点数量
    initializeGraph(n); // 初始化图

    // 添加边
    addEdge(0, 1, 10);
    addEdge(0, 4, 5);
    addEdge(1, 2, 1);
    addEdge(1, 4, 2);
    addEdge(2, 3, 4);
    addEdge(3, 2, 6);
    addEdge(4, 1, 3);
    addEdge(4, 2, 9);
    addEdge(4, 3, 2);

    Vertex vertices[n];
    int startVertex = 0; // 起始顶点

    dijkstra(vertices, n, startVertex); // 计算最短路径

    printShortestPaths(vertices, n, startVertex); // 打印最短路径

    return 0;
}

代码分析

  1. 初始化图

    • initializeGraph 函数将图初始化为一个邻接矩阵,其中对角线元素为0(表示自环),其他元素为 INT_MAX(表示无穷大)。
  2. 添加边

    • addEdge 函数用于向图中添加边。对于有向图,只需设置 graph[u][v];对于无向图,还需设置 graph[v][u]
  3. Dijkstra算法

    • dijkstra 函数实现Dijkstra算法。
    • 初始化所有顶点的距离为 INT_MAX,已知状态为 0,前驱顶点为 -1
    • 将起始顶点的距离设为0。
    • 在主循环中,选择一个距离最小且未确定最短路径的顶点,标记为已确定最短路径。
    • 更新该顶点的所有邻居节点的距离,如果通过当前顶点到达邻居节点的距离更短,则更新邻居节点的距离和前驱顶点。
  4. 打印最短路径

    • printShortestPaths 函数用于打印从起始顶点到其他所有顶点的最短路径及其路径信息。

运行示例

假设图中有5个顶点,边的权重如下:

  • (0, 1): 10
  • (0, 4): 5
  • (1, 2): 1
  • (1, 4): 2
  • (2, 3): 4
  • (3, 2): 6
  • (4, 1): 3
  • (4, 2): 9
  • (4, 3): 2

运行程序后,输出如下:

从顶点 0 到顶点 1 的最短路径为: 8 1 <- 4 <- 0
从顶点 0 到顶点 2 的最短路径为: 9 2 <- 1 <- 4 <- 0
从顶点 0 到顶点 3 的最短路径为: 7 3 <- 4 <- 0
从顶点 0 到顶点 4 的最短路径为: 5 4 <- 0

3. 所有顶点对间的最短路径


有时需要找出图中所有顶点之间的最短路径,这可以运行 |V| 次适当的单源(single-source) 算法,但对于稠密图,还是应该用更快些的算法。
之后会给出 对赋权图求解这种问题的一个 O(|V|3) 算法 。虽然对于稠密图它具有和运行 |V| 次简单(非-优先队列)Dijkstra 算法相同的时间界,但它循环非常紧凑,以至于这种专业化的所有顶点对算法很可能在实践中更快。当然,对于稀疏图,更快的是运行 |V| 次用优先队列编码的 Dijkstra 算法。

4. 最短路径的例子1


思考以下问题:使用C++ 来计算词梯游戏(word ladder)。在一个词梯中,每个单词均由其前面的单词改变一个字母而得到。例如,可以通过一系列单字母替换而将 zero 转换为 five:zero hero here hire five。
这是一个无权最短路径问题,其中每一个单词都是一个顶点,如果两个单词可以通过单字母替换而相互转换,那么它们之间就有边存在(双向)。

该例程创建一个map,其关键字是单词,相应的值是包含从单字母变换得到的那些单词的vector 对象。即,这个 map 代表一个以邻接表格式表示的图。

//求词梯的C++ 例程
//从邻接映射(adjacency map) 进行最短路径计算,返回一个向量
//该向量包含从first 到second 得到的单词相继变化
unordered_map<string,string>
findChain(const unordered_map<string, vector<string>>& adjacentWords,
	const string& first, const string& second)
{
	unordered_map<string, string> previousWord;
	queue<string>q;

	q.push(first);

	while (!q.empty())
	{
		string current = q.front();
		q.pop();
		auto itr = adjacentWords.find(current);

		const vector<string>& adj = itr->second;
		for(const string&str:adj)
			if (previousWord[str] == "")
			{
				previousWord[str] = current;
				q.push(str);
			}
	}
	previousWord[first] = "";

	return previousWord;
}

//在最短路径计算运行之后,计算包含从first 到second 得到的
//单词相继变化的vector 对象
vector<string> getChainFromPreviousMap(
	const unordered_map<string, string>& previous, const string& second)
{
	vector<string>result;
	auto& prev = const_cast<unordered_map<string, string>&>(previous);

	for (string current = second; current != ""; current = prev[current])
		result.push_back(current);

	reverse(begin(result), end(result));
	return result;
}

4. 最短路径的例子2

假设我们有一个有向图,包含5个节点(A, B, C, D, E),各边之间的权重如下所示:

  • A -> B: 1
  • A -> C: 4
  • B -> C: 1
  • B -> D: 2
  • C -> E: 3
  • D -> E: 1

我们的目标是找到从节点A到所有其他节点的最短路径。 

解决方案

1. 数据结构

为了实现Dijkstra算法,我们需要几个数据结构:

  • distance[]:存储从起始节点到每个节点的最短距离。
  • visited[]:标记节点是否已被访问过。
  • parent[]:记录最短路径上的前驱节点,以便于回溯路径。
2. 算法步骤
  1. 初始化distance[]数组为无穷大,除了起始节点的距离为0。
  2. 初始化visited[]数组为false。
  3. 创建一个优先队列(或使用简单的数组),按照距离从小到大的顺序存储未访问的节点。
  4. 当优先队列非空时:
    • 从队列中取出距离最小的节点u。
    • 对于节点u的所有邻接节点v,如果通过u到达v的距离小于当前已知的最短距离,则更新v的最短距离,并将v加入优先队列。
  5. 重复上述过程直到优先队列为空。
#include <stdio.h>
#include <limits.h>

#define V 5 // 节点数量

// 选择距离最近且未访问的节点
int minDistance(int dist[], int visited[]) {
    int min = INT_MAX, min_index;
    for (int v = 0; v < V; v++)
        if (!visited[v] && dist[v] <= min)
            min = dist[v], min_index = v;
    return min_index;
}

void dijkstra(int graph[V][V], int src) {
    int dist[V]; // 存储最短距离
    int visited[V]; // 标记节点是否已被访问
    int parent[V]; // 记录最短路径上的前驱节点

    // 初始化
    for (int i = 0; i < V; i++) {
        dist[i] = INT_MAX;
        visited[i] = 0;
        parent[i] = -1;
    }
    dist[src] = 0;

    // 找到最短路径
    for (int count = 0; count < V - 1; count++) {
        int u = minDistance(dist, visited);
        visited[u] = 1;

        for (int v = 0; v < V; v++)
            if (!visited[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) {
                parent[v] = u;
                dist[v] = dist[u] + graph[u][v];
            }
    }

    // 打印结果
    printf("Vertex\tDistance from Source\n");
    for (int i = 0; i < V; i++)
        printf("%d \t\t %d\n", i, dist[i]);
}

int main() {
    int graph[V][V] = { {0, 1, 4, 0, 0},
                        {0, 0, 1, 2, 0},
                        {0, 0, 0, 0, 3},
                        {0, 0, 0, 0, 1},
                        {0, 0, 0, 0, 0} };

    dijkstra(graph, 0);

    return 0;
}
  • minDistance 函数用于寻找当前未被访问过的节点中距离最小的一个。
  • dijkstra 函数实现了Dijkstra算法的核心逻辑。
  • 在主函数中,定义了一个图的邻接矩阵表示,并调用了dijkstra函数以0作为源节点开始计算最短路径。

输出了从源节点0到其他所有节点的最短距离。通过调整邻接矩阵和源节点,可以应用于不同的图和起点

别忘了请点个赞+收藏+关注支持一下博主喵!!!

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

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

相关文章

vue通过iframe方式嵌套grafana图表

文章目录 前言一、iframe方式实现xxx.xxx.com拒绝连接登录不跳转Cookie 的SameSite问题解决不显示额外区域(kiosk1) 前言 我们的前端是vue实现的&#xff0c;监控图表是在grafana中的&#xff0c;需要在项目web页面直接显示grafana图表 一、iframe方式实现 xxx.xxx.com拒绝连…

苹果系统安装Homebrew时CLT缺失的问题

前言 为了使用brew命令&#xff0c;必须安装Homebrew工具。但是在Howebrew安装的时候&#xff0c;会出现CLT&#xff08;Command Line Tools&#xff09;缺失的问题。本博客就是讨论如何来解决这个问题的。 1、问题的出现 2、解决途径 在命令行终端中输入命令&#xff1a;xcod…

LeetCode Hot100 49.字母异位词分组

题干&#xff1a; 思路&#xff1a; 输入的是一个字符串数组&#xff0c;输出是一个列表&#xff0c;首先我们需要通过遍历数组获得每一个字符串&#xff0c;我们想要判断获得的任意两个字符串是不是字母异位词&#xff0c;所以可以将获得的字符串排序&#xff08;转换为字符数…

小白初入Android_studio所遇到的坑以及怎么解决

1. 安装Android_studio 参考&#xff1a;Android Studio 安装配置教程 - Windows(详细版)-CSDN博客 Android Studio超级详细讲解下载、安装配置教程&#xff08;建议收藏&#xff09;_androidstudio-CSDN博客 想下旧版本的android_studio的地址&#xff08;仅供参考&#xf…

游戏引擎中LOD渲染技术

一.LOD(Level Of Detail) 为了降低GPU渲染压力,根据摄像机距离模型距离将面数较高的模型替换为面数较低的模型. LOD LOD0(distance<10) LOD1(distance<20) LOD2(distance<30) 故通常引擎中MetaMesh是由一个或多个LOD模型构成. MetaMesh mesh mesh.lod1 mesh.lod…

论文阅读《Structure-from-Motion Revisited》

摘要 增量式地运动结构恢复是从无序图像集合中进行三维重建的一个普遍策略。虽然增量式地重建系统在各个方面上都取得了巨大的进步&#xff0c;但鲁棒性、准确性、完整度和尺度仍然是构建真正通用管道的关键问题。我们提出了一种新的运动结构恢复技术&#xff0c;它改进了目前…

【人工智能训练师】3 集群搭建

开启集群环境 本次环境为单节点伪集群环境&#xff0c;环境中已经安装JDK1.8、Hadoop2.7.7、Mysql5.7、hive2.3.4。— 1.环境中已经安装/root/software/hadoop-2.7.7&#xff0c;格式化HDFS&#xff0c;开启集群&#xff0c;查看集群状态。&#xff08;HDFS端口为9000&#x…

使用 GPT-4V 全面评估泛化情绪识别 (GER)

概述 由于情绪在人机交互中扮演着重要角色&#xff0c;因此情绪识别备受研究人员关注。目前的情感识别研究主要集中在两个方面&#xff1a;一是识别刺激物引起的情感&#xff0c;并预测观众观看这些刺激物后的感受。另一个方面是分析图像和视频中的人类情绪。在本文中&#xf…

8.机器学习--决策树

(⊙﹏⊙)下周有要开组会&#xff0c;不知道该说啥&#xff0c;啊啊啊啊&#x1f62b; 目录 1.基本概念 2.ID3算法 3.C4.5算法 4.CART算法 5.连续与缺失值处理 5.1.连续值处理 5.2.缺失值处理 6.剪枝处理 6.1.预剪枝策略 6.2.后剪枝策略 7.实例代码 1.基本概念 提…

uniApp之uni-file-picker使用踩坑

标题党~也不算坑吧 就是初体验 上传是需要存储一下子的&#xff0c;我以为uniApp是自己免费开的服务给大家中转使用&#xff0c;就没管这个事&#xff0c;但是官网是这么说的&#xff1a; 就我是怎么发现的&#xff0c;使用了一段时间后&#xff0c;上传的图片都裂了&#xff…

22.04Ubuntu---ROS2使用rclcpp编写节点C++

节点需要存在于功能包当中&#xff0c;功能包需要存在于工作空间当中。 所以我们要想创建节点&#xff0c;就要先创建一个工作空间&#xff0c;再创建功能包。 第一步&#xff1a;创建工作空间 mkdir -p chapt2_ws/src/ 第二步&#xff1a;创建example_cpp功能包&#xff0c…

UIStackView使用进阶

01 技术背景 前端的布局方式比较灵活&#xff0c;提供有Flex的布局方式&#xff0c;可以实现不同方向的弹性布局。Flex就像一个容器&#xff0c;可以将其内部的子控件统一进行布局。其包含主轴方向和交叉轴方向&#xff0c;主轴方向表示控件的排布方向&#xff0c;交叉轴方向和…

AMD-OLMo:在 AMD Instinct MI250 GPU 上训练的新一代大型语言模型。

AMD-OLMo是一系列10亿参数语言模型&#xff0c;由AMD公司在AMD Instinct MI250 GPU上进行训练&#xff0c;AMD Instinct MI250 GPU是一个功能强大的图形处理器集群&#xff0c;它利用了OLMo这一公司开发的尖端语言模型。AMD 创建 OLMo 是为了突出其 Instinct GPU 在运行 “具有…

【HarmonyOS】Install Failed: error: failed to install bundle.code:9568289

【HarmonyOS】Install Failed: error: failed to install bundle.code:9568289 一、问题背景&#xff1a; Install Failed: error: failed to install bundle. code:9568289 error: install failed due to grant request permissions failed. View detailed instructions. 11/…

#渗透测试#SRC漏洞挖掘#CSRF漏洞的防御

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…

C++builder中的人工智能(7)如何在C++中开发特别的AI激活函数?

在当今的AI开发中&#xff0c;人工智能模型正迅速增加。这些模型使用数学函数来执行和学习&#xff0c;以便在传播时优化最佳结果&#xff0c;或在反向传播时选择最佳解决方案。其中之一就是激活函数。也称为转移函数或阈值函数&#xff0c;它决定了神经元的激活值作为输出&…

ReactPress数据库表结构设计全面分析

ReactPress Github项目地址&#xff1a;https://github.com/fecommunity/reactpress 欢迎Star。 ReactPress是一个基于React框架开发的开源发布平台和内容管理系统&#xff08;CMS&#xff09;。它不仅支持用户在支持React和MySQL数据库的服务器上搭建自己的博客和网站&#…

小菜家教平台(四):基于SpringBoot+Vue打造一站式学习管理系统

前言 昨天配置完了过滤器&#xff0c;权限检验&#xff0c;基本的SpringSecurity功能已经配置的差不多了&#xff0c;今天继续开发&#xff0c;明天可能会暂停一天整理一下需求&#xff0c;然后就进行CRUD了。 今日进度 补充SpringSecurity异常处理和全局异常处理器 详细操作…

零基础Java第十五期:抽象类接口(一)

目录 一、抽象类 1.1. 抽象的概念 1.2. 抽象类语法 1.3. 抽象类的特性 1.4. 图形类例子 二、 接口 2.1. 接口的概念 2.2. 语法规则 2.3. 接口的特性 2.4. 接口的使用 2.5. 实现多个接口 2.6. 工作当中常用的接口 一、抽象类 1.1. 抽象的概念 如果 一个类中没…

13.UE5流星火雨,引导施法技能制作

2-15 流星火雨&#xff0c;引导施法技能制作、随机数_哔哩哔哩_bilibili 目录 1.为流星火雨添加按键映射 2.创建流星火雨的动画蒙太奇 3.实现播放动画蒙太奇的逻辑 ​编辑 4.定义发射一波流星火雨的发射物 5.使用动画通知释放流星火雨 1.为流星火雨添加按键映射 创建名为流…