当前位置: 首页> 房产> 建筑 > 代码随想录算法训练营第二十二天| 491. 递增子序列、46. 全排列、47. 全排列Ⅱ

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

时间:2025/7/14 7:43:59来源:https://blog.csdn.net/weixin_43831376/article/details/142062378 浏览次数:0次

今日内容

  • Leetcode. 491 递增子序列
  • Leetcode. 46 全排列
  • Leetcode. 47 全排列Ⅱ

Leetcode. 491 递增子序列

文章链接:代码随想录 (programmercarl.com)

题目链接:491. 非递减子序列 - 力扣(LeetCode)

本题也是一个子集问题,根据题意也知道要去重,提到去重就会不自觉地想到 90. 子集 II - 力扣(LeetCode)中的去重。但在本题中不能使用 Leetcode. 90 这种去重思想,因为这种去重需要对数组进行排序,而本题如果进行排序,会对得到的结果产生影响。

所以本题的去重应该用一个 Set 来记录本层已经使用过的元素。

根据上述内容,可写出代码:

class Solution {List<Integer> elem = new LinkedList<>();List<List<Integer>> result = new LinkedList<>();public List<List<Integer>> findSubsequences(int[] nums) {backtracking(nums, 0);return result;}public void backtracking(int[] nums, int startIndex){if (elem.size() >= 2){result.add(new LinkedList<>(elem));}if (startIndex == nums.length){return;}Set<Integer> used = new HashSet<>(); // 记录已使用元素的Setfor (int i = startIndex; i < nums.length; i++){if (!elem.isEmpty() && nums[i] < elem.get(elem.size() - 1) || used.contains(nums[i])){continue;}used.add(nums[i]);elem.add(nums[i]);backtracking(nums, i + 1);elem.remove(elem.size() - 1);}}
}
  • 时间复杂度:O(n * 2 ^ n)
  • 空间复杂度:O(n)

Leetcode. 46 全排列

文章链接:代码随想录 (programmercarl.com)

题目链接:46. 全排列 - 力扣(LeetCode)

本题是回溯问题中的排列问题, 排列问题和组合问题不同,排列问题返回得结果是有序的,比如{1, 2} {2, 1} 在排列问题中是两个不同的结果。

也正因为结果有序,所以和组合问题、分割问题以及子集问题不同,排列问题每次搜索都要从头开始,所以不需要startIndex指示下一次搜索开始的位置。

虽然排列问题不需要startIndex了,但是它返回的结果是有序的,元素的排列顺序依然需要其他变量进行标识。因此需要使用一个数组来记录每个元素的使用情况

本题中,我们使用一个布尔数组 used 来标识每个元素的使用情况,当本层遍历使用到该元素后,该元素在used数组中的值被赋为 true。在遍历元素时,就可以根据 used 数组中对应索引的值来判断是否被使用,被使用就跳过。

排列问题经过抽象得到的树形结构图如下所示:

根据上述内容,写出如下代码:

class Solution {List<Integer> elem = new LinkedList<>();List<List<Integer>> result = new LinkedList<>();public List<List<Integer>> permute(int[] nums) {boolean[] used = new boolean[nums.length]; // 记录元素的使用情况backtracking(nums, used);return result;}public void backtracking(int[] nums, boolean[] used){if (elem.size() == nums.length){result.add(new LinkedList<>(elem));return;}for (int i = 0; i < nums.length; i++){if (used[i]){continue;} // 若元素已使用过,则跳过该元素elem.add(nums[i]);used[i] = true; // 元素被使用,赋值为truebacktracking(nums, used);used[i] = false; // 回溯elem.remove(elem.size() - 1);  }}
}
  • 时间复杂度:O(n!)
  • 空间复杂度:O(n)

Leetcode. 47 全排列Ⅱ

文章链接:代码随想录 (programmercarl.com)

题目链接:47. 全排列 II - 力扣(LeetCode)

本题可以看作是 Leetcode. 46 + Leetcode. 90 的结合,也就是在全排列问题中加入去重。

代码如下:

class Solution {List<Integer> elem = new LinkedList<>();List<List<Integer>> result = new LinkedList<>();public List<List<Integer>> permuteUnique(int[] nums) {boolean[] used = new boolean[nums.length];Arrays.sort(nums); // 注意一定要排序backtracking(nums, used);return result;}public void backtracking(int[] nums, boolean[] used){if (elem.size() == nums.length){result.add(new LinkedList<>(elem));return;}for (int i = 0; i < nums.length; i++){if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == true){ // 判断元素是否被使用以及去重continue;}if (used[i]){continue;}elem.add(nums[i]);used[i] = true;backtracking(nums, used);used[i] = false;elem.remove(elem.size() - 1);}}
}
  • 时间复杂度:O(n! * n)
  • 空间复杂度:O(n)

总结

第一题是去重的不同,去重时注意所给数据是否有序,有序的话就可以使用 Leetcode. 90 的去重思想;无序的话就需要其他容器记录已使用元素。

排列问题要求结果有序,所以每次遍历都需要从头开始,并且需要一个容器记录哪些元素被使用过,注意这里的标识不是为了去重,而是为了调整结果顺序。

去重和判断元素使用情况的区别,从Leetcode. 47 就可以看出来了。

 

关键字:代码随想录算法训练营第二十二天| 491. 递增子序列、46. 全排列、47. 全排列Ⅱ

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: