月度归档: 2022年6月

LeetCode刷题【208】实现 Trie (前缀树)

Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。

请你实现 Trie 类:

  • Trie() 初始化前缀树对象。
  • void insert(String word) 向前缀树中插入字符串 word
  • boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false
  • boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false

 

示例:

输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]

解释
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple");   // 返回 True
trie.search("app");     // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app");     // 返回 True

 

提示:

  • 1 <= word.length, prefix.length <= 2000
  • wordprefix 仅由小写英文字母组成
  • insertsearchstartsWith 调用次数 总计 不超过 3 * 104
Related Topics
  • 设计
  • 字典树
  • 哈希表
  • 字符串

  • 👍 1209
  • 👎 0
  • 字典树基本模板操作

    class Trie {
    
        Node root = new Node();
    
        public Trie() {
    
        }
        
        public void insert(String word) {
            int idx = -1;
            Node cur = root;
            while (++idx < word.length()){
                if (cur.children[word.charAt(idx)-'a'] == null){
                    cur.children[word.charAt(idx)-'a'] = new Node();
                }
                cur = cur.children[word.charAt(idx)-'a'];
            }
            cur.flag = true;
        }
        
        public boolean search(String word) {
            int idx = -1;
            Node cur = root;
            while (++idx < word.length()){
                if (cur.children[word.charAt(idx)-'a'] != null){
                    cur = cur.children[word.charAt(idx)-'a'];
                }else{
                    return false;
                }
            }
            return cur.flag;
        }
        
        public boolean startsWith(String prefix) {
            int idx = -1;
            Node cur = root;
            while (++idx < prefix.length()){
                if (cur.children[prefix.charAt(idx)-'a'] != null){
                    cur = cur.children[prefix.charAt(idx)-'a'];
                }else{
                    return false;
                }
            }
            return true;
        }
    
    
        class Node{
            boolean flag = false;
            Node[] children = new Node[26];
            public Node(){}
        }
    }

    LeetCode刷题【563】二叉树的坡度

    给你一个二叉树的根节点 root ,计算并返回 整个树 的坡度 。

    一个树的 节点的坡度 定义即为,该节点左子树的节点之和和右子树节点之和的 差的绝对值 。如果没有左子树的话,左子树的节点之和为 0 ;没有右子树的话也是一样。空结点的坡度是 0 。

    整个树 的坡度就是其所有节点的坡度之和。

     

    示例 1:

    输入:root = [1,2,3]
    输出:1
    解释:
    节点 2 的坡度:|0-0| = 0(没有子节点)
    节点 3 的坡度:|0-0| = 0(没有子节点)
    节点 1 的坡度:|2-3| = 1(左子树就是左子节点,所以和是 2 ;右子树就是右子节点,所以和是 3 )
    坡度总和:0 + 0 + 1 = 1
    

    示例 2:

    输入:root = [4,2,9,3,5,null,7]
    输出:15
    解释:
    节点 3 的坡度:|0-0| = 0(没有子节点)
    节点 5 的坡度:|0-0| = 0(没有子节点)
    节点 7 的坡度:|0-0| = 0(没有子节点)
    节点 2 的坡度:|3-5| = 2(左子树就是左子节点,所以和是 3 ;右子树就是右子节点,所以和是 5 )
    节点 9 的坡度:|0-7| = 7(没有左子树,所以和是 0 ;右子树正好是右子节点,所以和是 7 )
    节点 4 的坡度:|(3+5+2)-(9+7)| = |10-16| = 6(左子树值为 3、5 和 2 ,和是 10 ;右子树值为 9 和 7 ,和是 16 )
    坡度总和:0 + 0 + 0 + 2 + 7 + 6 = 15
    

    示例 3:

    输入:root = [21,7,14,1,1,2,2,3,3]
    输出:9
    

     

    提示:

    • 树中节点数目的范围在 [0, 104]
    • -1000 <= Node.val <= 1000
    Related Topics
  • 深度优先搜索
  • 二叉树

  • 👍 270
  • 👎 0
  • 深搜套模板嘞

    就原来的基础模板里夹两个东西

    1. dfs方法返回 当前节点下所有节点的值的和 = 当前节点的值 + 左子树的和 + 右子树的和
    2. dfs过程中 累加一个sum值,等于左右子树与右子树节点和的 差值绝对值
    class Solution {
        int sum = 0;
    
        public int findTilt(TreeNode root) {
            if (null == root){
                return 0;
            }
            dfs(root);
            return sum;
    
        }
    
        public int dfs(TreeNode node){
            if (null == node){
                return 0;
            }
            int left = dfs(node.left);
            int right = dfs(node.right);
            sum += left > right? left - right : right - left;
            return node.val + left + right;
        }
    }

    LeetCode刷题【剑指 Offer 11】旋转数组的最小数字

    把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

    给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2][1,2,3,4,5] 的一次旋转,该数组的最小值为 1。  

    注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

     

    示例 1:

    输入:numbers = [3,4,5,1,2]
    输出:1
    

    示例 2:

    输入:numbers = [2,2,2,0,1]
    输出:0
    

     

    提示:

    • n == numbers.length
    • 1 <= n <= 5000
    • -5000 <= numbers[i] <= 5000
    • numbers 原来是一个升序排序的数组,并进行了 1n 次旋转

    注意:本题与主站 154 题相同:https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array-ii/

    Related Topics
  • 数组
  • 二分查找

  • 👍 648
  • 👎 0
  • java 二分

    public int minArray(int[] nums) {
            int l = 0;
            int r = nums.length-1;
            while ( l < r){
                int mid = l + ( (r-l) >> 1 );
                if (nums[mid] < nums[r]){
                    r = mid;
                }else if (nums[mid] > nums[r]){
                    l = mid+1;
                }else{
                    r--;
                }
    
    
            }
    
            return nums[l];
    
        }

    LeetCode刷题【剑指 Offer 42】连续子数组的最大和

    输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

    要求时间复杂度为O(n)。

     

    示例1:

    输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
    输出: 6
    解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

     

    提示:

    • 1 <= arr.length <= 10^5
    • -100 <= arr[i] <= 100

    注意:本题与主站 53 题相同:https://leetcode-cn.com/problems/maximum-subarray/

     

    Related Topics
  • 数组
  • 分治
  • 动态规划

  • 👍 548
  • 👎 0
  • 动态规划,简单理解DP数组的意义和转移方程来源

    class Solution {
    
        public int maxSubArray(int[] nums) {
            int[] dp = new int[nums.length];
            int max = nums[0];
            dp[0] = nums[0];
            int idx = 0;
            while (++idx < nums.length){
                dp[idx] = Math.max(nums[idx],dp[idx-1]+nums[idx]);
                max = Math.max(max,dp[idx]);
            }
            return max;
        }
    }
    

    直接看代码说话,动态规划需要明白的最重要的就是你的dp[]数组表达的意义是什么?

    这里我们的int[] dp 需要表达的意义是

    以原 int[] nums 数组中的每一位结尾的子序列能组成的最大子序列合

    这很重要,当求dp[i]的时候,我们有两种情况

    1. 和前一位置的数字结尾的子序列组成新的子序列
    2. 单独自己为一个序列

    这两个来源对应的值分别是 dp[i-1] 和 nums[i]

    要求子序列合最大,那么我们就可以得到以下转移方程

    dp[i] = MAX ( nums[i] , dp[i-1]+nums[i] )

    或者也可以这样

    dp[i] = MAX ( 0 , dp[i-1] ) + nums[i]

    都一样的,代码就是上面那样了,当然也可以再简化的用一个变量代替下dp[]数组来操作

    class Solution {
        public int maxSubArray(int[] nums) {
            int max = nums[0];
            int pre = nums[0];
            int idx = 0;
            while (++idx < nums.length){
                pre = Math.max(0 ,pre) + nums[idx];
                max = Math.max(max,pre);
            }
            return max;
        }
    }
    

    emmm,测试用例的问题么?单个变量的内存消耗甚至比数组大

    LeetCode刷题【剑指 Offer 47】礼物的最大价值

    在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

     

    示例 1:

    输入: 
    [
      [1,3,1],
      [1,5,1],
      [4,2,1]
    ]
    输出: 12
    解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物

     

    提示:

    • 0 < grid.length <= 200
    • 0 < grid[0].length <= 200
    Related Topics
  • 数组
  • 动态规划
  • 矩阵

  • 👍 305
  • 👎 0
  • 动态规划简单题,再压缩到一维dp[]数组

    1. 当前格子可以由上面一格 或者 左边一格 过来,取较大的一个
    2. 再加上当前格子的价值,就是当前格子能得到的最大价值
    3. 转移方程
      dp[i][j] = MAX( dp[i][j-1], dp[i-1][j]) + grid[i][j]

    在直接压缩到一维dp[]数组如下

    class Solution {
        public int maxValue(int[][] grid) {
            int[] dp = new int[grid[0].length];
            dp[0] = grid[0][0];
            for (int i = 1; i < grid[0].length; i++) {
                dp[i] = dp[i-1] + grid[0][i];
            }
            int idx = 0;
            while (++idx < grid.length){
                dp[0] += grid[idx][0];
                for (int i = 1; i < grid[0].length; i++) {
                    dp[i] = Math.max(dp[i-1],dp[i]) + grid[idx][i];
                }
            }
            return dp[dp.length-1];
        }
    }

    LeetCode刷题【剑指 Offer 21】调整数组顺序使奇数位于偶数前面

    输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。

     

    示例:

    输入:nums = [1,2,3,4]
    输出:[1,3,2,4] 
    注:[3,1,2,4] 也是正确的答案之一。

     

    提示:

    1. 0 <= nums.length <= 50000
    2. 0 <= nums[i] <= 10000
    Related Topics
  • 数组
  • 双指针
  • 排序

  • 👍 233
  • 👎 0
  • 双指针原地交换

    双指针

    1. 一个找偶数数字
    2. 一个往后找奇数数字
    3. 需要判断下cur = Math.max(i,cur);,因为当找偶数数字的指针往后移动的时候,找奇数的指针可能留在原地,这样再找奇数的时候会找到偶数位前面的奇数
    4. swap()方法常规操作
    class Solution {
        public int[] exchange(int[] nums) {
            if (nums.length < 2){
                return nums;
            }
            int cur = 1;
            for (int i = 0; cur < nums.length && i < nums.length ; i++) {
                if (nums[i] % 2 == 0){
                    cur = Math.max(i,cur);
                    while (cur < nums.length && nums[cur] % 2 == 0){
                        cur++;
                    }
                    swap(nums,i,cur);
                }
            }
            return nums;
    
        }
    
        private void swap(int[] nums, int x, int y){
            if (y >= nums.length){
                return;
            }
            int tmp = nums[x];
            nums[x] = nums[y];
            nums[y] = tmp;
        }
    }

    LeetCode刷题【318】最大单词长度乘积

    给你一个字符串数组 words ,找出并返回 length(words[i]) * length(words[j]) 的最大值,并且这两个单词不含有公共字母。如果不存在这样的两个单词,返回 0

     

    示例 1:

    输入:words = ["abcw","baz","foo","bar","xtfn","abcdef"]
    输出:16 
    解释这两个单词为 "abcw", "xtfn"

    示例 2:

    输入:words = ["a","ab","abc","d","cd","bcd","abcd"]
    输出:4 
    解释这两个单词为 "ab", "cd"

    示例 3:

    输入:words = ["a","aa","aaa","aaaa"]
    输出:0 
    解释不存在这样的两个单词。
    

     

    提示:

    • 2 <= words.length <= 1000
    • 1 <= words[i].length <= 1000
    • words[i] 仅包含小写字母
    Related Topics
  • 位运算
  • 数组
  • 字符串

  • 👍 364
  • 👎 0
  • 位运算->哈希,位运算->重复判断

    抄大佬作业,这个我是真想不来

    class Solution {
        public int maxProduct(String[] words) {
            int max = 0;
            int[] arr = new int[words.length];
            for (int i = 0; i < words.length; i++) {
                int h = 0;
                for (int c = 0; c < words[i].length(); c++) {
                    h |= 1 << (words[i].charAt(c) - 'a');
                }
                arr[i] = h;
            }
            for (int i = 0; i < words.length; i++) {
                for (int j = i+1; j < words.length; j++) {
                    if ((arr[i] & arr[j]) != 0){
                        continue;
                    }
                    max = Math.max(max,words[i].length() * words[j].length());
                }
            }
            return max;
        }
    }