算法汇总

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];
            }
        }
    }
}
复制代码

转载于:https://juejin.im/post/5cf0d3b65188257ac6252045

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值