Hierholzer算法&重新安排行程

36 篇文章 2 订阅
30 篇文章 0 订阅

1、Hierholzer算法

欧拉迹是指一条包含图中所有边的一条路径,该路径中所有的边会且仅会出现一次。

一个无向图中包含欧拉迹,当且仅当下面两条性质同时满足:

  • 图是连通的
  • 图中每个顶点的度均为偶数

而一个有向图包含欧拉迹,当且仅当下面两条性质同时满足:

  • 图是连通的
  • 图中每个顶点入度和出度相同

Hierholzer算法用于在连通图寻找欧拉迹,其流程非常简单。

从一个可能的起点出发,进行深度优先搜索,但是每次沿着辅助边从某个顶点移动到另外一个顶点的时候,都需要删除这个辅助边。如果没有可移动的路径,则将所在结点加入到栈中,并返回。

void dfs(Node node, Deque trace){
	while(!node.edges.isEmpty())
	{
		Node next = node.edges.removeLast();
		dfs(next, trace);
	}
	trace.addLast(node);
}

最后得到的栈中保存的就是整个欧拉迹中的顶点。

这个算法应用起来非常简单,下面我们先证明几条性质。

性质1. 如果图中包含闭欧拉迹,则栈的底部存储的必定是起点。如果图中包含的是开欧拉轨迹,则栈底部存储的是与起点不同的另外一个奇度数端点。

证明:当我们要入栈时,说明当前所在顶点没有任何边了。考虑到从起点出发到当前结点的路径中,除了起点和当前顶点外,所有的顶点都失去了偶数度数(在移除了途经的边后)。如果起点和当前顶点不同,那么两者都失去了奇数度数。如果图中包含闭欧拉迹,这意味着所有顶点的初始度数都是偶数,而当前顶点的剩余度数为0,表示当前顶点的初始度数必定是奇数,当然这是不可能的,因此假设不成立,当前顶点就是起点。同样的对于开欧拉迹,当前顶点不可能是起点,否则起点的度数就是偶数,而开欧拉迹中起点和终点的度数一定是奇数。这样就能推出当前顶点既不是起点度数也是偶数,因此一定是终点。

性质2.如果图中包含闭欧拉迹,入栈的倒数第二个顶点一定是路径中的第二个顶点。

证明:由于路径中的第二个顶点入栈,说明起点已经入栈过,换言之,起点已经没有多余的边了。

参考原文:Hierholzer算法 | Daltao's blog!

2、例题:重新安排行程

给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 出发。

说明:

如果存在多种有效的行程,你可以按字符自然排序返回最小的行程组合。例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前
所有的机场都用三个大写字母表示(机场代码)。
假定所有机票至少存在一种合理的行程。
示例 1:

输入: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]
输出: ["JFK", "MUC", "LHR", "SFO", "SJC"]
示例 2:

输入: [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
输出: ["JFK","ATL","JFK","SFO","ATL","SFO"]
解释: 另一种有效的行程是 ["JFK","SFO","ATL","JFK","ATL","SFO"]。但是它自然排序更大更靠后。

来源:力扣(LeetCode)
链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

套用欧拉迹的公式,可以这样理解:

def findItinerary(self, tickets):
        import collections
        maps = collections.defaultdict(list)
        for start, end in sorted(tickets)[::-1]:
            maps[start].append(end)
            
        def dfs(cur, maps, res):
            while maps[cur]:
                dfs(maps[cur].pop(), maps, res)
            res.append(cur)
            return res[::-1]

        return dfs("JFK", maps, [])

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值