LC 332.重新安排行程

332.重新安排行程

给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。

所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。

  • 例如,行程 ["JFK", "LGA"]["JFK", "LGB"] 相比就更小,排序更靠前。

假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。

示例 1:

输入: tickets = [[“MUC”,“LHR”],[“JFK”,“MUC”],[“SFO”,“SJC”],[“LHR”,“SFO”]]
输出:[“JFK”,“MUC”,“LHR”,“SFO”,“SJC”]

示例 2:

输入: tickets = [[“JFK”,“SFO”],[“JFK”,“ATL”],[“SFO”,“ATL”],[“ATL”,“JFK”],[“ATL”,“SFO”]]
输出:[“JFK”,“ATL”,“JFK”,“SFO”,“ATL”,“SFO”]
解释: 另一种有效的行程是 [“JFK”,“SFO”,“ATL”,“JFK”,“ATL”,“SFO”] ,但是它字典排序更大更靠后。

提示:

  • 1 ≤ t i c k e t s . l e n g t h ≤ 300 1 \leq tickets.length \leq 300 1tickets.length300
  • t i c k e t s [ i ] . l e n g t h = = 2 tickets[i].length == 2 tickets[i].length==2
  • f r o m i . l e n g t h = = 3 fromi.length == 3 fromi.length==3
  • t o i . l e n g t h = = 3 toi.length == 3 toi.length==3
  • fromitoi 由大写英文字母组成
  • fromi != toi

解法一(回溯)

思路分析:

  1. 对于该问题进行分析,即我们需要从起点去寻找路径,找到一个节点后,若该节点符合则可以继续查找,若该节点不符合,则需要回溯删除该节点,换节点继续搜索。
  2. 即解决该题,要考虑回溯,则使用回溯的思路来解决。
  3. 考虑回溯函数的三要素:
    1. 即回溯函数的返回值和参数;对于该题,我们需要确定选择某个节点后,可以找到符合题意(使用所有机票,并字典排序返回最小)的行程,所以当可以找到时,返回true,如此便可确定选择该节点,可以找到行程;当无法找到时,再进行回溯;对于函数的参数,可以当需要时进行添加。
    2. 思考回溯的结束条件,假设符合题意的路径存储在List<String> ans中,则当ans.size() == tickets.size()+1时,说明找到路径,返回true
    3. 考虑回溯的遍历过程,即根据上一层递归确定的某机票的终点,即该层递归所依据的终点,来查找对应的机票,并选择对应的节点加入路径中,然后继续递归并判断是否为符合题意得节点,若符合,则结束递归,若不符合,则回溯并更换节点。
  4. 对于根据机票起点,查找对应字典序最小得终点;使用for循环遍历整个tickets机票列表,容易超时。
  5. 所以可以再寻找行程前,对整个机票做一个映射关系,即创建一个Map<String, Map<String, Integer>> reflects;则可以通过起点字符串,快速得到其对应得所有终点reflects.get(from);即Map<String, Integer>中,Integer用于记录对应终点得数目,每到达一次对应终点,则其数目减一,如此可以避免重复使用机票。
  6. 同时因为题目要求所得行程为字典排序最小的行程,所以Map<String, Integer> reflect = TreeMap(),即TreeMap()使对应得终点机票按照key,即字符串字典序进行排序。
  7. 如此,回溯函数,选取终点节点时,是从字典排序最小得终点开始选择,这样便可以保证,函数获取到的第一条行程,便是最符合题意得行程。

实现代码如下:

class Solution {
	LinkedList<String> ans = new LinkedList<>();
	Map<String, Map<String, Integer>> reflects = new HashMap<>();
    public List<String> findItinerary(List<List<String>> tickets) {
		// 建立机票之间起点到多个终点的映射
		for (List<String> ticket : tickets) {
			Map<String, Integer> reflect;	// 终点Map
			if (reflects.containsKey(ticket.get(0))) {
				reflect = reflects.get(ticket.get(0));	// 获取已存在的起点-终点映射
				reflect.put(ticket.get(1), reflect.getOrDefault(ticket.get(1), 0) + 1);
			} else {
				reflect = new TreeMap<>();	// 升序Map
				reflect.put(ticket.get(1), 1);	// 保存对应的终点及数目
			}
			reflects.put(ticket.get(0), reflect);	// 更新 起点-终点的映射
		}
		ans.add("JFK");	// 添加行程起点
		backtracking(tickets.size());
		return ans;
    }
	private boolean backtracking(int ticketNumber) {
		if (ans.size() == ticketNumber + 1)
			return true;	// 找到符合题意的行程
		String from = ans.getLast();
		if (reflects.containsKey(from)) {
			for (Map.Entry<String, Integer> ticket : reflects.get(from).entrySet()) {
				int count = ticket.getValue();	// 获取该起点对应的终点的数目
				if (count > 0) {	// 说明存在机票
					ans.add(ticket.getKey());	// 将对应终点添加到行程
					ticket.setValue(count-1);	// 修改机票数目
					if (backtracking(ticketNumber))
						return true;	// 找到符合题意的行程后 返回
					ans.removeLast();	// 回溯
					ticket.setValue(count);
				}
			}
		}
		return false;
	}
}

提交结果如下:

解答成功:
执行耗时:9 ms,击败了55.67% 的Java用户
内存消耗:44.4 MB,击败了13.22% 的Java用户

复杂度分析:

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( n ) O(n) O(n)
  • 31
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值