代码随想录算法训练营第二十九天|491.递增子序列、46.全排列、47.全排列 II

代码随想录算法训练营第二十九天|491.递增子序列、46.全排列、47.全排列 II

491.递增子序列

题目链接

问题简述:输出数组中的所有非递减子序列,数组中和序列中可能存在重复元素,但结果的序列集不能有重复的序列。

思考:其实回溯的题很多都很相似啊,模版化有点重。本质就是无法确定需要套几层for循环,只能用回溯。这道题区别在于,因为要找原数组的非递减序列,所以不能改变原数组序列顺序,但仍要去重。去重的本质就是同一层一个数字只能访问一次,同一层的结点就是每个for循环,每个分支就是递归,所以只需要在for前标记单层的数字访问情况,即可去重。

算法思路

  • 定义result存储所有组合,定义list存储当前序列。
  • 递归函数中在for前先定义一个used数组,用来表示数组中数字[-100,100]的使用情况,如果数字被使用或者path不为空的同时,满足前一个数字大于当前数字,则跳过本次循环。没有跳过则将当前节点加入path,同时修改used值,再进行递归,最后回溯path和used数组。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> list = new ArrayList<>();
    public List<List<Integer>> findSubsequences(int[] nums) {
        //标记使用
        backtracking(nums, 0);
        return result;
    }
    public void backtracking(int[] nums, int startIdx){
        //标记单层的数字使用情况
        int[] used = new int[201];
        for (int i = startIdx; i < nums.length; i++) {
            //去重
            if (used[nums[i] + 100] == 1 || (!list.isEmpty() && nums[i] < list.get(list.size() - 1))) continue;
            //加入当前元素
            list.add(nums[i]);
            used[nums[i] + 100] = 1;
            //保留递归树中每个结点的值,即为每个子集
            if (list.size() > 1) result.add(new ArrayList<>(list));
            //递归i的下一个元素
            backtracking(nums, i + 1);
            //回溯
            list.remove(list.size() - 1);
        }
    }
}

46.全排列

题目链接

问题简述:获得无重复数组的所有排列。

思考:之前都是组合,每次递归都是从下一个位置startisx + 1开始,但这次是排列,虽然也是在叶子结点取到值,但不需要每次传入startidx了。

算法思路

  • 定义path存储每个结果集;lists存储path的集合。定义used数组来标记元素的访问情况。

  • 每次递归从先判断path中是否已经包含所有元素,如果是则加入lists。然后每次都遍历所有元素,如果元素被使用过,则跳过该元素;未被使用过则加入该元素,,并递归后进行回溯。

import java.util.ArrayList;
import java.util.List;

class Solution {
    //定义返回值
    List<List<Integer>> lists = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    int[] used;
    public List<List<Integer>> permute(int[] nums) {
        used = new int[nums.length];
        backtracking(nums);
        return lists;
    }
    //回溯函数
    public void backtracking(int[] nums){
        //path中元素个数和nums相等时保存path
        if (path.size() == nums.length){
            lists.add(new ArrayList<>(path));
          	return;
        }
        //每次都遍历所有结点,如果当前结点已经在path中则跳过
        for (int i = 0; i < nums.length; i++) {
            //如果当前结点已经被使用,说明其在path中则跳过
            if (used[i] == 1) continue;
            //将当前元素标记访问
            used[i] = 1;
            path.add(nums[i]);
            //递归
            backtracking(nums);
            //回溯
            path.remove(path.size() - 1);
            used[i] = 0;
        }
    }
}

47.全排列 II

题目链接

问题简述:求出有重复集合的所有排列。

思考:排列需要去重的问题。

算法思路

  • 定义path存储每个结果集;lists存储path的集合。定义used数组来标记元素的访问情况。

  • 先对数组进行排序。

  • 每次递归从先判断path中是否已经包含所有元素,如果是则加入lists。然后每次都遍历所有元素,如果元素被使用过,则跳过该元素;未被使用过则进去重,保证i大于0同时上一个元素未被使用(未在当前路径中)且当前元素与上一个元素相等,则跳过;否则加入该元素,并递归后进行回溯。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
    //定义返回值
    List<List<Integer>> lists = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    int[] used;
    public List<List<Integer>> permuteUnique(int[] nums) {
        Arrays.sort(nums);
        used = new int[nums.length];
        backtracking(nums);
        return lists;
    }
    //回溯函数
    public void backtracking(int[] nums){
        //path中元素个数和nums相等时保存path
        if (path.size() == nums.length){
            lists.add(new ArrayList<>(path));
            return;
        }
        //每次都遍历所有结点,如果当前结点已经在path中则跳过
        for (int i = 0; i < nums.length; i++) {
            //去重
            if (i > 0 && used[i - 1] == 0 && nums[i] == nums[i - 1]) continue;
            //如果当前结点已经被使用,说明其在path中则跳过
            if (used[i] == 1) continue;
            //将当前元素标记访问
            used[i] = 1;
            path.add(nums[i]);
            //递归
            backtracking(nums);
            //回溯
            path.remove(path.size() - 1);
            used[i] = 0;
        }
    }
}

感想

进行一定再完成一天的。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值