力扣39、40、131题。第二十七天打卡

今天的三道题非常有难度五颗星

39:组合总和

错了一次,对回溯算法的理解不够深刻没有想到自增这个过程发生的时间与影响。在此再做复习,所有的回溯算法都可以抽象为树形结构,向下和横向for循环控制的是横向,递归控制的是纵向。切记代码如下:

    List<List<Integer>> re = new ArrayList();
    List<Integer> path = new ArrayList();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        backTracking(candidates,target,0,0);
        return re;
    }
    public void backTracking(int[] candidates,int target,int sum,int startIndex){
        if(sum>target) return;
        if(sum==target){
            re.add(new ArrayList(path));
            return;
        }
        for(int i=startIndex;i<candidates.length;i++){
            sum += candidates[i];
            path.add(candidates[i]);
            backTracking(candidates,target,sum,i);
            sum -=candidates[i];
            path.remove(path.size()-1);
        }
        return;
    }

40题:组合总和 II

去重,它来了,本题用到了used数组来去重操作,在横向的也就是for循环控制的部分让之前没有被使用的相同元素直接跳过。纵向的不受影响。很吊代码如下:

    List<List<Integer>> re = new ArrayList();
    List<Integer> path = new ArrayList();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        boolean[] used = new boolean[candidates.length];
        backTracking(candidates,target,0,0,used);
        return re;
    }
    public void backTracking(int[] candidates, int target,int sum,int startIndex,boolean[] used){
        if(sum>target) return;
        if(sum==target){
            re.add(new ArrayList(path));
            return;
        }
        for(int i=startIndex;i<candidates.length;i++){
            if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false){continue;}
            sum+=candidates[i];
            path.add(candidates[i]);
            used[i]=true;
            backTracking(candidates,target,sum,i+1,used);
            sum-=candidates[i];
            path.remove(path.size()-1);
            used[i]=false;
        }
        return;
    }

131题:分割回文串

难点在于如何把它放到集合里,还是一样的逻辑。用横向和纵向来判定分割。其实就是遍历所有结果,但是有一个问题就是回溯的终止条件变了,变成了只要指针扫过了所有的集合。很吊,暂时没有理解的特别透彻代码如下:

    List<List<String>> lists = new ArrayList<>();
    Deque<String> deque = new LinkedList<>();

    public List<List<String>> partition(String s) {
        backTracking(s, 0);
        return lists;
    }

    private void backTracking(String s, int startIndex) {
        //如果起始位置大于s的大小,说明找到了一组分割方案
        if (startIndex >= s.length()) {
            lists.add(new ArrayList(deque));
            return;
        }
        for (int i = startIndex; i < s.length(); i++) {
            //如果是回文子串,则记录
            if (isPalindrome(s, startIndex, i)) {
                String str = s.substring(startIndex, i + 1);
                deque.addLast(str);
            } else {
                continue;
            }
            //起始位置后移,保证不重复
            backTracking(s, i + 1);
            deque.removeLast();
        }
    }
    //判断是否是回文串
    private boolean isPalindrome(String s, int startIndex, int end) {
        for (int i = startIndex, j = end; i < j; i++, j--) {
            if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
        }
        return true;
    }

力扣礼盒的最大密度问可以转化为一个函数 f(x) 的形式,其中 f(x) 表示 "礼盒中最大数不超过 x" 这个条件下的密度。 具体地,对于一个给定的数 x,我们可以使用贪心算法来判断在不超过 x 的情况下,最多能拿到多少的礼盒。假设当前已经拿到了 k 个礼盒,其数分别为 d1, d2, ..., dk,且满足 d1 <= d2 <= ... <= dk。此时,我们可以从剩余的礼盒中选择一个最小的数大于 dk 的礼盒,加入到已拿到的礼盒中,直到不能再加入礼盒为止。这个贪心算法的时间复杂度是 O(nlogn),其中 n 是礼盒的数量。 对于一个给定的数 x,如果能拿到的最多数不超过 x,则 f(x) 为 true,否则 f(x) 为 false。这个函数的曲线是一个阶梯状的函数,如下图所示: ``` | | | | | | | | | | |___|___|___|___ x1 x2 x3 x4 ``` 其中,每个竖直的线段表示一个礼盒,x1、x2、x3、x4 分别表示四个礼盒的最大数,每个水平的线段表示函数值为 true 的区间。例如,当 x 取值在 [x3, x4] 区间内时,f(x) 的值都为 true,因为在不超过 x3 的情况下,最多能拿到的数为 3+4+4=11,不超过 x4 的限制。 我们要找到的最大的密度,就是最后一个函数值为 true 的点所对应的 x 值,即 x4。这个问可以通过二分查找法解决,每次取中间值,判断中间值是否满足条件,然后不断缩小搜索区间,最终找到最大的 x 值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值