作者: CheungQ

LeetCode刷题 【剑指 Offer 64】队列的最大值

1+2+...+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

 

示例 1:

输入: n = 3
输出: 6

示例 2:

输入: n = 9
输出: 45

 

限制:

  • 1 <= n <= 10000
Related Topics
  • 位运算
  • 递归
  • 脑筋急转弯
  • \n
  • 👍 347
  • 👎 0
  • 题解

    &&短路符号的特性,

    或者的话试试位运算的方法,二进制的相关东西太久远,忘了好多,暂时 不深入了

    主要思路还是用&&控制递归深度,因为这里不能用if,二元运算等方法来判断了

    联动下前面的斐波那契数列的计算,加上了这些限制条件之后,也让解法变得有趣很多

    在递归外定义一个变量,用控制递归次数的方法,依次为这个变量加运算

    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        int d = 0;
    
        public int sumNums(int n) {
            dep(n);
            return d;
        }
    
        public int dep(int n){
            d = d + n;
            boolean b = n>0 && dep(n-1)>0;
            return n;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    直接递归:

        //另外一个方法
        public int sumNums(int n) {
            boolean t = (n>0) && (n = n + sumNums(n-1))>0;
            return n;
        }

    LeetCode刷题【365】水壶问题

    有两个容量分别为 x升 和 y升 的水壶以及无限多的水。请判断能否通过使用这两个水壶,从而可以得到恰好 z升 的水?

    如果可以,最后请用以上水壶中的一或两个来盛放取得的 z升 水。

    你允许:

    • 装满任意一个水壶
    • 清空任意一个水壶
    • 从一个水壶向另外一个水壶倒水,直到装满或者倒空

    示例 1: (From the famous "Die Hard" example)

    输入: x = 3, y = 5, z = 4
    输出: True
    

    示例 2:

    输入: x = 2, y = 6, z = 5
    输出: False
    
    Related Topics
  • 深度优先搜索
  • 广度优先搜索
  • 数学
  • \n
  • 👍 301
  • 👎 0
  • 题解

    首先想到的是深搜,代码写完了开心提交,emmm,问题来了。有个用例过不去,提示超时,这个用例是

    104579, 104593, 12444

    1水壶容量104579,2水壶容量104593,目标水量12444

    自己在本地写了个test试了下,,提示栈溢出,修改-Xss参数多次尝试之后终于能跑过去了,这个时候的设置是-Xss102400k,加了个标记看了栈深度大概看了下,好家伙,直接跑到了362138。这只是个大概值,并不肯定十分准确。

    先贴下这个代码,思路应该还是比较明晰的。

    
    import org.junit.Test;
    
    import java.util.HashSet;
    
    /**
     * LeetCode365<br>
     * [在此填写类描述]
     *
     * @date 2021/7/14 19:54
     * @see [相关类/方法](可选)
     * @since [产品/模块版本] (可选)
     */
    public class LeetCode365 {
    
        @Test
        public void test(){
            System.out.println(canMeasureWater(104579, 104593, 12444));;
        }
    
    
        private HashSet<String> visited = new HashSet<>();
    
        private boolean ifCan = false;
    
        private  int target;
    
        private int jug1Max;
    
        private int jug2Max;
    
        private int depth = 0;
    
        public boolean canMeasureWater(int jug1Capacity, int jug2Capacity, int targetCapacity) {
            this.target = targetCapacity;
            this.jug1Max = jug1Capacity;
            this.jug2Max = jug2Capacity;
            if (jug1Max+jug2Max < target){
                return false;
            }
            tryNext( 0 ,0 ,1);
            System.out.println(depth);
            return ifCan;
        }
    
    
        private void tryNext( int jug1Current, int jug2Current, int count){
            count++;
            if (count > this.depth){
    
                this.depth = count;
            }
            if (ifCan)return;
            if (jug1Current == target || jug2Current == target || jug1Current + jug2Current == target){
                ifCan = true;
                return;
            }
            if (visited.contains(jug1Current+"_"+jug2Current)){
                return;
            }
            visited.add(jug1Current+"_"+jug2Current);
            //下一步几种情况
            //往1瓶子倒满水, 但是只有在1瓶子水没满的情况下可以
            if (jug1Current < jug1Max){
                tryNext(jug1Max,jug2Current,count);
            }
            //往2瓶子倒满水,但是只有在2瓶子水没有满的情况下可以
            if (jug2Current < jug2Max){
                tryNext(jug1Current,jug2Max,count);
            }
            if ( jug1Current > 0 && jug2Current>0){
                //把1瓶子的水倒掉,但是只在1瓶子里有水的情况可以,且2瓶子里也有水,如果2瓶子没水会回到0_0的状态
                tryNext(0,jug2Current,count);
                //把2瓶子的水倒掉,但是只在2瓶子里有水的情况可以,且1瓶子里也有水,如果1瓶子没水会回到0_0的状态
                tryNext(jug1Current,0,count);
            }
            //把一个瓶子里的水倒到另外一个瓶子里分情况
            //1.倒到满,倒出的瓶子还有剩余
            //2.倒完了,被倒入的瓶子还没满
            //中间正好满的情况都可以并在这两个里面,但是因为前面其实有两个瓶子各倒满一个的情况了,其实可以忽略
    
            //把1瓶子的水倒入2瓶子,余下的水留在1瓶子中,但是只有在2瓶子水没满,且1瓶子有水的情况下
            if (jug2Current < jug2Max && jug1Current > 0){
                if ( jug1Current + jug2Current >= jug2Max){
                    //如果倒满了还有剩余
                    tryNext(jug1Current-(jug2Max-jug2Current),jug2Max,count);
                }else {
                    //如果能全倒进去
                    tryNext(0,jug1Current+jug2Current,count);
                }
            }
            //把2瓶子的水倒入1瓶子,余下的水留在2瓶子中,但是只有在1瓶子水没满,且2瓶子有水的情况下
            if (jug1Current < jug1Max && jug2Current > 0){
                if ( jug1Current + jug2Current >= jug1Max){
                    //如果倒满了还有剩余
                    tryNext(jug1Max,jug2Current - (jug1Max-jug1Current),count);
                }else {
                    //如果能全倒进去
                    tryNext(jug1Current + jug2Current,0,count);
                }
            }
        }
    }

    既然好吧,换个思路看看了。把深度递归调用改成队列试试呢。

    
        private HashSet<String> visited = new HashSet<>();
    
        private  int target;
    
        private int jug1Max;
    
        private int jug2Max;
    
        private Queue<int[]> queue = new LinkedList<>();
    
        public boolean canMeasureWater(int jug1Capacity, int jug2Capacity, int targetCapacity) {
            this.target = targetCapacity;
            this.jug1Max = jug1Capacity;
            this.jug2Max = jug2Capacity;
            if (jug1Max+jug2Max < target){
                return false;
            }
            queue.add(new int[]{0, 0});
            while (!queue.isEmpty()){
                int[] arr = queue.poll();
                int jug1Current = arr[0];
                int jug2Current = arr[1];
                //1和2的当前状态已经拿到了,那么和之前以前判断当前的状态并得到下一个可能的状态
                //将下一个可能的状态放到队列尾部
                if (jug1Current == target || jug2Current == target || jug1Current + jug2Current == target){
                    return true;
                }
                visited.add(jug1Current+"_"+jug2Current);
                if (jug1Current < jug1Max){
                    addNext(jug1Max,jug2Current);
                }
                //往2瓶子倒满水,但是只有在2瓶子水没有满的情况下可以
                if (jug2Current < jug2Max){
                    addNext(jug1Current,jug2Max);
                }
                if ( jug1Current > 0 && jug2Current>0){
                    //把1瓶子的水倒掉,但是只在1瓶子里有水的情况可以,且2瓶子里也有水,如果2瓶子没水会回到0_0的状态
                    addNext(0,jug2Current);
                    //把2瓶子的水倒掉,但是只在2瓶子里有水的情况可以,且1瓶子里也有水,如果1瓶子没水会回到0_0的状态
                    addNext(jug1Current,0);
                }
                //把一个瓶子里的水倒到另外一个瓶子里分情况
                //1.倒到满,倒出的瓶子还有剩余
                //2.倒完了,被倒入的瓶子还没满
                //中间正好满的情况都可以并在这两个里面,但是因为前面其实有两个瓶子各倒满一个的情况了,其实可以忽略
    
                //把1瓶子的水倒入2瓶子,余下的水留在1瓶子中,但是只有在2瓶子水没满,且1瓶子有水的情况下
                if (jug2Current < jug2Max && jug1Current > 0){
                    if ( jug1Current + jug2Current >= jug2Max){
                        //如果倒满了还有剩余
                        addNext(jug1Current-(jug2Max-jug2Current),jug2Max);
                    }else {
                        //如果能全倒进去
                        addNext(0,jug1Current+jug2Current);
                    }
                }
                //把2瓶子的水倒入1瓶子,余下的水留在2瓶子中,但是只有在1瓶子水没满,且2瓶子有水的情况下
                if (jug1Current < jug1Max && jug2Current > 0){
                    if ( jug1Current + jug2Current >= jug1Max){
                        //如果倒满了还有剩余
                        addNext(jug1Max,jug2Current - (jug1Max-jug1Current));
                    }else {
                        //如果能全倒进去
                        addNext(jug1Current + jug2Current,0);
                    }
                }
            }
            return false;
        }
        
        private void addNext(int jug1Current,int jug2Current){
            if (visited.contains(jug1Current+"_"+jug2Current)){
                //已经有过,不添加
                return;
            }
            queue.add(new int[]{jug1Current,jug2Current});
        }

    写queue版本的时候原本把已访问的判断写在poll之后的,但是想了下,还是放在了添加进queue之前,这样也许可以减少点判断。

    if (visited.contains(jug1Current+"_"+jug2Current))

    如果是写在poll之后的话,就用CONTINUE中断,继续处理queue中的下一个。

    另外,写queue这个版本的时候,可以看到,大部分逻辑代码和之前的递归深搜的是基本一致的,区别仅仅在于,递归是调用自身,而queue这个是把下一个可能的状态加进队列,等待处理。是不是看到的BFS的影子,确实,本质上来说这两种方法就是DFS和BFS的区别。

    LeetCode刷题【43】字符串相乘

    给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

    示例 1:

    输入: num1 = "2", num2 = "3"
    输出: "6"

    示例 2:

    输入: num1 = "123", num2 = "456"
    输出: "56088"

    说明:

    1. num1 和 num2 的长度小于110。
    2. num1 和 num2 只包含数字 0-9
    3. num1 和 num2 均不以零开头,除非是数字 0 本身。
    4. 不能使用任何标准库的大数类型(比如 BigInteger)直接将输入转换为整数来处理
    Related Topics
  • 数学
  • 字符串
  • 模拟
  • \n
  • 👍 670
  • 👎 0
  • 题解

    按照基本的乘法计算规则,分别计算每一位即可

    
    
    import java.util.Arrays;
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public String multiply(String num1, String num2) {
            if ("0".equals(num1)||"0".equals(num2)){
                return "0";
            }
            if ("1".equals(num1)){
                return num2;
            }
            if ("1".equals(num2)){
                return num1;
            }
            int[] res = new int[num1.length() + num2.length()];
            Arrays.fill(res,0);
            for (int k1 = num1.length()-1; k1 >= 0 ; k1--) {
                int n1 = num1.charAt(k1) - '0';
                for (int k2 = num2.length()-1; k2 >=0 ; k2--) {
                    int n2 = num2.charAt(k2) - '0';
                    //开始往res上堆结果
                    int k = k1 + k2 + 1;
                    int tmp = res[k] + (n2 * n1);
                    if (tmp >= 10){
                        res[k] = tmp % 10;
                        res[k-1] = res[k-1] + tmp/10;
                    }else{
                        res[k] = tmp;
                    }
                }
            }
            StringBuffer stringBuffer = new StringBuffer();
            for (int i = 0; i < res.length; i++) {
                if (i==0 && res[i] == 0){
                    continue;
                }
                stringBuffer.append(res[i]);
            }
            return stringBuffer.toString();
    
    
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    补个小知识点Char转int

            System.out.println((int)("123".toCharArray()[0]));  //49
            System.out.println(("123".toCharArray()[0]-'0'));  //1
            System.out.println(("123".toCharArray()[0]+'0'));  //97
            System.out.println(('1'+'0'));  //97
            System.out.println((Character.getNumericValue("123".toCharArray()[0])));//1

    LeetCode刷题【93】复原IP地址

    给定一个只包含数字的字符串,用以表示一个 IP 地址,返回所有可能从 s 获得的 有效 IP 地址 。你可以按任何顺序返回答案。

    有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

    例如:”0.1.2.201″ 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245”、”192.168.1.312″ 和 “192.168@1.1” 是 无效 IP 地址。

     

    示例 1:

    输入:s = "25525511135"
    输出:["255.255.11.135","255.255.111.35"]
    

    示例 2:

    输入:s = "0000"
    输出:["0.0.0.0"]
    

    示例 3:

    输入:s = "1111"
    输出:["1.1.1.1"]
    

    示例 4:

    输入:s = "010010"
    输出:["0.10.0.10","0.100.1.0"]
    

    示例 5:

    输入:s = "101023"
    输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
    

     

    提示:

    • 0 <= s.length <= 3000
    • s 仅由数字组成
    Related Topics
  • 字符串
  • 回溯
  • \n
  • 👍 617
  • 👎 0
  • 题解

    回溯。具体思路就是,每次尝试截取1-3位字符,判断是否符合0-255之间的数值,这里单独写个方法判断截取出来的字符串是否符合ip中的数字的要求。不能是0开头的字符,但是可以是单独的一个字符“0”。

    另外,终止条件必须是凑齐了4个数字,且最后截取到了提供的字符串的最后一位

    if (strArr.size()==3 && lastIndex < s.length()){
                    continue;
                }

    这样的逻辑是,已经存了3个数字,且最后截取到了提供的字符的最后一位才会继续执行。这部分甚至可以继续深入优化下,比如已经存了2个数字,且剩余字符位数小于7位,因为每个数字可能为1-3位数。或者,已经存了1个数字,且剩余字符位数小于10位这样的逻辑。

    
    import java.util.ArrayList;
    import java.util.List;
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        private List<String> res = new ArrayList<>();
    
        public List<String> restoreIpAddresses(String s) {
            List<String> strArr = new ArrayList<>();
            dfs(strArr,s,0);
    
            return res;
        }
    
    
        private void dfs(List<String> strArr, String s, int startIndex){
            for (int i = 1; i <= 3; i++) {
                //尝试往后截取1-3位
                int lastIndex = startIndex+i;
                //如果当前递归收集到的数组中已经有三个,并且,后面要截取的字符没能截取到给定字符串的最后一位
                //结束当前长度的尝试,进入下一个更长长度的截取尝试
                if (strArr.size()==3 && lastIndex < s.length()){
                    continue;
                }
                if (lastIndex > s.length()){
                    //substring最后一次截取如果index超出了长度,会产生重复,且这个分支说明已经结束了
                    return;
                }
    
                String str = s.substring(startIndex,lastIndex);
                if (validateIpNum(str, strArr.size())){
                    strArr.add(str);
                    if (strArr.size()==4 && lastIndex == s.length()){
                        String newStr = String.join(".",strArr);
                        System.out.println(newStr);
                        res.add(newStr);
    
                    }
                    dfs(strArr,s,lastIndex);
                    strArr.remove(strArr.size()-1);
                }
    
            }
        }
    
        private boolean validateIpNum(String numStr,int numIndex){
            if (numStr.length()>1 && numStr.startsWith("0")){
                return false;
            }
            int num = Integer.parseInt(numStr);
            return num < 256;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    LeetCode刷题【135】分发糖果

    老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。

    你需要按照以下要求,帮助老师给这些孩子分发糖果:

    • 每个孩子至少分配到 1 个糖果。
    • 评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果。

    那么这样下来,老师至少需要准备多少颗糖果呢?

     

    示例 1:

    输入:[1,0,2]
    输出:5
    解释:你可以分别给这三个孩子分发 2、1、2 颗糖果。
    

    示例 2:

    输入:[1,2,2]
    输出:4
    解释:你可以分别给这三个孩子分发 1、2、1 颗糖果。
         第三个孩子只得到 1 颗糖果,这已满足上述两个条件。
    Related Topics
  • 贪心
  • 数组
  • \n
  • 👍 592
  • 👎 0
  • 题解

    有点水的困难题,从前到后,从后到前各来一遍就可以了

    
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public int candy(int[] ratings) {
            int[] resArr = new int[ratings.length];
            resArr[0] = 1;
            for (int i = 0; i < ratings.length-1; i++) {
                if (ratings[i+1] > ratings[i]){
                    resArr[i+1] = resArr[i]+1;
                }else{
                    resArr[i+1] = 1;
                }
            }
    
            int count = resArr[ratings.length-1];
            //这里的循环是最后一位开始往前,计算前一位的需要符合的值
            for (int k = ratings.length-1; k > 0; k--) {
                if (ratings[k-1] > ratings[k] && resArr[k-1] <= resArr[k]){
                    //ratings中的前一位比当前这位大,且结果中的前一位不比当前位置大
                    resArr[k-1] = resArr[k]+1;
                }
                //前一位的值已经确定
                count += resArr[k-1];
            }
            return count;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    LeetCode刷题【513】找树左下角的值

    给定一个二叉树,在树的最后一行找到最左边的值。

    示例 1:

    输入:
    
        2
       / \
      1   3
    
    输出:
    1
    

     

    示例 2:

    输入:
    
            1
           / \
          2   3
         /   / \
        4   5   6
           /
          7
    
    输出:
    7
    

     

    注意: 您可以假设树(即给定的根节点)不为 NULL

    Related Topics
  • 深度优先搜索
  • 广度优先搜索
  • 二叉树
  • \n
  • 👍 186
  • 👎 0
  • 题解

    BFS或者DFS

    两个解法都写了,BFS看到网友有个写法,先插入右子节点,再插入左子节点,这样最终queue的最后一个节点,必定是最深一层,最左侧的节点,这样的好处是,不用分层循环

    我的写法还是先插入左子节点,再插入右子节点,而每层循环的时候第一个peek到的值则是本层的最左侧节点

    分层循环的时候,踩了个小坑,之前没太注意

    //这是更正后的
    int size = queue.size();
    for (int i = 0; i < size; i++) {
        //poll逻辑
    }
    //更正前的
    for (int i = 0; i < queue.size(); i++)

    因为每次循环都会poll下,queue的size是会动态变化的,而每次循环的时候i值还在增加,会导致逻辑出错。

    深搜DFS的时候也是,先遍历左节点,这样,只有深度更深的时候才更新查到的节点,深度相同的时候,肯定先取到的是左节点。

    
    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode() {}
     *     TreeNode(int val) { this.val = val; }
     *     TreeNode(int val, TreeNode left, TreeNode right) {
     *         this.val = val;
     *         this.left = left;
     *         this.right = right;
     *     }
     * }
     */
    
    
    class Solution {
        public int findBottomLeftValue(TreeNode root) {
            dfs(root,0);
    
            bfs(root);
            return val;//theDeepNode.val
        }
        private int val;
    
    
        private TreeNode theDeepNode;
    
        private int depth = 0;
    
        private void dfs(TreeNode node, int currentDepth){
            if (null != node.left){
                dfs(node.left, currentDepth+1);
            }
            if (null != node.right){
                dfs(node.right, currentDepth+1);
            }
            if (node.left ==null && node.right == null){
                if (currentDepth > this.depth || this.theDeepNode == null){
                    theDeepNode =  node;
                    this.depth = currentDepth;
                }
            }
        }
    
        private Queue<TreeNode> queue = new LinkedList<>();
    
        private void bfs(TreeNode node){
            val = node.val;
            if (node.left!=null){
                queue.offer(node.left);
            }
            if (node.right!=null){
                queue.offer(node.right);
            }
            while (!queue.isEmpty()){
                val = queue.peek().val;
                int size = queue.size();
                for (int i = 0; i < size; i++) {
                    TreeNode currentNode = queue.poll();
                    if (currentNode.left!=null){
                        queue.offer(currentNode.left);
                    }
                    if (currentNode.right!=null){
                        queue.offer(currentNode.right);
                    }
                }
            }
        }
    
    //
    //    public class TreeNode {
    //        int val;
    //        TreeNode left;
    //        TreeNode right;
    //        TreeNode() {}
    //        TreeNode(int val) { this.val = val; }
    //        TreeNode(int val, TreeNode left, TreeNode right) {
    //            this.val = val;
    //            this.left = left;
    //            this.right = right;
    //        }
    //    }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    LeetCode刷题【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]]
    

    示例 3:

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

     

    提示:

    • 1 <= nums.length <= 6
    • -10 <= nums[i] <= 10
    • nums 中的所有整数 互不相同
    Related Topics
  • 数组
  • 回溯
  • \n
  • 👍 1441
  • 👎 0
  • 题解

    回溯,嗯

    
    import java.util.ArrayList;
    import java.util.List;
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
    
        private List<List<Integer>> list = new ArrayList<>();
    
        public List<List<Integer>> permute(int[] nums) {
            tryNext(new ArrayList<>(),nums);
            return list;
        }
    
    
        private void tryNext(List<Integer> ls, int[] nums){
            if (ls.size()== nums.length){
                List<Integer> l = new ArrayList<>();
                for (Integer integer : ls) {
                    l.add(integer);
                }
                list.add(l);
                return;
            }
            for (int num : nums) {
                if (ls.contains(num)){
                    continue;
                }
                ls.add(num);
                tryNext(ls,nums);
                ls.remove(ls.size()-1);
            }
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)