月度归档: 2021年8月

LeetCode刷题【981】基于时间的键值存储

设计一个基于时间的键值数据结构,该结构可以在不同时间戳存储对应同一个键的多个值,并针对特定时间戳检索键对应的值。

实现 TimeMap 类:

  • TimeMap() 初始化数据结构对象
  • void set(String key, String value, int timestamp) 存储键 key、值 value,以及给定的时间戳 timestamp
  • String get(String key, int timestamp)
    • 返回先前调用 set(key, value, timestamp_prev) 所存储的值,其中 timestamp_prev <= timestamp
    • 如果有多个这样的值,则返回对应最大的  timestamp_prev 的那个值。
    • 如果没有值,则返回空字符串("")。
 

示例:

输入:
["TimeMap", "set", "get", "get", "set", "get", "get"]
[[], ["foo", "bar", 1], ["foo", 1], ["foo", 3], ["foo", "bar2", 4], ["foo", 4], ["foo", 5]]
输出:
[null, null, "bar", "bar", null, "bar2", "bar2"]

解释:
TimeMap timeMap = new TimeMap();
timeMap.set("foo", "bar", 1);  // 存储键 "foo" 和值 "bar" ,时间戳 timestamp = 1   
timeMap.get("foo", 1);         // 返回 "bar"
timeMap.get("foo", 3);         // 返回 "bar", 因为在时间戳 3 和时间戳 2 处没有对应 "foo" 的值,所以唯一的值位于时间戳 1 处(即 "bar") 。
timeMap.set("foo", "bar2", 4); // 存储键 "foo" 和值 "bar2" ,时间戳 timestamp = 4  
timeMap.get("foo", 4);         // 返回 "bar2"
timeMap.get("foo", 5);         // 返回 "bar2"

 

提示:

  • 1 <= key.length, value.length <= 100
  • keyvalue 由小写英文字母和数字组成
  • 1 <= timestamp <= 107
  • set 操作中的时间戳 timestamp 都是严格递增的
  • 最多调用 setget 操作 2 * 105
Related Topics
  • 设计
  • 哈希表
  • 字符串
  • 二分查找
  • \n
  • 👍 135
  • 👎 0
  • 题解

    HashMap配合List的二分查找来找到指定的值。当然也可以用HashMap配合BST二叉搜索树来实现,或者更进一步AVL树,树的搜索表现应当比二分法来得更好些。

    不过其实我对这题有点点疑问

    1. 看了下timestamp一定是递增的嘛?感觉不应该是恒定递增的,常见的在业务中,并发条件下时间12345和时间12346发起的两个请求,因为网络或者硬件等因素可能是12346的请求先到服务器的,并不能预先假设这个时间一定是递增的。
    2. 题目中要求“如果有多个这样的值,则返回对应最大的 timestamp_prev 的那个值”,题目中感觉说的模棱两可,并没有明确说明是对应当前key下的前一个时间的值,还是不限定当前key下的前一个时间的值

    先放个二分法查找的在这,配合二叉树的写法回头加上

    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class TimeMap {
    
        class Node {
            String val;
            int timestamp;
    
            public Node(String val, int timestamp) {
                this.val = val;
                this.timestamp = timestamp;
            }
        }
    
        HashMap<String, List<Node>> map;
    
        /** Initialize your data structure here. */
        public TimeMap() {
            map = new HashMap<>();
        }
    
        public void set(String key, String value, int timestamp) {
            Node node = new Node(value,timestamp);
            if (map.containsKey(key)){
                map.get(key).add(node);
            }else{
                List<Node> list = new ArrayList<>();
                list.add(node);
                map.put(key,list);
            }
        }
    
        public String get(String key, int timestamp) {
            if (!map.containsKey(key)){
                return "";
            }
            String val = "";
    //            for (int i = 0; i < map.get(key).size(); i++) {
    //                if (map.get(key).get(i).timestamp <= timestamp){
    //                    val = map.get(key).get(i).val;
    //                }
    //                if (map.get(key).get(i).timestamp > timestamp){
    //                    break;
    //                }
    //            }//直接遍历查找超时了,放弃
            int left = 0, right = map.get(key).size()-1;
            while (left <= right){
                int mid =  (left + right) >> 1;
                if (map.get(key).get(mid).timestamp <= timestamp){
                    left = mid + 1;
                    val = map.get(key).get(mid).val;
                }else{
                    right = mid-1;
                }
            }
            return val;
        }
    }
    

    LeetCode刷题【526】优美的排列

    假设有从 1 到 N 的 个整数,如果从这 个数字中成功构造出一个数组,使得数组的第 i 位 (1 <= i <= N) 满足如下两个条件中的一个,我们就称这个数组为一个优美的排列。条件:

    1. 第 位的数字能被 整除
    2. i 能被第 i 位上的数字整除

    现在给定一个整数 N,请问可以构造多少个优美的排列?

    示例1:

    输入: 2
    输出: 2
    解释: 
    
    第 1 个优美的排列是 [1, 2]:
      第 1 个位置(i=1)上的数字是1,1能被 i(i=1)整除
      第 2 个位置(i=2)上的数字是2,2能被 i(i=2)整除
    
    第 2 个优美的排列是 [2, 1]:
      第 1 个位置(i=1)上的数字是2,2能被 i(i=1)整除
      第 2 个位置(i=2)上的数字是1,i(i=2)能被 1 整除
    

    说明:

    1. N 是一个正整数,并且不会超过15。
    Related Topics
  • 位运算
  • 数组
  • 动态规划
  • 回溯
  • 状态压缩
  • \n
  • 👍 172
  • 👎 0
  • 题解

    8月16日的每日一题,一开始拿到题目有点不理解,反复看了几遍,大意就是,1-15的数字,任意排序,索引0的位置忽略(这样实际需要的是一个长度为16的数组),保证索引i的位置上的数字,要么能整除i,要么能被i整除。

    明白了题意就好办了,和之前的拨轮盘一样,用回溯来处理。

    
    import java.util.*;
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
    
        Map<Integer, List<Integer>> map = new HashMap<>();
        Set<Integer> visited = new HashSet<>();
        int count = 0;
        int length = 0;
        public int countArrangement(int n) {
            length = n;
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    if (!map.containsKey(i)){
                        map.put(i,new ArrayList<>());
                    }
                    if (i%j==0 || j%i==0){
                        map.get(i).add(j);
                    }
                }
            }
            backtrack(1);
            return count;
        }
    
        void backtrack(int index) {
            if (visited.size() == length){
                count++;
                return;
            }
            for (Integer matchNum : map.get(index)) {
                if (!visited.contains(matchNum)){
                    visited.add(matchNum);
                    backtrack(index+1);
                    visited.remove(matchNum);
                }
            }
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    LeetCode刷题【1008】前序遍历构造二叉搜索树

    返回与给定前序遍历 preorder 相匹配的二叉搜索树(binary search tree)的根结点。

    (回想一下,二叉搜索树是二叉树的一种,其每个节点都满足以下规则,对于 node.left 的任何后代,值总 < node.val,而 node.right 的任何后代,值总 > node.val。此外,前序遍历首先显示节点 node 的值,然后遍历 node.left,接着遍历 node.right。)

    题目保证,对于给定的测试用例,总能找到满足要求的二叉搜索树。

     

    示例:

    输入:[8,5,1,7,10,12]
    输出:[8,5,10,1,7,null,12]
    
    

     

    提示:

    • 1 <= preorder.length <= 100
    • 1 <= preorder[i] <= 10^8
    • preorder 中的值互不相同
    Related Topics
  • 二叉搜索树
  • 数组
  • 二叉树
  • 单调栈
  • \n
  • 👍 161
  • 👎 0
  • 题解

    思路和LeetCode刷题【剑指 Offer 33】二叉搜索树的后续遍历序列一样了,根据前序遍历的特性,一样的第一位为根节点,后面的序列中,比根节点小的为左子树上的节点,比根节点大的为右子树上的节点

    
    
    //leetcode submit region begin(Prohibit modification and deletion)
    /**
     * 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 TreeNode bstFromPreorder(int[] preorder) {
            return buildTree(preorder,0,preorder.length-1);
        }
    
    
    
        private TreeNode buildTree(int[] preorder,int left ,int right){
            //必要判断条件,如果区间范围不成立,表名没有这个节点
            if (left > right || left < 0 || right < 0) return null;
            //根据前序遍历的特性,该连续数组的第一位为这个树的根节点
            TreeNode node = new TreeNode(preorder[left]);
            if (left==right){
                return node;
            }
            //首位为根节点,往后进一格,处理子节点
            left++;
            //预设一个-1标记
            int rightStart = -1;
            for (int i = left; i <= right ; i++) {
                if (preorder[i]>node.val){
                    //如果当前节点比根节点大
                    //那么从当前节点开始,往后的都是右子节点
                    rightStart = i;
                    break;
                }
            }
            //如果没有找到右子节点的话,因为之前预设了-1,需要额外判断下右子节点的开始位置
            //不然还是-1,
            //没有找到右子节点的区间的话,说明左子节点区间应该为从left到right都是
            if (rightStart == -1){
                rightStart = right+1;
            }
            //分别拿出左右子节点区间继续判断处理
            node.left = buildTree(preorder,left,rightStart-1);
            node.right = buildTree(preorder,rightStart,right);
            return node;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    LeetCode刷题【剑指 Offer 33】二叉搜索树的后续遍历序列

    输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

     

    参考以下这颗二叉搜索树:

         5
        / \
       2   6
      / \
     1   3

    示例 1:

    输入: [1,6,3,2,5]
    输出: false

    示例 2:

    输入: [1,3,2,6,5]
    输出: true

     

    提示:

    1. 数组长度 <= 1000
    Related Topics
  • 二叉搜索树
  • 递归
  • 二叉树
  • 单调栈
  • \n
  • 👍 321
  • 👎 0
  • 题解

    以一个正确的序列为例,根据后续遍历的性质

    1,3,2,6,5

    应该是根节点,从根节点往前。比他大的部分应该为根节点的右子树,比根节点的小的应该是左子树,这样找到了[1,3,2]和[6]两个区间。而[6]只有一个节点,可以不做更多判断了。左边的[1,3,2]区间中,2必然是根节点,按照之前的方法,[1]是2的左子树上的节点,[3]是2的右子树上的节点。

    在这样的分割的中还需要满足一个特征,就是比根节点小的部分和比根节点大的部分的应当是正好分割的,不能有重叠,也不能有中间空的部分。

    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public boolean verifyPostorder(int[] postorder) {
            return validate(postorder,0,postorder.length-1);
        }
    
    
        private boolean validate(int[] postorder, int left ,int right){
            //如果左指针大于等于右指针了,说明当前节点以及没有子节点了,自然是符合条件的】
            //小于0的判断是从下面leftEnd rightStart 的初始赋值来的,说明没有这个子节点
            if (left>=right || left < 0 || right < 0) return true;
            //找到这段数组对应的根节点,根据后序遍历的特性,即为这段数组的最后一位
            int rootNum = postorder[right];
            //初始赋值
            int leftEnd = -1;
            int rightStart = -1;
            //开始遍历
            for (int i = left; i < right; i++) {
                if (postorder[i] < rootNum){
                    //如果这个值小于根节点的值,说明这个节点应该是在左子树中
                    leftEnd = i;
                }
                if (postorder[i] > rootNum && rightStart == -1){
                    //如果这个值大于根节点的值,说明这个节点应该是右子树上的
                    //且rightStart == -1 表示是第一次碰到的
                    rightStart = i;
                }
            }
            //此时如果符合条件的话,应该是 leftEnd 在 rightStart 的左边一位
            //或者  没有左子树:leftEnd == -1 且rightStart == left
            //或者  没有右子树:rightStart == -1 且leftEnd == right-1
            boolean validateResult = (leftEnd>-1 &&  rightStart> -1 && leftEnd+1== rightStart)
                    || ( leftEnd == -1 && rightStart == left )
                    || ( rightStart == -1 && leftEnd == right-1);
            //自身验证完了,还要对分割好了的子序列的有效性判断
            if (validateResult){
                return validate( postorder, left, leftEnd ) && validate( postorder, rightStart, right-1 );
            }
            return false;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    LeetCode刷题【面试题17.12】BiNode

    二叉树数据结构TreeNode可用来表示单向链表(其中left置空,right为下一个链表节点)。实现一个方法,把二叉搜索树转换为单向链表,要求依然符合二叉搜索树的性质,转换操作应是原址的,也就是在原始的二叉搜索树上直接修改。

    返回转换后的单向链表的头节点。

    注意:本题相对原题稍作改动

     

    示例:

    输入: [4,2,5,1,3,null,6,0]
    输出: [0,null,1,null,2,null,3,null,4,null,5,null,6]
    

    提示:

    • 节点数量不会超过 100000。
    Related Topics
  • 深度优先搜索
  • 二叉搜索树
  • 链表
  • 二叉树
  • \n
  • 👍 79
  • 👎 0
  • 题解

    很明显,二叉搜索树转换成的是有序递增链表,直接用DFS中序遍历处理。

    需要两个变量,一个用来记录一开始访问到的新的头结点位置,另一个用来记录在DFS过程中上次遍历到的节点,及该节点的前驱节点。

    按照题目的要求,在遍历过程中重新指向节点之间新的left、right指向关系,而不重新生成新的节点。

    另外,最后记得清空下最后的节点的左右子节点

    current.left = null;
    current.right = null;

    当二叉树的右子树最末端是这样的情况的时候

           …
             \
              5
             /
            4

    4节点先被加入到节点结尾,4节点的left被指向null,right指向到5。

    5节点最后被加入到结尾,此时5的left还是指向到4,需要再去清除下关系。

    当然可以在dfs中处理一下也是可以的。

    
    
    //leetcode submit region begin(Prohibit modification and deletion)
    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    class Solution {
    
        private TreeNode head;
        private TreeNode current;
        public TreeNode convertBiNode(TreeNode root) {
            if (null == root)return null;
            dfs(root);
            current.left = null;
            current.right = null;
            return head;
        }
    
        private void dfs(TreeNode root){
            if (root.left!=null){
                dfs(root.left);
            }
            if (head ==null){
                head = root;
            }
            if (current!=null){
                current.left = null;
                current.right = root;
            }
            current = root;
            if (root.right!=null){
                dfs(root.right);
            }
        }
    
    
    }
    //leetcode submit region end(Prohibit modification and deletion)

    LeetCode刷题【516】最长回文子序列

    给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

    子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

     

    示例 1:

    输入:s = "bbbab"
    输出:4
    解释:一个可能的最长回文子序列为 "bbbb" 。
    

    示例 2:

    输入:s = "cbbd"
    输出:2
    解释:一个可能的最长回文子序列为 "bb" 。
    

     

    提示:

    • 1 <= s.length <= 1000
    • s 仅由小写英文字母组成
    Related Topics
  • 字符串
  • 动态规划
  • \n
  • 👍 572
  • 👎 0
  • 题解

    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public int longestPalindromeSubseq(String s) {
            int[][] dp = new int[s.length()][s.length()];
            for (int i = s.length() - 1; i >= 0; i--) {
                dp[i][i] = 1;
                for (int j = i + 1; j < s.length(); j++) {
                    if (s.charAt(i)==s.charAt(j)){
                        dp[i][j] = dp[i+1][j-1]+2;
                    }else{
                        dp[i][j] = Math.max(dp[i][j],Math.max(dp[i+1][j],dp[i][j-1]));
                    }
                }
            }
    
            return dp[0][s.length()-1];
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    LeetCode刷题【98】验证二叉搜索树

    给定一个二叉树,判断其是否是一个有效的二叉搜索树。

    假设一个二叉搜索树具有如下特征:

    • 节点的左子树只包含小于当前节点的数。
    • 节点的右子树只包含大于当前节点的数。
    • 所有左子树和右子树自身必须也是二叉搜索树。

    示例 1:

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

    示例 2:

    输入:
        5
       / \
      1   4
         / \
        3   6
    输出: false
    解释: 输入为: [5,1,4,null,null,3,6]。
         根节点的值为 5 ,但是其右子节点值为 4 。
    
    Related Topics
  • 深度优先搜索
  • 二叉搜索树
  • 二叉树
  • \n
  • 👍 1159
  • 👎 0
  • 题解

    一样的,中序遍历,然后当前值要比前驱值大。不要求验证平衡,一开始没注意还加了平衡验证有点费事。

    /**
     * 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 {
    
    
        private Integer lastNum;
        boolean valied = false;
    
        public boolean isValidBST(TreeNode root) {
            return dfs(root)==1;
        }
    
    
        private int dfs(TreeNode root){
            if (valied){
                return 0;
            }
            int left = 1;
            int right = 1;
            if (root.left != null){
                left = dfs(root.left);
            }
            if (lastNum!=null && lastNum >= root.val){
                valied = true;
                return 0;
            }
            lastNum = root.val;
            if (root.right != null){
                right = dfs(root.right);
            }
            return left & right;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)