LeetCode刷题之路
回溯法
47.全排列II
题目
给定一个可包含重复数字的序列,返回所有不重复的全排列。
输入: [1,1,2] 输出: [ [1,1,2], [1,2,1], [2,1,1] ]
思路
本题的要求略有不同,体现在会有重复的数字,因此相比普通的全排列,难点主要在去重。 因此就想到尽量让相同的数字连在一起,方便去重处理,首先要对数组进行排序。使用一个大小相同的数组作为数字是否被使用的状态标记,1代表已使用,0代表未使用。如果已使用则进入下一步循环,如果当前下标大于0且前一个数字未被使用且和前一个数字相同,因为后面还有可能被用到,因此也要跳过。 内部循环的起点都是0,这里的i的含义是“剩余数组的起点”,保证不同的数字在每一个位置上都会出现一次。
代码
public static List<List<Integer>> permuteUnique(int[] nums) {
if(nums.length == 0) return new ArrayList<>();
Arrays.sort(nums);
List<List<Integer>> lists = new ArrayList<>();
int[] visited = new int[nums.length];
util( visited,nums, lists, new ArrayList<>());
return lists;
}
public static void util(int[] visited, int[] nums, List<List<Integer>> lists, List<Integer> list ) {
if (list.size() == nums.length) {
lists.add(new ArrayList<>(list));
return;
}
for(int i = 0; i < nums.length ; i++) {
if(visited[i] ==1 || (i > 0 && visited[i-1] == 0 && nums[i-1] == nums[i])) continue;
visited[i] = 1;
list.add(nums[i]);
util(visited,nums, lists, list);
list.remove(list.size() -1);
visited[i] = 0;
}
}
复制代码
526.优美排列
题目
假设有从 1 到 N 的 N 个整数,如果从这 N 个数字中成功构造出一个数组,使得数组的第 i 位 (1 <= i <= N) 满足如下两个条件中的一个,我们就称这个数组为一个优美的排列。条件:
第 i 位的数字能被 i 整除 i 能被第 i 位上的数字整除 现在给定一个整数 N,请问可以构造多少个优美的排列?
示例1:
输入: 2 输出: 2 解释:
第 1 个优美的排列是 [1, 2]: 第 1 个位置(i=1)上的数字是1,1能被 i(i=1)整除 第 2 个位置(i=2)上的数字是2,2能被 i(i=2)整除
第 2 个优美的排列是 [2, 1]: 第 1 个位置(i=1)上的数字是2,2能被 i(i=1)整除 第 2 个位置(i=2)上的数字是1,i(i=2)能被 1 整除 示例:
思路
思路还是和全排列一样,只是要加一个判断条件,判断是否满足最美条件。一开始在最后加了判断条件,结果正确但是超时了,所以在每次交换前就进行判断,避免后面不必要的递归。
代码
public static int countArrangement(int N) {
List<Integer> list = new ArrayList<>();
for(int i =0; i < N ; i++) list.add(i+1);
List<List<Integer>> lists = new ArrayList<>();
sort(N, 0, lists, list);
return lists.size();
}
public static void sort(int n, int i, List<List<Integer>> lists, List<Integer> list ) {
if(i == n ) lists.add(list);
for(int j = i; j < n; j ++) {
if(list.get(j) % (i + 1) == 0 || (i + 1) % list.get(j) == 0){
Collections.swap(list, i, j);
sort(n, i+1, lists, list);
Collections.swap(list, i, j);
}
}
}
复制代码
72.子集II
题目
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2] 输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]
思路
与子集1相比,和全排列II类似,难点主要是在有重复的数字,按之前的思路会出现[1,2],[2,1]的组合,在此题中属于重复情况,因此第一步处理仍然是进行排序,为去重做准备。 去重主要体现在两个地方,1.内部循环的起点不再是从0 -> n-1,而是i+1,递归起点i=0,这样能保证每个数字只会被用做一次起点,由于是子集,每次循环都要添加当前数组到结果中。
代码
public static List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> lists = new ArrayList<>();
List<Integer> tem = new ArrayList<>();
int[] visited = new int[nums.length];
util(lists, tem, nums, 0,visited);//递归入口
lists.add(new ArrayList<>());
for(int a : nums) tem.add(a);
return lists;
}
public static void util( List<List<Integer>> lists, List<Integer> tem, int[] nums, int i, int[] visited) {
if(tem.size() == nums.length) return;
for(int j = i; j < nums.length; j++) {
if(j > i && nums[j] == nums[j - 1]) continue;
tem.add(nums[j]);
lists.add(new ArrayList<>(tem));
util(lists, tem, nums, j+1,visited);
tem.remove(tem.size()-1);
}
}
复制代码
双指针
287.寻找重复数
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2] 输出: 2 示例 2:
输入: [3,1,3,4,2] 输出: 3 说明:
不能更改原数组(假设数组是只读的)。 只能使用额外的 O(1) 的空间。 时间复杂度小于 O(n2) 。 数组中只有一个重复的数字,但它可能不止重复出现一次。
思路
快慢指针思想, fast 和 slow 是指针, nums[slow] 表示取指针对应的元素 注意 nums 数组中的数字都是在 1 到 n 之间的(在数组中进行游走不会越界), 因为有重复数字的出现, 所以这个游走必然是成环的, 环的入口就是重复的元素, 即按照寻找链表环入口的思路来做
代码
class Solution {
public int findDuplicate(int[] nums) {
int slow = 0;
int fast = 0;
while(true) {
slow = nums[slow];
fast = nums[nums[fast]];
if(slow == fast) {
fast = 0;
while (nums[slow] != nums[fast]) {
slow = nums[slow];
fast = nums[fast];
}
return nums[slow];
}
}
}
}
复制代码