【202309】算法基础-L7-贪心法(2)

第二节 贪心法应用

例题3 哈夫曼编码

本题为自学内容+题解作业,建议参考课本内容或者这个视频

例题4 单源最短路径

单源最短路径(dijkstra)算法为本章重点

【问题描述】

设 G(V,E) 是一个带权有向图,其中每条边的权值是非负实数 。给定图中的一个点 S∈V,称之为源点,现要求 S 到所有其他顶点 T (T∈V,T≠S) 之间的最短路径长度。路径长度是指一条路径所经过的所有边的边权值之和。

【算法思路】

Dijkstra算法是解单源最短路径问题的贪心算法。其基本思想是,设置顶点集合并不断地做贪心选择来扩充这个集合。当一个顶点已求得从源到该顶点的最短路径时,我们便把该顶点加入到集合中来。从初始的时候,集合中仅含有源点S,到最后计算完毕集合包含了所有从源点可到达的终点。同时我们把结果用一个数组 dist 记录。Dijkstra算法每次从剩余顶点(即未加入集合的顶点)中取出具有最短路径长度的顶点u,将u添加到集合中,同时对数组 dist进行必要的修改。一旦集合包含了所有图中顶点,dist就记录了从源到所有其他顶点之间的最短路径长度。
我们结合以上算法思想把Dijkstra算法进一步规范描述如下:

算法4.3 Dijkstra算法实现:
	输入:一个有向图G,若G中有两个顶点s,u存在一条边,长度用d(s,u)表示
设dist[u]为从起点S到顶点u的最短路径长度,初始时dist[S]=0,对于所有其他未处理顶点u,dist[u]=INF(即 ∞ )
然后我们将图的顶点进行一下分类,对于每个顶点,必定属于以下三个分类之一:
红点:最短路径长度确定的顶点集合,从源点到该点的最短路径长度为dist[u]
绿点:访问到但最短路径未确定的顶点集合,当前已知最短长度保存在dist[u]
白点:未访问到的顶点集合,其长度dist[u]=INF
最开始时,设源点S为绿点,其他所有顶点均为白点
Dijkstra算法更新顶点最短路径长度的具体步骤如下:
1. 在绿点中取出一个长度最短的顶点s,作为当前搜索起点
2. 遍历所有从s出发可以到达的相邻顶点u:
  2.1 若u已经为红点,不做任何处理;
  2.2 若u为白色顶点,将其取出放入绿色顶点,并更新dist[u]=dist[s]+d(s,u)
  2.3 若u为绿色顶点,检查dist[s]+d(s,u)是否比当前dist[u]更短,如果是则
更新当前dist[u]的长度
3. 完成对s所有相邻顶点的搜索,将s加入红点集合
4. 循环重复执行以上步骤1-3,直到不存在绿点(若此时还存在白点,这些白点必定是
   源点无法到达的。
5. 对于所有的红点u,输出dist[u]为最终结果(源点到u的最短路径长度)

Java代码实现:

import java.util.Arrays;

// 最短路径Dijkstra算法实现代码
public class ShortestPathSolution {

    // 定义顶点的访问状态:
    // RED(红):已访问且已确定最短路径
    // GREEN(绿): 已访问但未确定当前长度为最短
    // WHITE(白): 未访问
    static final int RED = 2;
    static final int GREEN = 1;
    static final int WHITE = 0;
    
    /**
     * 计算最短路径
     * @param n 表示最多总共有n个顶点
     * @param edges 所有路径信息,以二维数组给出,
     *     数组元素edges[i] = [s, t, w] 表示从 s 到 t 存在权值为 w 的路径
     * @param src 起点编号
     * @return 输出从起点到所有顶点的路径长度,并返回最后一个路径长度
     */
    public int findAllShortestPath(int n, int[][] edges, int src) {
        // dist[x]: 保存起点到 x 的最短路径长度
        // 初始值为无穷大
        int[] dist = new int[n + 1]; 
        Arrays.fill(dist, Integer.MAX_VALUE);
        // visit[x]表示 x 的访问状态:
        //     0(白)-未访问(初始值); 
        //     1(绿)-已访问未确定; 
        //     2(红)-已确定最短长度
        int[] visit = new int[n + 1];

        // 初始时只有起点被访问到,设置路径长度为0
        visit[src] = GREEN;
        dist[src] = 0;

        // 执行dijkstra搜索最短路径
        for (int i = 0; i < n; i++) {
            // 搜索所有的绿点,找到路径最短的一个
            // 标记为红点,并作为当前搜索点
            int s = 0;
            for (int j = 1; j <= n; j++) 
                if (visit[j] == GREEN && dist[j] < dist[s]) 
                    s = j;

            // 如果没有更多可访问的顶点则直接退出
            if (s == 0) break;
            
            // 标记 s 为当前顶点,设置访问状态为红色并开始搜索
            visit[s] = RED;

            // 下面这行代码输出中间过程,可用于调试
            // System.out.println("s=" + s + ", " + dist[s]);

            // 枚举当前顶点 s 的邻接路径,发现新的路径并更新维护排序数组
            for (int[] ed : edges) {
                int u = ed[0], v = ed[1], w = ed[2];

                // 当前边是 s 的邻边,且目标顶点未完成搜索(不是红色)
                if (s == u && visit[v] != RED) {
                    // 计算从起点经过s到达v的路径长度
                    int d = dist[s] + w;

                    // 计算出一条更短的路径长度,更新路径长度及访问状态
                    if (d < dist[v]) {
                        dist[v] = d;
                        visit[v] = GREEN;
                    }
                }
            }
        }

        // 输出结果并返回
        int ans = -1;
        System.out.println("所有的顶点路径信息:");
        for (int i = 1; i <= n; i++) {
            System.out.println("目标顶点 " + i + " -> " + dist[i]);
            ans = Math.max(ans, dist[i]);
        }
        return ans;
    }

    public static void main(String[] args) {
        ShortestPathSolution sln = new ShortestPathSolution();
        int[][] edges = {{1,2,2}, {1,5,8},{1,4,3},{2,3,5},{3,5,1},{4,3,2},{4,5,6}};
        int n = 5, src = 1;
        int ans = sln.findAllShortestPath(n, edges, src);
        System.out.println("搜索完毕,最远顶点路径为:" + ans);
    }
}

Python3代码实现

# 最短路径Dijkstra算法实现代码

# 定义顶点的访问状态:
# RED(红):已访问且已确定最短路径
# GREEN(绿): 已访问但未确定当前长度为最短
# WHITE(白): 未访问
RED = 2
GREEN = 1
WHITE = 0

## 计算最短路径
## n 表示最多总共有n个顶点
## edges 所有路径信息,以二维数组给出,
##     数组元素edges[i] = [s, t, w] 表示从 s 到 t 存在权值为 w 的路径
## src 起点编号
## 返回:输出从起点到所有顶点的路径长度,并返回最后一个路径长度
def findAllShortestPath(n: int, edges: list[list[int]], src: int) -> int:
    # dist[x]: 保存起点到 x 的最短路径长度
    # 初始值为理论上的最大值(所有边长加起来再加1)
    INF = sum(e[2] for e in edges) + 1
    dist = [INF] * (n + 1) 

    # visit[x]表示 x 的访问状态:
    #     0(白)-未访问(初始值) 
    #     1(绿)-已访问未确定 
    #     2(红)-已确定最短长度
    visit = [WHITE] * (n + 1)

    # 初始时只有起点被访问到,设置路径长度为0
    visit[src] = visit[0] = GREEN
    dist[src] = 0

    # 执行dijkstra搜索最短路径
    for _ in range(n):
        # 搜索所有的绿点,找到路径最短的一个
        # 标记为红点,并作为当前搜索点
        s = min([x for x in range(0, n + 1) if visit[x] == GREEN], key=lambda x: dist[x])

        # 如果没有更多可访问的顶点则直接退出
        if (s == 0): break
        
        # 标记 s 为当前顶点,设置访问状态为红色并开始搜索
        visit[s] = RED

        # 下面这行代码输出中间过程,可用于调试
        print('s=', s, dist[s])

        # 枚举当前顶点 s 的邻接路径,发现新的路径并更新维护排序数组
        for u, v, w in edges:
            # 当前边是 s 的邻边,且目标顶点未完成搜索(不是红色)
            if s == u and visit[v] != RED:
                # 计算从起点经过s到达v的路径长度
                d = dist[s] + w
                # 计算出一条更短的路径长度,更新路径长度及访问状态
                if d < dist[v]:
                    dist[v] = d
                    visit[v] = GREEN

    # 输出结果并返回
    print('所有的顶点路径信息:')
    for i, d in enumerate(dist):
        if visit[i] != RED: continue
        print('目标顶点', i, '->', d)
    return max(dist[1:])

# main
n, src = 5, 1
edges = [[1,2,2],[1,5,8],[1,4,3],[2,3,5],[3,5,1],[4,3,2],[4,5,6]]
ans = findAllShortestPath(n, edges, src)
print('搜索完毕,最远顶点路径为:', ans)

例题5 最小生成树

首先要理清概念,弄清楚什么是:

  • 树 (1 连通,2 无环)
  • 生成树 (连通图的一个子集,且必须是树)
  • 最小生成树(连通图的一个生成树,且边权值之和为最小)

解决最小生成树问题的常见算法:

  • Prim
  • Kruskal

参考讲解视频

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值