【贪心算法】经典案例分析2

目录

案例四:最小生成树

案例五:最优并归


贪心算法(Greedy Algorithm)是一种基于贪心策略的算法设计方法,它在每一步选择中都采取当前状态下最优的选择,以期望达到全局最优解。贪心算法通常适用于那些具有最优子结构性质的问题,即问题的最优解可以通过子问题的最优解来构造。


案例四:最小生成树

给定一个带权重的连通无向图,我们希望找到一个包含所有节点的子图,使得这个子图是一棵树(没有环路)并且边的权重之和最小。这个子图被称为最小生成树。

方法:Kruskal算法是解决最小生成树问题的一种经典算法。它基于贪心策略,通过选择当前具有最小权重的边来构建最小生成树。

算法步骤

  1. 初始化:将图中的所有边按照权重从小到大进行排序。

  2. 创建并查集:为每个节点创建一个单独的集合。

  3. 遍历边:按照排序后的顺序,依次考虑每一条边。

  4. 检查边的两个节点是否属于同一集合

    • 如果是,则忽略这条边,因为它会形成环路。
    • 如果不是,则将这条边加入最小生成树中,并将边的两个节点合并到同一个集合中。
  5. 重复步骤3和4,直到所有节点都属于同一个集合,或者所有边都被考虑过。

  6. 最终结果:最小生成树就是构建过程中选择的边的集合。

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n

    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]

    def union(self, x, y):
        root_x, root_y = self.find(x), self.find(y)
        if root_x == root_y:
            return False
        if self.rank[root_x] > self.rank[root_y]:
            self.parent[root_y] = root_x
        elif self.rank[root_x] < self.rank[root_y]:
            self.parent[root_x] = root_y
        else:
            self.parent[root_y] = root_x
            self.rank[root_x] += 1
        return True

def kruskal(graph):
    edges = []
    for node in graph:
        for neighbor, weight in graph[node].items():
            edges.append((node, neighbor, weight))
    edges.sort(key=lambda x: x[2])

    n = len(graph)
    uf = UnionFind(n)
    min_spanning_tree = []

    for edge in edges:
        u, v, w = edge
        if uf.union(u, v):
            min_spanning_tree.append((u, v, w))

    return min_spanning_tree

# 示例图的邻接表表示
graph = {
    'A': {'B': 4, 'C': 2},
    'B': {'A': 4, 'D': 5},
    'C': {'A': 2, 'D': 1},
    'D': {'B': 5, 'C': 1}
}

# 使用Kruskal算法找到最小生成树
min_spanning_tree = kruskal(graph)

# 输出最小生成树的边
for edge in min_spanning_tree:
    print(f'Edge: {edge[0]} - {edge[1]}, Weight: {edge[2]}')

在这段代码中,我们首先定义了一个UnionFind类,用于实现并查集。然后定义了一个kruskal函数,它接受一个带权无向图(以邻接表的形式表示)作为输入,并返回构建出的最小生成树的边。

这个例子的输出将是:

Edge: C - D, Weight: 1
Edge: A - C, Weight: 2
Edge: A - B, Weight: 4

案例五:最优并归

最优并归问题是指将多个有序序列合并成一个有序序列,并使得合并过程中的比较次数最少。这个问题在排序算法中经常出现。

 在最优并归问题中,一个常用的贪心策略是使用优先队列(通常是最小堆)来实现。

具体步骤如下:

  1. 初始化:将每个有序序列的第一个元素放入优先队列,同时记录每个元素所属的序列。

  2. 循环:从优先队列中取出最小的元素,将其加入结果序列,同时取出该元素所属序列的下一个元素放入优先队列。

  3. 重复:重复上述步骤,直到所有序列中的元素都被处理完毕。

假设我们有两个有序序列:

序列1:[2, 4, 6, 8, 10]

序列2:[1, 3, 5, 7, 9]

import heapq

def merge_sorted_lists(lists):
    min_heap = [(lst[0], i, 0) for i, lst in enumerate(lists) if lst]
    heapq.heapify(min_heap)
    result = []

    while min_heap:
        val, lst_index, elem_index = heapq.heappop(min_heap)
        result.append(val)

        if elem_index + 1 < len(lists[lst_index]):
            next_val = lists[lst_index][elem_index + 1]
            heapq.heappush(min_heap, (next_val, lst_index, elem_index + 1))

    return result

# 示例
lists = [[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]
merged_list = merge_sorted_lists(lists)
print("合并后的有序序列:", merged_list)

在这段代码中,我们定义了一个名为merge_sorted_lists的函数,它接受一个包含多个有序序列的列表作为输入,并返回合并后的有序序列。

函数使用了一个最小堆(优先队列)来实现贪心策略。首先,将每个序列的第一个元素和所属序列的索引放入最小堆中。然后,在循环中,从最小堆中取出最小的元素,将其加入结果序列,并将所属序列的下一个元素放入最小堆。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恭仔さん

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值