leetCode 21-25

39- 组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target
的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。 解集不能包含重复的组合。

示例 1:

输入:candidates = [2,3,6,7], target = 7, 所求解集为: [ [7], [2,2,3] ]

回溯法::处理结束条件,可以套模板

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        int len = candidates.length;
        List<List<Integer>> res = new ArrayList<>();
        if(len==0) return res;

        Deque<Integer> path = new LinkedList<>();
        dfs(candidates,0,len,target,path,res);
        return res;
    }
    void dfs(int[] candidates, int begin,int len,int target, Deque<Integer> path, List<List<Integer>> res){
        if(target<0) return;
        if(target==0){
            res.add(new LinkedList<>(path));
            return;
        }
        //从begin开始搜索,begin前的数已经被遍历过了
        for(int i=begin;i<len;i++){
            path.addLast(candidates[i]);
            System.out.println("递归之前 => " + path + ",剩余 = " + (target - candidates[i]));
            //每一个元素都可以使用因此,begin都是从i开始,递进从在for循环
            dfs(candidates,i,len,target-candidates[i],path,res);
            //删除添加元素,回到上一个状态,继续向下遍历
            path.removeLast();
            System.out.println("递归之后 => " + path);
        }
    }
}

打印是这个样子:

递归之前 => [2],剩余 = 5
递归之前 => [2, 2],剩余 = 3
递归之前 => [2, 2, 2],剩余 = 1
递归之前 => [2, 2, 2, 2],剩余 = -1
递归之后 => [2, 2, 2]
递归之前 => [2, 2, 2, 3],剩余 = -2
递归之后 => [2, 2, 2]
递归之前 => [2, 2, 2, 6],剩余 = -5
递归之后 => [2, 2, 2]
递归之前 => [2, 2, 2, 7],剩余 = -6
递归之后 => [2, 2, 2]
递归之后 => [2, 2]
递归之前 => [2, 2, 3],剩余 = 0
递归之后 => [2, 2]
递归之前 => [2, 2, 6],剩余 = -3
递归之后 => [2, 2]
递归之前 => [2, 2, 7],剩余 = -4
递归之后 => [2, 2]
递归之后 => [2]
递归之前 => [2, 3],剩余 = 2
递归之前 => [2, 3, 3],剩余 = -1
递归之后 => [2, 3]
递归之前 => [2, 3, 6],剩余 = -4
递归之后 => [2, 3]
递归之前 => [2, 3, 7],剩余 = -5
递归之后 => [2, 3]
递归之后 => [2]
递归之前 => [2, 6],剩余 = -1
递归之后 => [2]
递归之前 => [2, 7],剩余 = -2
递归之后 => [2]
递归之后 => []
递归之前 => [3],剩余 = 4
递归之前 => [3, 3],剩余 = 1
递归之前 => [3, 3, 3],剩余 = -2
递归之后 => [3, 3]
递归之前 => [3, 3, 6],剩余 = -5
递归之后 => [3, 3]
递归之前 => [3, 3, 7],剩余 = -6
递归之后 => [3, 3]
递归之后 => [3]
递归之前 => [3, 6],剩余 = -2
递归之后 => [3]
递归之前 => [3, 7],剩余 = -3
递归之后 => [3]
递归之后 => []
递归之前 => [6],剩余 = 1
递归之前 => [6, 6],剩余 = -5
递归之后 => [6]
递归之前 => [6, 7],剩余 = -6
递归之后 => [6]
递归之后 => []
递归之前 => [7],剩余 = 0
递归之后 => []
输出 => [[2, 2, 3], [7]]

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/combination-sum/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-m-2/

优化剪枝操作:
剪枝也就是在target小于0之后,就break就不再继续执行本次的for循环。(前提是序列有序)
参考link

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

public class Solution {

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        int len = candidates.length;
        List<List<Integer>> res = new ArrayList<>();
        if (len == 0) {
            return res;
        }

        // 排序是剪枝的前提
        Arrays.sort(candidates);
        Deque<Integer> path = new ArrayDeque<>();
        dfs(candidates, 0, len, target, path, res);
        return res;
    }

    private void dfs(int[] candidates, int begin, int len, int target, Deque<Integer> path, List<List<Integer>> res) {
        // 由于进入更深层的时候,小于 0 的部分被剪枝,因此递归终止条件值只判断等于 0 的情况
        if (target == 0) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = begin; i < len; i++) {
            // 重点理解这里剪枝,前提是候选数组已经有序,
            if (target - candidates[i] < 0) {
                break;
            }
            
            path.addLast(candidates[i]);
            dfs(candidates, i, len, target - candidates[i], path, res);
            path.removeLast();
        }
    }
}

42-接雨水

class Solution {
    public int trap(int[] height) {
         Stack<Integer> stack = new Stack<Integer>();
         
         int water = 0;       
         //特殊情况       
         if(height.length <3){
             return 0;
         }       
         for(int i = 0; i < height.length; i++){
             while(!stack.isEmpty() && height[i] > height[stack.peek()]){          
                 //栈顶元素            
                 int popnum = stack.pop();           
                 //相同元素的情况例1,1          
                 while(!stack.isEmpty()&&height[popnum] == height[stack.peek()]){
                     stack.pop();
                 }           
                 //计算该层的水的单位           
                 if(!stack.isEmpty()){
                     int temp = height[stack.peek()];//栈顶元素值              
                     //高                     
                     int hig = Math.min(temp-height[popnum],height[i]-height[popnum]);                    
                     //宽                   
                     int wid = i-stack.peek()-1;
                     water +=hig * wid;
                 }

             }             
             //这里入栈的是索引          
             stack.push(i);
         }
         return water;
    }
}

46 - 数字全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:

输入:nums = [0,1] 输出:[[0,1],[1,0]]

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        int len = nums.length;
        List<List<Integer>> res =  new ArrayList<>();
        if(len==0) return res;
        //用来标记数字是否已经被使用过。。如果使用符号标记是否使用就要使用交换法,
        //每次交换位置
        boolean[] used = new boolean[len];
        Deque<Integer> path = new LinkedList<>();
        dfs(nums,len,0,path,used,res);
        return res;
    }
    void dfs(int[] nums, int len,int depth,Deque<Integer> path, boolean[] used,List<List<Integer>> res){
        if(depth==len){
            res.add(new LinkedList<>(path));
            return;
        }
        for(int i=0;i<len;i++){
            if(!used[i]){
                path.addLast(nums[i]);
                used[i]=true;

                dfs(nums,len,depth+1,path,used,res);

                //取消选择
                used[i] = false;
                path.removeLast();
            }
        }
    }
}

48-矩阵旋转90度

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

link
在这里插入图片描述

class Solution {
    public void rotate(int[][] matrix) {
        int len = matrix.length;
        if(len<1) return;
        int temp;
        //先水平翻转
        for(int i=0;i<len/2;i++){
            for(int j=0;j<len;j++){
                temp = matrix[i][j];
                matrix[i][j] = matrix[len-1-i][j];
                matrix[len-1-i][j] = temp;
            }
        }
        //再转置即可得到答案
        for(int i=0;i<len;i++){
            //转置转换一个三角即可-->j=i
            for(int j=i;j<len;j++){
                temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp; 
            }
        }
    }
}

49-字母异位词分组

给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

示例:

输入: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”] 输出: [
[“ate”,“eat”,“tea”], [“nat”,“tan”], [“bat”] ] 说明:

所有输入均为小写字母。 不考虑答案输出的顺序。

使用每个字符中字母出现的频次和对应字母组合作为key,然后统计

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String,List<String>> map = new HashMap<>();
        for(String str:strs){
            //记录字符中每个字母出现的次数
            int[] counts = new int[26];
            int len = str.length();
            for(int i=0;i<len;i++){
                //如a的ascii是65,则a出现的次数存储在counts[0]
                counts[str.charAt(i)-'a']++;
            }
            //将每个出现次数大于 0 的字母和出现次数按顺序拼接成字符串,作为哈希表的键
            //如[a b b c a] 编码之后是a2b2c1
            StringBuilder sb=new StringBuilder();
            for(int i = 0;i<26;i++){
                if(counts[i]!=0){
                    //保存字母
                    sb.append((char)('a'+i));
                    //保存频次
                    sb.append(counts[i]);
                }
            }
            String key = sb.toString();
            //返回key对应的List,然后添加。不存在则new ArrayList<String>
            List<String> list = map.getOrDefault(key,new ArrayList<String>());
            list.add(str);
            map.put(key,list);
        }
        return new ArrayList<List<String>>(map.values());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值