数据结构第14节 加权图

加权图是在图论中一种更为复杂的图结构,它扩展了无向图和有向图的概念,通过给图中的边附加一个数值来表示边的某种属性,如成本、距离、容量或相似度等。这个数值被称为边的“权重”。

定义

加权图可以被形式化地定义为一个三元组 ( G = (V, E, W) ),其中:

  • ( V ) 是顶点的集合。
  • ( E ) 是边的集合,每条边连接 ( V ) 中的一对顶点。
  • ( W ) 是一个函数,将每条边映射到一个实数上,即 ( W: E \rightarrow \mathbb{R} )。

分类

加权图可以根据边的方向进一步分类为:

  • 加权无向图:图中的边没有方向,权重是对称的,即 ( W(u,v) = W(v,u) )。
  • 加权有向图:图中的边有方向,权重可能不对称,即 ( W(u,v) ) 可能与 ( W(v,u) ) 不同。

权重的意义

在不同的应用场景中,权重可以有不同的含义:

  • 在网络路由中,权重可能是网络链路的延迟或带宽。
  • 在交通网络中,权重可能是道路的距离或通行时间。
  • 在电子电路中,权重可能是电阻或电容值。
  • 在社交网络中,权重可以表示人际关系的强度。

图的表示

加权图可以通过以下几种方式表示:

  • 邻接矩阵:对于加权图,邻接矩阵中的非零元素表示边的权重。
  • 邻接列表:对于每个顶点,列表中的每个元素除了包含邻接顶点的信息外,还包含边的权重。

常见算法

处理加权图的一些常见算法包括:

  • 最短路径算法:如Dijkstra算法和Bellman-Ford算法,用于找到两点间具有最小权重和的路径。
  • 最小生成树算法:如Prim算法和Kruskal算法,用于在加权连通图中找到一棵包含所有顶点的最小权重的树。
  • 最大流算法:如Ford-Fulkerson算法,用于在有向加权图中找到从源点到汇点的最大流。
  • 匹配算法:如匈牙利算法,用于在加权二部图中找到最优匹配。

实际应用

加权图在多个领域有着广泛的应用,包括:

  • 物流和运输系统:规划最短路线或最低成本的配送路径。
  • 网络通信:优化数据包的传输路径,以减少延迟或提高效率。
  • 生物信息学:构建基因或蛋白质网络,分析其相互作用。
  • 机器学习:构建基于图的模型,如图神经网络,用于预测和分类任务。

加权图是理解和建模现实世界中复杂关系的重要工具,其研究不仅限于理论数学,也与计算机科学、工程学、经济学和生物学等领域密切相关。

为了更深入理解加权图,我们可以考虑一个具体案例:在一个城市交通网络中寻找两点之间的最短路径。在这个网络中,边的权重代表两个地点之间的行驶时间(或者距离)。我们将使用Dijkstra算法来解决这个问题,该算法是一种用于查找加权图中单源最短路径的经典算法。

背景

假设我们有一个城市,其中有若干个地点和连接它们的道路。每个地点都是图中的一个顶点,而每条道路则是一条有向或无向的边,且每条边都有一个权重,代表驾驶所需的时间。我们的目标是从某个起点出发,找到到达另一个终点的最短路径。

Java源代码实现

首先,我们需要定义加权图的数据结构,然后实现Dijkstra算法。以下是一个简化版的实现:

import java.util.*;

class Edge {
    int dest;
    int weight;

    public Edge(int d, int w) {
        dest = d;
        weight = w;
    }
}

class Node {
    List<Edge> neighbors = new ArrayList<>();
    boolean visited = false;
    int distance = Integer.MAX_VALUE;
    Node previous = null;
}

public class DijkstraAlgorithm {

    private List<Node> nodes = new ArrayList<>();

    public void addNode() {
        nodes.add(new Node());
    }

    public void addEdge(int source, int destination, int weight) {
        Node srcNode = nodes.get(source);
        Node destNode = nodes.get(destination);
        srcNode.neighbors.add(new Edge(destNode, weight));
    }

    public void dijkstra(int startNode) {
        PriorityQueue<Node> queue = new PriorityQueue<>(Comparator.comparingInt(node -> node.distance));
        Node start = nodes.get(startNode);
        start.distance = 0;
        queue.add(start);

        while (!queue.isEmpty()) {
            Node current = queue.poll();
            if (current.visited)
                continue;

            for (Edge edge : current.neighbors) {
                Node neighbor = edge.dest;
                int tentativeDistance = current.distance + edge.weight;

                if (tentativeDistance < neighbor.distance) {
                    neighbor.distance = tentativeDistance;
                    neighbor.previous = current;
                    queue.add(neighbor);
                }
            }

            current.visited = true;
        }
    }

    public List<Integer> getPath(int destination) {
        List<Integer> path = new ArrayList<>();
        Node current = nodes.get(destination);

        while (current != null) {
            path.add(current.hashCode());
            current = current.previous;
        }

        Collections.reverse(path);
        return path;
    }

    public static void main(String[] args) {
        DijkstraAlgorithm graph = new DijkstraAlgorithm();

        // 添加节点
        for (int i = 0; i < 6; i++)
            graph.addNode();

        // 添加边
        graph.addEdge(0, 1, 5);
        graph.addEdge(0, 2, 3);
        graph.addEdge(1, 3, 6);
        graph.addEdge(1, 2, 2);
        graph.addEdge(2, 4, 4);
        graph.addEdge(2, 5, 2);
        graph.addEdge(3, 4, -2); // 负权重边,但Dijkstra不处理负权重环
        graph.addEdge(4, 5, 1);

        // 执行Dijkstra算法
        graph.dijkstra(0);

        // 输出最短路径
        System.out.println("Shortest Path to all nodes from node 0:");
        for (int i = 0; i < graph.nodes.size(); i++) {
            System.out.println("To node " + i + ": " + graph.getPath(i));
        }
    }
}

解析

  1. Edge 类定义了边的目的地和权重。
  2. Node 类定义了顶点的邻居列表、是否访问过、当前距离和前驱节点。
  3. DijkstraAlgorithm 类实现了加权图的添加节点和边的功能,以及Dijkstra算法的执行。
  4. 主方法中创建图、添加节点和边,执行Dijkstra算法并打印出从起始点到所有其他点的最短路径。

这个Demo展示了如何使用Java实现加权图以及Dijkstra算法的基本框架,实际应用中可能需要根据具体需求进行调整和优化。例如,处理大规模图时,可能需要更高效的数据结构和算法优化,如使用Fibonacci堆代替优先队列。

Demo展示

1. 迷宫游戏:寻找出口

在迷宫游戏中,玩家需要从起点出发,找到通往终点的路径。这可以通过图论中的搜索算法来实现,例如深度优先搜索(DFS)或广度优先搜索(BFS)。

Java代码示例:使用DFS寻找迷宫出口
import java.util.*;

public class MazeGame {
    private static final int[][] DIRECTIONS = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    private char[][] maze;
    private boolean[][] visited;

    public MazeGame(char[][] maze) {
        this.maze = maze;
        this.visited = new boolean[maze.length][maze[0].length];
    }

    private boolean dfs(int x, int y) {
        if (x < 0 || x >= maze.length || y < 0 || y >= maze[0].length || maze[x][y] == '#' || visited[x][y]) {
            return false;
        }
        if (maze[x][y] == 'E') {
            return true;
        }
        visited[x][y] = true;
        for (int[] dir : DIRECTIONS) {
            if (dfs(x + dir[0], y + dir[1])) {
                return true;
            }
        }
        return false;
    }

    public boolean hasPath() {
        for (int i = 0; i < maze.length; i++) {
            for (int j = 0; j < maze[0].length; j++) {
                if (maze[i][j] == 'S') {
                    return dfs(i, j);
                }
            }
        }
        return false;
    }

    public static void main(String[] args) {
        char[][] maze = {
            {'#', '#', '#', '#', 'S', '#', '#'},
            {'#', '.', '.', '.', '.', '.', '#'},
            {'#', '#', '#', '#', '#', '.', '#'},
            {'#', '.', '.', '.', '.', '.', '#'},
            {'#', '#', '#', '#', '#', 'E', '#'}
        };

        MazeGame game = new MazeGame(maze);
        System.out.println(game.hasPath() ? "There is a path." : "There is no path.");
    }
}

2. 策略游戏:资源收集与管理

在策略游戏中,玩家需要收集资源、建设设施,并管理资源分配。这可以通过构建加权图,其中节点代表资源点或设施,边的权重代表资源的流动成本或收益。

Java代码示例:资源管理加权图
import java.util.*;

public class ResourceManagementGame {
    private Map<String, List<Pair<String, Integer>>> graph = new HashMap<>();

    public void addNode(String name) {
        graph.putIfAbsent(name, new ArrayList<>());
    }

    public void addEdge(String source, String target, int weight) {
        graph.get(source).add(new Pair<>(target, weight));
    }

    public int calculateTotalResources(String start, int resources) {
        Set<String> visited = new HashSet<>();
        Queue<Pair<String, Integer>> queue = new LinkedList<>();
        queue.add(new Pair<>(start, resources));

        while (!queue.isEmpty()) {
            Pair<String, Integer> current = queue.poll();
            String node = current.getKey();
            int resource = current.getValue();
            if (visited.contains(node)) continue;
            visited.add(node);
            resource += processNode(node);
            for (Pair<String, Integer> next : graph.get(node)) {
                queue.add(new Pair<>(next.getKey(), resource - next.getValue()));
            }
        }
        return resources;
    }

    private int processNode(String node) {
        // Simulate resource processing at each node
        return node.equals("mine") ? 10 : 0;
    }

    public static void main(String[] args) {
        ResourceManagementGame game = new ResourceManagementGame();
        game.addNode("mine");
        game.addNode("factory");
        game.addNode("warehouse");
        game.addEdge("mine", "factory", 2);
        game.addEdge("factory", "warehouse", 3);
        game.addEdge("warehouse", "mine", 1);
        System.out.println("Total resources: " + game.calculateTotalResources("mine", 100));
    }
}

class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }
}

3. 棋盘游戏:策略与移动

在棋盘游戏中,玩家需要在棋盘上移动棋子以达成胜利条件。棋盘上的每个位置可以视为图中的一个节点,而合法的移动则构成边。

Java代码示例:国际象棋中的骑士走法
import java.util.*;

public class ChessKnightMoves {
    private static final int[][] MOVES = {{2, 1}, {1, 2}, {-1, 2}, {-2, 1}, {-2, -1}, {-1, -2}, {1, -2}, {2, -1}};

    public int minMoves(int startX, int startY, int endX, int endY) {
        int[][] board = new int[8][8];
        Queue<int[]> queue = new LinkedList<>();
        queue.add(new int[]{startX, startY});
        board[startX][startY] = 1;

        while (!queue.isEmpty()) {
            int[] pos = queue.poll();
            if (pos[0] == endX && pos[1] == endY) {
                return board[pos[0]][pos[1]] - 1;
            }
            for (int[] move : MOVES) {
                int newX = pos[0] + move[0];
                int newY = pos[1] + move[1];
                if (newX >= 0 && newX < 8 && newY >= 0 && newY < 8 && board[newX][newY] == 0) {
                    queue.add(new int[]{newX, newY});
                    board[newX][newY] = board[pos[0]][pos[1]] + 1;
                }
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        ChessKnightMoves game = new ChessKnightMoves();
        System.out.println("Minimum moves: " + game.minMoves(0, 0, 7, 7));
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RAG综述汇总

第一篇&#xff1a;Retrieval-Augmented Generation for Large Language Models: A Survey(同济/复旦) 论文链接 1.简介 这篇全面的综述论文详细研究了 RAG 范式的发展&#xff0c;包括 Naive RAG、Advanced RAG 和 Modular RAG。介绍了 RAG 框架的三个基础技术&#xff0c;…

Python28-7.4 独立成分分析ICA分离混合音频

独立成分分析&#xff08;Independent Component Analysis&#xff0c;ICA&#xff09;是一种统计与计算技术&#xff0c;主要用于信号分离&#xff0c;即从多种混合信号中提取出独立的信号源。ICA在处理盲源分离&#xff08;Blind Source Separation&#xff0c;BSS&#xff0…

CANopen协议开发梳理总结笔记教程

0、提醒 CANOpen使用时&#xff0c;需要清楚什么是大端和小端&#xff0c;这对于CANOpen数据发送及解析时&#xff0c;有很大的帮助。且学习开发CANOpen时&#xff0c;需要具备一定的CAN基础。 1、CANOpen协议介绍 ①、什么是CANOpen协议 CANOpen协议是一种架构在控制局域网络…

yaml格式转换成json格式

yaml格式转换成json格式 ①postman生成的结果是yaml格式 ps&#xff1a;postman输出的格式是没有自动换行的&#xff0c;需要将内容换行 ②复制到Python的脚本跑一趟&#xff1a;自动换行并去掉/n&#xff1b; str " "//(postman输出的内容&#xff09; print(st…

【python技巧】parser传入参数

参考网址: https://lightning.ai/docs/pytorch/LTS/api/pytorch_lightning.utilities.argparse.html#pytorch_lightning.utilities.argparse.add_argparse_args 1. 简单传入参数. parse_known_args()方法的作用就是把不在预设属性里的参数也返回,比如下面这个例子, 执行pytho…

2024年信息系统项目管理师1批次上午客观题参考答案及解析(1)

1、新型基础设施建设是以新发展理念为引领&#xff0c;以()为驱动&#xff0c;以信息网络为基础&#xff0c;面向高质量发展需要&#xff0c;提供数字转型、智能升级、融合创新等服务的基础设施体系。 A&#xff0e;技术创新 B&#xff0e;人工智能 C&#xff0e;区块链 D&…

代码随想录算法训练营第二十七天|452. 用最少数量的箭引爆气球、435. 无重叠区间、763.划分字母区间

452. 用最少数量的箭引爆气球 如何使用最少的弓箭呢&#xff1f; 直觉上来看&#xff0c;貌似只射重叠最多的气球&#xff0c;用的弓箭一定最少&#xff0c;那么有没有当前重叠了三个气球&#xff0c;我射两个&#xff0c;留下一个和后面的一起射这样弓箭用的更少的情况呢&am…

STM32-输入捕获IC和编码器接口

本内容基于江协科技STM32视频学习之后整理而得。 文章目录 1. 输入捕获IC1.1 输入捕获IC简介1.2 频率测量1.3 输入捕获通道1.4 主从触发模式1.5 输入捕获基本结构1.6 PWMI基本结构 2. 输入捕获库函数及代码2.1 输入捕获库函数2.2 6-6 输入捕获模式测频率2.2.1 硬件连接2.2.2 硬…

曹操的五色棋布阵 - 工厂方法模式

定场诗 “兵无常势&#xff0c;水无常形&#xff0c;能因敌变化而取胜者&#xff0c;谓之神。” 在三国的战场上&#xff0c;兵法如棋&#xff0c;布阵如画。曹操的五色棋布阵&#xff0c;不正是今日软件设计中工厂方法模式的绝妙写照吗&#xff1f;让我们从这个神奇的布阵之…

MSPM0G3507——串口0从数据线传输变为IO口传输

默认的跳线帽时这样的&#xff0c;这样时是数据线传输 需要改成这样&#xff0c;即可用IO口进行数据传输

实验六 图像的傅立叶变换

一&#xff0e;实验目的 1了解图像变换的意义和手段&#xff1b; 2熟悉傅立叶变换的基本性质&#xff1b; 3熟练掌握FFT变换方法及应用&#xff1b; 4通过实验了解二维频谱的分布特点&#xff1b; 5通过本实验掌握利用MATLAB编程实现数字图像的傅立叶变换。 6评价人眼对图…

Mac 系统如何将搜狗输入法设置为默认输入法

Mac 系统默认将自带的ABC输入法作为默认输入法&#xff0c;很不方便中文输入&#xff0c;想设置搜狗输入法为默认输入法如何设置呢&#xff1f;具体步骤如下&#xff1a; 1、打开&#xff1a;系统设置——键盘——文字输入&#xff0c;点击设置 2、点击左下角的 3、选择 其他…

52-5 内网代理2 - LCX端口转发(不推荐使用LCX)

环境搭建: 本地开3台虚拟机:kali(必须)、windows2012与2008 (可换成其他windows虚拟机) kali - 网络配置成桥接模式 windows2012 - 设置两个网卡,NAT与桥接模式 注意:windows2012要关闭防火墙,要不然其他主机ping不通 关闭防火墙后再开启远程桌面连接 windwos20…

Java项目:基于SSM框架实现的德云社票务管理系统【ssm+B/S架构+源码+数据库+开题报告+毕业论文】

一、项目简介 本项目是一套基于SSM框架实现的德云社票务管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、功…

Python 学习中什么是字典,如何操作字典?

什么是字典 字典&#xff08;Dictionary&#xff09;是Python中的一种内置数据结构&#xff0c;用于存储键值对&#xff08;key-value pair&#xff09;。字典的特点是通过键来快速查找值&#xff0c;键必须是唯一的&#xff0c;而值可以是任何数据类型。字典在其他编程语言中…

游戏AI的创造思路-技术基础-遗传算法

遗传算法&#xff0c;选对了遗传算子&#xff0c;那就是优秀的继承者&#xff0c;选错了&#xff0c;那就是传说在祸害遗千年~~~~~ 目录 1. 定义 2. 发展历史 3. 遗传算法的基本原理和流程 3.1. 基本原理 3.1.1.基本原理 3.1.2. 算法流程 3.1.3. 关键要素 3.2. 函数和方…

栈和队列---循环队列

1.循环队列的出现 &#xff08;1&#xff09;上面的这个就是一个普通的数据的入队和出队的过程我们正常情况下去实现这个入队和出队的过程&#xff0c;就是这个数据从这个队尾进入&#xff0c;从队头离开&#xff0c;但是这个加入的时候肯定是没有其他的问题的&#xff0c;直接…

为什么固定尺寸 AdSense 广告依旧会出现并非指定的尺寸广告?

经常在网站上投放谷歌 AdSense广告的站长应该都碰到过&#xff0c;明明投放的是固定尺寸的广告位里旧会出现并非指定尺寸的AdSense 广告&#xff0c;很诡异的感觉。其实这都是因为你的 AdSense 账号广告优化造成的&#xff0c;其中里面就包含了广告尺寸优化&#xff0c;只需要在…

盘点当下智能体应用开发的几种形态

现在多智能体系统开发的关注度越来越高了&#xff0c;不光在开发者的圈子热度很高&#xff0c;很多职场人士&#xff0c;甚至是小白也参与其中&#xff0c;因为现在的门槛越来越低了&#xff0c;尤其是&#xff0c;最近特别火的扣子&#xff08;coze&#xff09;和百度的appbui…

Sequelize 操作 MySQL 数据库

安装 npm install --save sequelize安装驱动程序&#xff1a; npm install --save mysql2连接到数据库 要连接到数据库,必须创建一个 Sequelize 实例. 这可以通过将连接参数分别传递到 Sequelize 构造函数或通过传递一个连接 URI 来完成&#xff1a; const {Sequelize} re…