月度归档: 2021年7月

LeetCode刷题【543】二叉树的直径

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。

 

示例 :
给定二叉树

          1
         / \
        2   3
       / \     
      4   5    

返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。

 

注意:两结点之间的路径长度是以它们之间边的数目表示。

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

    求最长直径,且这个直径可能不经过root节点,看起来应该是深度搜索。先把深搜模板写出来看下下一步怎么办,定义一个length在不断深搜遍历过程中取到最大的直径的数值。

    int length = 0;
    
        public int diameterOfBinaryTree(TreeNode root) {
            if (root==null){
                return 0;
            }
            dfs(root);
            return length;
        }
    
        private void dfs(TreeNode node){
            if (node.left!=null){
                dfs(node.left);
            }
            if (node.right!=null){
                dfs(node.right);
            }
            return;
        }

    根据题意,当前节点的最大直径的路径为左边最大深度+当前节点+右边最大深度,所以在dfs算法中递归左子树和递归右子树的时候应当取到他们的对应子树的最大深度。那么这个时候改下dfs方法。

    private int dfs(TreeNode node){
            int leftHeight = 0;
            int rightHeight = 0;
            if (node.left!=null){
                leftHeight = dfs(node.left);
            }
            if (node.right!=null){
                rightHeight = dfs(node.right);
            }
            return Math.max(rightHeight,leftHeight)+1;
        }

    最后当然要return Math.max(rightHeight,leftHeight)+1;取到当前节点下的左子树和右子树中深度更深的那个子树的深度,再加上当前节点的深度+1;

    而在这个时候就可以发现,左子树右子树的深度有了,就可以得到经过当前节点的最大直径的长度了,顺便重命名下length为maxLength,只要拿已存的maxLength和当前的maxLength比较得到更大的值重新赋值给maxLength就可以了。

    private int dfs(TreeNode node){
            int leftHeight = 0;
            int rightHeight = 0;
            if (node.left!=null){
                leftHeight = dfs(node.left);
            }
            if (node.right!=null){
                rightHeight = dfs(node.right);
            }
            maxLength = Math.max(maxLength,leftHeight+rightHeight+1);
            return Math.max(rightHeight,leftHeight)+1;
        }

    跑一下用例

    			测试用例:[1,2,3,4,5]
    			测试结果:4
    			期望结果:3

    不大对劲,差在哪里呢?哦

    返回 3, 它的长度是路径 [4,2,1,3] 

    路径长度应该是节点数减1

    最终代码如下

    
    /**
     * 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 {
    
        int maxLength = 0;
    
        public int diameterOfBinaryTree(TreeNode root) {
            if (root==null){
                return 0;
            }
            dfs(root);
            return maxLength-1;
        }
    
        private int dfs(TreeNode node){
            int leftHeight = 0;
            int rightHeight = 0;
            if (node.left!=null){
                leftHeight = dfs(node.left);
            }
            if (node.right!=null){
                rightHeight = dfs(node.right);
            }
            maxLength = Math.max(maxLength,leftHeight+rightHeight+1);
            return Math.max(rightHeight,leftHeight)+1;
        }
    
    
    
    
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    LeetCode刷题【781】森林中的兔子

    森林中,每个兔子都有颜色。其中一些兔子(可能是全部)告诉你还有多少其他的兔子和自己有相同的颜色。我们将这些回答放在 answers 数组里。

    返回森林中兔子的最少数量。

    示例:
    输入: answers = [1, 1, 2]
    输出: 5
    解释:
    两只回答了 "1" 的兔子可能有相同的颜色,设为红色。
    之后回答了 "2" 的兔子不会是红色,否则他们的回答会相互矛盾。
    设回答了 "2" 的兔子为蓝色。
    此外,森林中还应有另外 2 只蓝色兔子的回答没有包含在数组中。
    因此森林中兔子的最少数量是 5: 3 只回答的和 2 只没有回答的。
    
    输入: answers = [10, 10, 10]
    输出: 11
    
    输入: answers = []
    输出: 0
    

    说明:

    1. answers 的长度最大为1000
    2. answers[i] 是在 [0, 999] 范围内的整数。
    Related Topics
  • 贪心
  • 哈希表
  • 数学
  • \n
  • 👍 189
  • 👎 0
  • 题解

    题中给出了 answers[i]的范围在0-999,所以我在声明numCount数组的时候多加了一位,用来保存最终结果,也可以单独声明一个变量。顺便存在一个数组里的作法只是图省个事而已。

    那么来分析一下,要求最小可能的数量,可以假定,当前n+1次遇到说和自己颜色一样的兔子还有n个的时候,都是同一个颜色的兔子。

    如果在遇到了n+1个兔子之后还遇到了说和自己颜色一样的兔子还有n个的时候,我们可以认为这只兔子的颜色和前面说n只的颜色是不一样的。那么就应该有另外n+1只后面这个颜色的兔子。

    定义一个numCount数组,key就是说还有n,当第一次遇到的时候,就给加上n+1只的结果,同时value值统计遇到了几只说n的兔子,如果统计有n+1只说了n的兔子之后,就置零,后面再遇到就是其他的颜色了

    public class LeetCode781 {
        public int numRabbits(int[] answers) {
            int[] numCount = new int[1001];
            for (int num : answers) {
                if(numCount[num] == 0){
                    //没有遇到过,加上下数量
                    //如果大于0,则说明已经遇到过不再重复计数
                    numCount[1000] += num+1;
                }
                //对应数量的+1
                numCount[num]++;
                if (numCount[num] == num+1){
                    //已经有N+1只兔子说了有N只和自己颜色一样
                    //这时候可以认为,这种颜色的最小可能数量已经达到了
                    //可以清空,后面再有说这个数量的可以认为是其他的颜色
                    numCount[num] = 0;
                }
            }
            return numCount[1000];
        }
    }
    

    LeetCode刷题【328】奇偶链表

    给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

    请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

    示例 1:

    输入: 1->2->3->4->5->NULL
    输出: 1->3->5->2->4->NULL
    

    示例 2:

    输入: 2->1->3->5->6->4->7->NULL 
    输出: 2->3->6->7->1->5->4->NULL

    说明:

    • 应当保持奇数节点和偶数节点的相对顺序。
    • 链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。
    Related Topics
  • 链表
  • \n
  • 👍 447
  • 👎 0
  • 题解

    
    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode() {}
     *     ListNode(int val) { this.val = val; }
     *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
     * }
     */
    class Solution {
        public ListNode oddEvenList(ListNode head) {
            if (head == null || head.next == null) return head;
            ListNode n = head;
            ListNode n2 = head.next;
            ListNode n2head = n2;
            while (true) {
                if (n2.next != null) {
                    n.next = n2.next;
                    n = n.next;
                    if (n.next != null) {
                        n2.next = n.next;
                        n2 = n2.next;
                    } else {
                        break;
                    }
                } else {
                    break;
                }
            }
            n2.next=null;
            n.next = n2head;
            return head;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    
            解答成功:
            执行耗时:0 ms,击败了100.00% 的Java用户
            内存消耗:38.2 MB,击败了18.05% 的Java用户

    借助n,n2构建奇偶链表,每次从head向后移动2位,第一个是奇数位,第二个是偶数位,判断好有没有下一个的情况。在不断迭代循环中,依次建立新的关联关系。

    同时因为n,n2是不断向后迭代遍历的,需要借助一个n2head来保存偶数位的链表头,最终有个n2.next=null;切断偶数位再链接回原来他后一位的奇数位的关联关系。再此时n为奇数位的末位,next指向之前创建的偶数位链表的首位。

    重新捋下代码,把while中的两个if条件提到while条件中,那么两个break分支就可以去掉了。这边可能会有疑虑,如果n.next!=null而n2.next=null的情况,会被错误的跳过,但是实际情况是这个分支场景会被下面的n.next = n2head覆盖替换掉,所以无需担心。同样的n2.next=null要处理的切断环链的操作会在while循环里被n2.next = n.next;处理掉。

    最终优化过的代码

    
    //leetcode submit region begin(Prohibit modification and deletion)
    
    
    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode() {}
     *     ListNode(int val) { this.val = val; }
     *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
     * }
     */
    class Solution {
        public ListNode oddEvenList(ListNode head) {
            if (head == null || head.next == null) return head;
            ListNode n = head;
            ListNode n2 = head.next;
            ListNode n2head = n2;
            while (n.next != null && n2.next != null) {
                    n.next = n2.next;
                    n = n.next;
                    n2.next = n.next;
                    n2 = n2.next;
            }
            n.next = n2head;
            return head;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    提交结果

            解答成功:
            执行耗时:0 ms,击败了100.00% 的Java用户
            内存消耗:37.8 MB,击败了93.06% 的Java用户

    LeetCode刷题【66】加一

    给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

    最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

    你可以假设除了整数 0 之外,这个整数不会以零开头。

     

    示例 1:

    输入:digits = [1,2,3]
    输出:[1,2,4]
    解释:输入数组表示数字 123。
    

    示例 2:

    输入:digits = [4,3,2,1]
    输出:[4,3,2,2]
    解释:输入数组表示数字 4321。
    

    示例 3:

    输入:digits = [0]
    输出:[1]
    

     

    提示:

    • 1 <= digits.length <= 100
    • 0 <= digits[i] <= 9
    Related Topics
  • 数组
  • 数学
  • \n
  • 👍 723
  • 👎 0
  • 题解

    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public int[] plusOne(int[] digits) {
            digits[digits.length-1] = digits[digits.length-1]+1;
            boolean will = false;
            for (int i = digits.length - 1; i >= 0; i--) {
                if (digits[i]>9){
                    will = true;
                    digits[i] = digits[i]-10;
                    if (i > 0){
                        digits[i-1] = digits[i-1]+1;
                    }
                }else{
                    will = false;
                }
            }
            if (will){
                int[] arr = new int[digits.length+1];
                System.arraycopy(digits, 0, arr, 1, digits.length);
                arr[0] = 1;
                return arr;
            }else{
                return digits;
            }
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    额,再看看大佬的题解

    class Solution {
        public int[] plusOne(int[] digits) {
            for (int i = digits.length - 1; i >= 0; i--) {
                digits[i]++;
                digits[i] = digits[i] % 10;
                if (digits[i] != 0) return digits;
            }
            digits = new int[digits.length + 1];
            digits[0] = 1;
            return digits;
        }
    }
    

    头皮发麻。。从后往前遍历,因为只加1,有变更如果不进位,则可以直接返回当前数组作为结果,如果要进位,则前一位继续加一。

    到了最后,如果每一位都加1了,说明是9,99,999这种情况,则构建一个新的比原来长以为的数组,首位赋值为1,后面的所有位数默认是0。

    符合结果。

    LeetCode刷题【268】丢失的数字

    给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

     

    进阶:

    • 你能否实现线性时间复杂度、仅使用额外常数空间的算法解决此问题?

     

    示例 1:

    输入:nums = [3,0,1]
    输出:2
    解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。

    示例 2:

    输入:nums = [0,1]
    输出:2
    解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2 是丢失的数字,因为它没有出现在 nums 中。

    示例 3:

    输入:nums = [9,6,4,2,3,5,7,0,1]
    输出:8
    解释:n = 9,因为有 9 个数字,所以所有的数字都在范围 [0,9] 内。8 是丢失的数字,因为它没有出现在 nums 中。

    示例 4:

    输入:nums = [0]
    输出:1
    解释:n = 1,因为有 1 个数字,所以所有的数字都在范围 [0,1] 内。1 是丢失的数字,因为它没有出现在 nums 中。

     

    提示:

    • n == nums.length
    • 1 <= n <= 104
    • 0 <= nums[i] <= n
    • nums 中的所有数字都 独一无二
    Related Topics
  • 位运算
  • 数组
  • 哈希表
  • 数学
  • 排序
  • \n
  • 👍 421
  • 👎 0
  • 题解

    方法很多,先列一个数学方法,根据题意,数字不重复,那么其实可以根据特性知道从0-n的合比0-n的丢失了其中一个数字x的合大x,所以两组数的求和相减即为所求数字。而考虑到大数相加可能溢出的问题,可以用一边加一边减的作法。

    代码如下

    
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public int missingNumber(int[] nums) {
            int count = nums.length+1;
            int tmp = 0;
            for (int i = 0; i < count; i++) {
                tmp -= i;
                if (i<count-1){
                    tmp += nums[i];
                }
            }
            return -tmp;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    而最常规的作法,其实应该是按顺序排列,再挨个遍历,找到丢失的那一个。

    其次借助hashMap映射关系的额外空间,找到丢失的数字,不过可以换个写法

    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public int missingNumber(int[] nums) {
            int[] arr = new int[nums.length+1];
            Arrays.fill(arr,-1);
            for (int num : nums) {
                arr[num] = num;
            }
            for (int i = 0; i < arr.length; i++) {
                if (arr[i]==-1){
                    return i;
                }
            }
            return -1;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    思路和hashmap一样,只是用int[] arr的数组下标代替的hashmap的key

    LeetCode刷题【1673】找出最具竞争力的子序列

    给你一个整数数组 nums 和一个正整数 k ,返回长度为 k 且最具 竞争力 nums 子序列。

    数组的子序列是从数组中删除一些元素(可能不删除元素)得到的序列。

    在子序列 a 和子序列 b 第一个不相同的位置上,如果 a 中的数字小于 b 中对应的数字,那么我们称子序列 a 比子序列 b(相同长度下)更具 竞争力 。 例如,[1,3,4][1,3,5] 更具竞争力,在第一个不相同的位置,也就是最后一个位置上, 4 小于 5

     

    示例 1:

    输入:nums = [3,5,2,6], k = 2
    输出:[2,6]
    解释:在所有可能的子序列集合 {[3,5], [3,2], [3,6], [5,2], [5,6], [2,6]} 中,[2,6] 最具竞争力。
    

    示例 2:

    输入:nums = [2,4,3,3,5,4,9,6], k = 4
    输出:[2,3,3,4]
    

     

    提示:

    • 1 <= nums.length <= 105
    • 0 <= nums[i] <= 109
    • 1 <= k <= nums.length
    Related Topics
  • 贪心
  • 数组
  • 单调栈
  • \n
  • 👍 61
  • 👎 0
  • 题解

    
    import java.util.Stack;
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public int[] mostCompetitive(int[] nums, int k) {
            Stack<Integer> stack = new Stack<>();
            for (int i = 0; i < nums.length; i++) {
                if (stack.isEmpty()){
                    stack.push(nums[i]);
                    continue;
                }
                while (!stack.isEmpty() && k-stack.size() < nums.length - i && stack.peek() > nums[i]){
                    stack.pop();
                }
                stack.push(nums[i]);
            }
            int[] res = new int[k];
            while (stack.size()>k){
                stack.pop();
            }
            while (!stack.isEmpty()){
                res[stack.size()-1] = stack.pop();
            }
            return res;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    LeetCode刷题【739】每日温度

    请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。

    示例 1:

    输入: temperatures = [73,74,75,71,69,72,76,73]
    输出: [1,1,4,2,1,1,0,0]
    

    示例 2:

    输入: temperatures = [30,40,50,60]
    输出: [1,1,1,0]
    

    示例 3:

    输入: temperatures = [30,60,90]
    输出: [1,1,0]

     

    提示:

    • 1 <= temperatures.length <= 105
    • 30 <= temperatures[i] <= 100
    Related Topics
  • 数组
  • 单调栈
  • \n
  • 👍 824
  • 👎 0
  • 题解

    一样的,最直接的办法,遍历循环往后探测到一个比当天天温度高的。

    而对于题中的描述,要求的是后面第一个比当前天气高的天数,换个说法,就是找后面的数据的单调递增栈的栈顶元素。

    思路就出来了。

    从后往前遍历,如果当前天的气温比栈定元素的气温高(或者等于),则持续对栈弹出操作。直到弹出到最后栈顶的天气是比当前天气高的。

    那么此时的栈顶元素就是从当天开始往后找到的第一个比当前天温度高的那一天。

    
    import java.util.Stack;
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
    
        private Stack<Integer> stack;
    
        public int[] dailyTemperatures(int[] temperatures) {
            stack = new Stack<>();
            int[] res = new int[temperatures.length];
            for (int i = temperatures.length-1; i >= 0; i--) {
                if (stack.isEmpty()){
                    stack.push(i);
                }else{
                    //把后面的比当前温度高的弹出去
                    while (!stack.isEmpty() && temperatures[i] >= temperatures[stack.peek()]){
                        stack.pop();
                    }
                    if (!stack.isEmpty()){
                        //此时栈顶的那个就是当前天数后面第一个比当前天温度高的
                        res[i] = stack.peek()-i;
                    }
                    stack.push(i);
                }
            }
            return res;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    这个思路对于我而言是比较显而易见的。其实也可以从前面开始正向遍历。具体思路和另外一题LeetCode刷题【503】下一个更大元素 II一致。

    正向遍历,存递减栈数据,判断,如果当前值小于等于栈顶值,直接加入,如果大于栈顶值,挨个从栈顶弹出比当前天的温度小的天,并给他们赋值,他们后面的第一个比他们的温度高是当前这一天。