重新安排行程问题

问题描述

我一开始的做法:

class Solution {
    ArrayList<String> result = new ArrayList<>();
    ArrayList<String> path = new ArrayList<>();
    boolean[] used;
    
    public List<String> findItinerary(List<List<String>> tickets) {
        if (tickets.size() == 0) {
            return result;
        }
        used = new boolean[tickets.size()]; 

        //为空时添加第一个元素并开始回溯
        for (int i = 0; i < tickets.size(); i++) {
            if (tickets.get(i).get(0).equals("JFK")) {
                path.add(tickets.get(i).get(0));
                path.add(tickets.get(i).get(1));
                used[i] = true;
                backtracing(tickets);
                used[i] = false;
                path.removeLast();
                path.removeLast();
            } 
        }
        return result;
    }

    public void backtracing(List<List<String>> tickets) {
        if (path.size() == tickets.size() + 1) {
            //System.out.println(path.toString());
            if (result.isEmpty()) {
                result = new ArrayList<>(path);
            } else{
                for (int i = 0; i < path.size(); i++) {
                    if (path.get(i).compareTo(result.get(i)) < 0) {
                        result = new ArrayList<>(path);
                        break;
                    }
                    if (path.get(i).compareTo(result.get(i)) > 0) {
                        break;
                    }
                }
            }
        }

        int addIndex = -1;

        //一次添加一个元素
        for (int i = 0; i < tickets.size(); i++) {
            if (used[i]) {
                continue;
            }
            if (tickets.get(i).get(0).equals(path.get(path.size() - 1))) {
                path.add(tickets.get(i).get(1));
                used[i] = true;
                backtracing(tickets);
                used[i] = false;
                path.removeLast();
            }
        }    
    }
}

可能能得到正确的结果,但是会超时,因为每次都是一个完全的遍历,O(n^tickets.size())的时间复杂度

改进的地方:对机票的信息进行预先的排序,同时用哈希表存储,这样可以降低搜索的复杂度从而提高效率

使用回溯的思想:

一个需要注意的地方:这里的回溯返回值为boolean

**基于list的回溯:**预先对tickets进行排序,然后就是简单的回溯,但是在最新的测试样例里,还是会超时

class Solution {
    LinkedList<String> result = new LinkedList<>();
    LinkedList<String> path = new LinkedList<>();
    boolean[] used;
    
    public List<String> findItinerary(List<List<String>> tickets) {
        Collections.sort(tickets, (a, b) -> a.get(1).compareTo(b.get(1)));
        used = new boolean[tickets.size()];
        path.add("JFK");
        backtracing((ArrayList)tickets);
        return result;
    }

    public boolean backtracing(ArrayList<List<String>> tickets) {
        if (path.size() == tickets.size() + 1) {
            result = new LinkedList<>(path);
            return true;
        }

        for (int i = 0; i < tickets.size(); i++) {
            if (!used[i] && tickets.get(i).get(0).equals(path.getLast())) {
                path.add(tickets.get(i).get(1));
                used[i] = true;

                if (backtracing(tickets)) {
                    return true;
                }

                //回溯
                used[i] = false;
                path.removeLast();
            }
        }
        return false;
    }
    
}

基于map的回溯:

用treemap来存储同一个起始站的机票,对终点进行排序,这样找到的第一组路径就是最终需要的序列最小的路径

class Solution {
    

    Map<String, Map<String, Integer>> record;
    LinkedList<String> res;
    public List<String> findItinerary(List<List<String>> tickets) {
        record = new HashMap<>();
        res = new LinkedList<>();
        //初始化record
        for (List<String> ticket : tickets) {
            //temp存的<目的地,票数>
            Map<String, Integer> temp;
            String startSite = ticket.get(0); //当前机票出发地
            String destination = ticket.get(1); //当前机票终点站
            if (record.containsKey(startSite)) {
                temp = record.get(startSite);
                temp.put(destination, temp.getOrDefault(destination, 0) + 1);
            } else {
                temp = new TreeMap<>();
                temp.put(destination, 1);
            }
            record.put(startSite, temp);
        }
        res.add("JFK");
        backtracing(tickets);
        return res;
    }

    public boolean backtracing(List<List<String>> tickets) {
        if (res.size() == tickets.size() + 1) {
            return true;
        }

        if (record.containsKey(res.getLast())) {
            for (Map.Entry<String, Integer> entry : record.get(res.getLast()).entrySet()) {
                int count = entry.getValue();
                if (count > 0) {
                    res.add(entry.getKey());
                    entry.setValue(count - 1);
                    if(backtracing(tickets)) {
                        return true;
                    }

                    entry.setValue(count);
                    res.removeLast();
                }
            }
        }
        return false;
    }

    
}

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
思路: 1. 先建立一个图,用邻接表表示。遍历tickets数组,以出发城市为键,以到达城市为值,将所有相同出发城市的到达城市加入到对应的键的值列表。 2. 对每个出发城市的到达城市列表进行排序,保证按字母顺序进行访问。 3. 从"JFK"出发,进行深度优先遍历。如果当前城市的到达城市列表不为空,则按顺序访问列表中的城市,并将访问过的城市从列表中删除。 4. 如果当前城市没有到达城市或者到达城市已经全部被访问过了,则将当前城市加入结果列表的头部。 5. 最后得到的结果列表,逆序输出即可得到答案。 代码实现如下: ```python from collections import defaultdict def findItinerary(tickets): # 用于保存图 graph = defaultdict(list) # 将tickets中的数据存入图 for ticket in tickets: src, dst = ticket graph[src].append(dst) # 按照字母顺序对到达城市进行排序 for src in graph: graph[src].sort() def dfs(city): # 当前城市的到达城市列表 destinations = graph[city] while destinations: # 递归遍历下一个城市 dfs(destinations.pop(0)) # 将当前城市加入结果列表 result.insert(0, city) # 结果列表 result = [] dfs("JFK") return result # 测试 tickets = [["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]] print(findItinerary(tickets)) # 输出:['JFK', 'MUC', 'LHR', 'SFO', 'SJC'] ``` 复杂度分析: - 时间复杂度:建立图的过程需要遍历tickets数组,时间复杂度为O(n),其中n为数组的长度。遍历图的过程中,所有的边都会被访问一次,时间复杂度为O(m),其中m为图中的边数。因此,总的时间复杂度为O(n+m)。 - 空间复杂度:使用了一个字典来保存图,空间复杂度为O(m),其中m为图中的边数。递归调用的深度为图中的边数+1,空间复杂度为O(m+1)。最后返回的结果列表的空间复杂度为O(n),其中n为结果列表的长度。因此,总的空间复杂度为O(m+n)。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值