2022.3.23 力扣46,47,332

本文介绍了如何使用回溯法解决全排列和旅行路线的组合优化问题。通过示例详细解释了46题全排列、47题全排列2以及332题重新安排行程的解题思路,包括使用额外数组记录已排列元素、去重策略以及回溯过程中的剪枝操作。
摘要由CSDN通过智能技术生成

学习回溯法

follow:代码随想录


46 全排列

题目描述:
给定一个不含重复数字的数组 nums ,返回其所有可能的全排列 。你可以按任意顺序返回答案。

示例:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

解析:
排列是有序的,[1,2]和[2,1]是两个集合。
这道题的重点就是:如何记录已经排列的元素。

  • 使用used额外数组,记录目前那个元素已经排列
  • 将所有以排列好的元素,移动到数组的前面,下一次递归时,跳过已排列好的元素

方法二:

List<List<Integer>> res = new ArrayList();
    List<Integer> element = new ArrayList();
    public List<List<Integer>> permute(int[] nums) {
        backTracking(nums, 0);
        return res;
    }

    public void backTracking(int[] nums, int now_index){
        if(now_index == nums.length){
            res.add(new ArrayList<>(element));
            return;
        }
        for(int i = now_index; i < nums.length; i++){
            int temp = 0;
            element.add(nums[i]);
            if(i != now_index){
                temp = nums[i];
                nums[i] = nums[now_index];
                nums[now_index] = temp;
            }
            backTracking(nums, now_index + 1);
            if(i != now_index){
                temp = nums[i];
                nums[i] = nums[now_index];
                nums[now_index] = temp;
            }
            element.remove(element.size() - 1);
        }
    }

方法一:

List<List<Integer>> res = new ArrayList();
    List<Integer> element = new ArrayList();
    boolean[] used;
    public List<List<Integer>> permute(int[] nums) {
        used = new boolean[nums.length];
        backTracking(nums, 0);
        return res;
    }

    public void backTracking(int[] nums, int now_index){
        if(now_index == nums.length){
            res.add(new ArrayList<>(element));
            return;
        }
        for(int i = 0; i < nums.length; i++){
            if(used[i]) continue;
            element.add(nums[i]);
            used[i] = true;
            backTracking(nums, now_index + 1);
            element.remove(element.size() - 1);
            used[i] = false;
        }
    }
47. 全排列2⃣️

题目描述:
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例:

输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]

解析:
该题的关键就是去重,这个去重需要两点,分别是:同一树枝去重和同一层去重

解答:

	List<List<Integer>> res = new ArrayList();
    List<Integer> element = new ArrayList();
    //used标志用来记录当前已遍历的数组中的元素:同一个树枝使用过
    boolean[] used;
    public List<List<Integer>> permuteUnique(int[] nums) {
        used = new boolean[nums.length]; 
        backTracking(nums, 0);
        return res;
    }

    public void backTracking(int[] nums, int now_index){
        if(now_index == nums.length){
            res.add(new ArrayList<>(element));
            return;
        }
        //flag标志用来记录重复的元素:同一层使用过
        boolean[] flag = new boolean[21];
        for(int i = 0; i < nums.length; i++){
            if(used[i] || flag[nums[i] + 10]) continue;
            element.add(nums[i]);
            used[i] = true;
            flag[nums[i] + 10] = true;
            backTracking(nums, now_index + 1);
            element.remove(element.size() - 1);
            used[i] = false;
        }
    }
332 重新安排行程

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

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

例如,行程 [“JFK”, “LGA”] 与 [“JFK”, “LGB”] 相比就更小,排序更靠前。
假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。

解答:

//保存结果集
    List<String> res = new ArrayList<>();
    //首先使用一个数据结构可以存储tickets信息
    //为了提前剪枝,需要记录的信息有:该出发机场的目的机场有哪些、这些目的机场按照字典顺序进行排序、从该出发机场到目的机场是否已经遍历到(若有多张票,即为该出发机场到目的机场还剩多少张票)
    Map<String,Map<String, Integer>> record  = new HashMap<>();
    public List<String> findItinerary(List<List<String>> tickets) {   
        //TreeMap是一个能比较元素大小的Map集合,会对传入的key进行了大小排序
        for(List<String> ticket : tickets){
            Map<String, Integer> temp = null;
            //record中有出发机场
            if(record.containsKey(ticket.get(0))){
                temp = record.get(ticket.get(0));
                temp.put(ticket.get(1), temp.getOrDefault(ticket.get(1), 0) + 1);
            }else{
                //record中没有出发机场
                //使用TreeMap,针对该出发机场的目的机场按字典顺序进行排序
                temp = new TreeMap<>();
                temp.put(ticket.get(1), 1);
            }
            record.put(ticket.get(0), temp);
        }
        //进行回溯
        res.add("JFK");
        backTracking(tickets.size(), "JFK");
        return res;
    }
    
    public boolean backTracking(int nums, String fromStation){
        //找到一个叶节点就是结果,可以直接返回结果
        if(res.size() == nums + 1) return true;
        //可能该map中不包括fromStation,则直接返回
        if(record.containsKey(fromStation)){
            for(Map.Entry<String, Integer> entry : record.get(fromStation).entrySet()){
                //目的地点已经按照字典排好序,只需要找到叶子节点,就可以得到结果
                //首先看该目的地还有没有票
                int count = entry.getValue();
                if(count > 0){
                    entry.setValue(count - 1);
                    res.add(entry.getKey());
                    if(backTracking(nums, entry.getKey())) return true;
                    entry.setValue(count);
                    res.remove(res.size() - 1);
                }
            }
        }
        return false;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值