月度归档: 2022年6月

LeetCode刷题【384】打乱数组

给你一个整数数组 nums ,设计算法来打乱一个没有重复元素的数组。打乱后,数组的所有排列应该是 等可能 的。

实现 Solution class:

  • Solution(int[] nums) 使用整数数组 nums 初始化对象
  • int[] reset() 重设数组到它的初始状态并返回
  • int[] shuffle() 返回数组随机打乱后的结果

 

示例 1:

输入
["Solution", "shuffle", "reset", "shuffle"]
[[[1, 2, 3]], [], [], []]
输出
[null, [3, 1, 2], [1, 2, 3], [1, 3, 2]]

解释
Solution solution = new Solution([1, 2, 3]);
solution.shuffle();    // 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。例如,返回 [3, 1, 2]
solution.reset();      // 重设数组到它的初始状态 [1, 2, 3] 。返回 [1, 2, 3]
solution.shuffle();    // 随机返回数组 [1, 2, 3] 打乱后的结果。例如,返回 [1, 3, 2]

 

提示:

  • 1 <= nums.length <= 50
  • -106 <= nums[i] <= 106
  • nums 中的所有元素都是 唯一的
  • 最多可以调用 104resetshuffle
Related Topics
  • 数组
  • 数学
  • 随机化

  • 👍 284
  • 👎 0
  • 随机简单,可以参考下java.util.Collections#shuffle方法

    核心:和前面的随机一位数字交换

    class Solution {
    
        int[] nums;
    
        int[] shuffle;
    
        public Solution(int[] nums) {
            this.nums = nums;
        }
        
        public int[] reset() {
            return nums;
        }
    
        
        public int[] shuffle() {
            shuffle = nums.clone();
            Random random = new Random();
            for (int i = 0; i < shuffle.length; i++) {
                swap(shuffle,i,random.nextInt(i+1));
            }
            return shuffle;
        }
    
        public void swap(int[] arr ,int x ,int y){
            int tmp = arr[x];
            arr[x] = arr[y];
            arr[y] = tmp;
        }
    
    }

    看下java.util.Collections#shuffle(java.util.List<?>, java.util.Random)方法的源码,其实思想是一样的

    public static void shuffle(List<?> list, Random rnd) {
        int size = list.size();
        if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
            for (int i=size; i>1; i--)
                swap(list, i-1, rnd.nextInt(i));
        } else {
            Object arr[] = list.toArray();
    
            // Shuffle array
            for (int i=size; i>1; i--)
                swap(arr, i-1, rnd.nextInt(i));
    
            // Dump array back into list
            // instead of using a raw type here, it's possible to capture
            // the wildcard but it will require a call to a supplementary
            // private method
            ListIterator it = list.listIterator();
            for (int i=0; i<arr.length; i++) {
                it.next();
                it.set(arr[i]);
            }
        }
    }

    LeetCode刷题【559】N 叉树的最大深度

    给定一个 N 叉树,找到其最大深度。

    最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。

    N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)。

     

    示例 1:

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

    示例 2:

    输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
    输出:5
    

     

    提示:

    • 树的深度不会超过 1000
    • 树的节点数目位于 [0, 104] 之间。
    Related Topics
  • 深度优先搜索
  • 广度优先搜索

  • 👍 288
  • 👎 0
  • 深搜

    遍历树并记录更新最大深度

    class Solution {
        int deep = 0;
        public int maxDepth(Node root) {
            dfs(root,0);
    
            return deep;
        }
    
        public void dfs(Node node,int current){
            if (null == node){
                return ;
            }
            current++;
            deep = Math.max(deep,current);
            for (Node child : node.children){
                dfs(child,current);
            }
        }
    }
    

    广搜也可以,按层遍历记录最终的层数,代码比较简单不做赘述

    LeetCode刷题【594】最长和谐子序列

    和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是 1

    现在,给你一个整数数组 nums ,请你在所有可能的子序列中找到最长的和谐子序列的长度。

    数组的子序列是一个由数组派生出来的序列,它可以通过删除一些元素或不删除元素、且不改变其余元素的顺序而得到。

     

    示例 1:

    输入:nums = [1,3,2,2,5,2,3,7]
    输出:5
    解释:最长的和谐子序列是 [3,2,2,2,3]
    

    示例 2:

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

    示例 3:

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

     

    提示:

    • 1 <= nums.length <= 2 * 104
    • -109 <= nums[i] <= 109
    Related Topics
  • 数组
  • 哈希表
  • 排序

  • 👍 326
  • 👎 0
  • 哈希表一次遍历

    1. 遍历int[] nums数组,统计每个数字出现的次数
    2. 如果统计的表中有当前数字 -1 或者 +1 则可以和当前数字组成符合要求的子序列,长度为两个数字统计次数之和
    3. 如果不存在-1 或者 +1 的统计结果,则表明只有当前数字,不能组成符合要求的子序列

    代码

    class Solution {
    
        public int findLHS(int[] nums) {
            int max = 0;
            HashMap<Integer,Integer> map = new HashMap<>();
            for (int num : nums) {
                map.put(num,map.getOrDefault(num,0)+1);
                int tmp = Math.max(map.getOrDefault(num-1,0),map.getOrDefault(num+1,0))+ map.get(num);
                if (tmp> map.get(num)){
                    max = Math.max(max,tmp);
                }
            }
            return max;
        }
    }

    LeetCode刷题【14】最长公共前缀

    编写一个函数来查找字符串数组中的最长公共前缀。

    如果不存在公共前缀,返回空字符串 ""

     

    示例 1:

    输入:strs = ["flower","flow","flight"]
    输出:"fl"
    

    示例 2:

    输入:strs = ["dog","racecar","car"]
    输出:""
    解释:输入不存在公共前缀。

     

    提示:

    • 1 <= strs.length <= 200
    • 0 <= strs[i].length <= 200
    • strs[i] 仅由小写英文字母组成
    Related Topics
  • 字符串

  • 👍 2309
  • 👎 0
  • 字典树解法

    思路

    字符串搜索匹配中常用的方法之一字典树,用在这里还是很方便的

    1. 遍历String[] strs构建字典树
    2. 从字典树的根节点看是往下找只有一个子节点的情况,把这个自己点转化为char字符记下来
    3. 不光是只有一个的子节点的情况。如果说遇到的节点是标记为了字符串结尾的话,也应当结束,因为记录下来的字符组成的字符串已经达到了某个字符串的长度了

    一点思考

    代码中的getSingleChild()方法是判断这个节点是否只有一个子节点的,用了个遍历26个字母的方法

    如果只有一个子节点的话,会返回那个子节点对应的下标,从而可以重新转换为char字符。

    不过可以再优化调整下,我们可以在Node节点中再增加一个字段标记当前节点下面有多少个不同的子节点,而需要操作就变为,当在Nodechildren上构建新子节点的时候,同时给这个统计字段+1,这样我们在后面遍历搜索的时候就可以直接判断了

    代码

    class Solution {
        public String longestCommonPrefix(String[] strs) {
            Trie trie = new Trie();
    
            for (String str : strs) {
                trie.insert(str);
            }
    
    
            return trie.longestCommonPrefix();
        }
    
    
    
        class Trie{
    
            Node root = new Node();
    
            public void insert(String str){
                if (null == str){
                    return;
                }
                int idx = -1;
                Node cur = root;
                while (++idx < str.length()){
                    int i = str.charAt(idx) - 'a';
                    if (cur.children[i] == null){
                        cur.children[i] = new Node();
                    }
                    cur = cur.children[i];
                }
                cur.flag = true;
            }
    
    
            public String longestCommonPrefix(){
                StringBuffer sb = new StringBuffer();
                Node cur = root;
                int idx = getSingleChild(cur);
                while (idx != -1){
                    if (cur.flag){
                        break;
                    }
                    //记char
                    sb.append((char)('a'+idx));
                    //找下一个
                    cur = cur.children[idx];
                    idx = getSingleChild(cur);
                }
                return sb.toString();
            }
    
            private int getSingleChild(Node node){
                int singleChild = -1;
                for (int i = 0; i < node.children.length; i++) {
                    if (node.children[i]!=null){
                        if (singleChild!=-1){
                            return -1;
                        }
                        singleChild = i;
                    }
                }
                return singleChild;
            }
    
            class Node{
                boolean flag;
                Node[] children;
                public Node(){
                    flag = false;
                    children = new Node[26];
                }
            }
        }
    }

    改了一下,完全没提升

    class Solution {
        public String longestCommonPrefix(String[] strs) {
            Trie trie = new Trie();
            for (String str : strs) {
                trie.insert(str);
            }
            return trie.longestCommonPrefix();
        }
    
    
        class Trie {
    
            Node root = new Node();
    
            public void insert(String str) {
                if (null == str) {
                    return;
                }
                int idx = -1;
                Node cur = root;
                while (++idx < str.length()) {
                    int i = str.charAt(idx) - 'a';
                    if (cur.children[i] == null) {
                        cur.children[i] = new Node();
                        cur.childrenCount++;
                    }
                    cur = cur.children[i];
                }
                cur.flag = true;
            }
    
    
            public String longestCommonPrefix() {
                StringBuffer sb = new StringBuffer();
                Node cur = root;
                int idx = cur.getSingleChild();
                while (idx != -1) {
                    if (cur.flag) {
                        break;
                    }
                    //记char
                    sb.append((char) ('a' + idx));
                    //找下一个
                    cur = cur.children[idx];
                    idx = cur.getSingleChild();
                }
                return sb.toString();
            }
    
            class Node {
    
                boolean flag;
                Node[] children;
                int childrenCount;
    
                public Node() {
                    childrenCount = 0;
                    flag = false;
                    children = new Node[26];
                }
    
                public boolean isSingleChild() {
                    return childrenCount == 1;
                }
    
                public int getSingleChild() {
                    if (isSingleChild()) {
                        for (int i = 0; i < children.length; i++) {
                            if (children[i] != null) {
                                return i;
                            }
                        }
                    }
                    return -1;
                }
            }
        }
    }

    LeetCode刷题【剑指 Offer 18】删除链表的节点

    给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

    返回删除后的链表的头节点。

    注意:此题对比原题有改动

    示例 1:

    输入: head = [4,5,1,9], val = 5
    输出: [4,1,9]
    解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
    

    示例 2:

    输入: head = [4,5,1,9], val = 1
    输出: [4,5,9]
    解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
    

     

    说明:

    • 题目保证链表中节点的值互不相同
    • 若使用 C 或 C++ 语言,你不需要 freedelete 被删除的节点
    Related Topics
  • 链表

  • 👍 232
  • 👎 0
  • 遍历修改引用

    基本分析

    1. 删除头结点的操作作为特殊情况处理,直接返回头结点的下一个节点
    2. 遍历链表,找到值等于val的节点,
    3. 遍历的时候还要有个变量记下前一个节点是谁,
    4. 直接把前一个节点的next指向当前值为val的节点的next节点
    5. 注意一些遍历的边界情况
      • 比如没有找到匹配的val的节点的话,最红对链表结尾的判定
      • 比如加入链表长度只有1,且val也没有匹配这个节点的值的话pre节点依旧为null
      • 该有比如,假如链表里值为val的节点不止一个的话怎么处理呢?本题好像没这个情况

    代码

    class Solution {
        public ListNode deleteNode(ListNode head, int val) {
            if (head==null){
                return head;
            }
            if (head.val == val){
                return head.next;
            }
    
            ListNode cur = head;
            ListNode pre = null;
            while (val != cur.val && cur != null){
                pre = cur;
                cur = cur.next;
            }
            if (null!=pre){
                pre.next = cur.next;
            }
            return head;
        }
    }

    LeetCode刷题【剑指 Offer 06】从尾到头打印链表

    输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

     

    示例 1:

    输入:head = [1,3,2]
    输出:[2,3,1]

     

    限制:

    0 <= 链表长度 <= 10000

    Related Topics
  • 递归
  • 链表
  • 双指针

  • 👍 310
  • 👎 0
  • 递归

    class Solution {
        int[] arr;
        public int[] reversePrint(ListNode head) {
            recursion(head,0);
            return arr;
        }
    
        private void recursion(ListNode node, int n) {
            if (node==null) {
                arr = new int[n];
                return;
            }
            recursion(node.next,n+1);
            arr[arr.length-n-1] = node.val;
        }
    }

    LeetCode刷题【1268】搜索推荐系统

    给你一个产品数组 products 和一个字符串 searchWord ,products  数组中每个产品都是一个字符串。

    请你设计一个推荐系统,在依次输入单词 searchWord 的每一个字母后,推荐 products 数组中前缀与 searchWord 相同的最多三个产品。如果前缀相同的可推荐产品超过三个,请按字典序返回最小的三个。

    请你以二维列表的形式,返回在输入 searchWord 每个字母后相应的推荐产品的列表。

     

    示例 1:

    输入:products = ["mobile","mouse","moneypot","monitor","mousepad"], searchWord = "mouse"
    输出:[
    ["mobile","moneypot","monitor"],
    ["mobile","moneypot","monitor"],
    ["mouse","mousepad"],
    ["mouse","mousepad"],
    ["mouse","mousepad"]
    ]
    解释:按字典序排序后的产品列表是 ["mobile","moneypot","monitor","mouse","mousepad"]
    输入 m 和 mo,由于所有产品的前缀都相同,所以系统返回字典序最小的三个产品 ["mobile","moneypot","monitor"]
    输入 mou, mous 和 mouse 后系统都返回 ["mouse","mousepad"]
    

    示例 2:

    输入:products = ["havana"], searchWord = "havana"
    输出:[["havana"],["havana"],["havana"],["havana"],["havana"],["havana"]]
    

    示例 3:

    输入:products = ["bags","baggage","banner","box","cloths"], searchWord = "bags"
    输出:[["baggage","bags","banner"],["baggage","bags","banner"],["baggage","bags"],["bags"]]
    

    示例 4:

    输入:products = ["havana"], searchWord = "tatiana"
    输出:[[],[],[],[],[],[],[]]
    

     

    提示:

    • 1 <= products.length <= 1000
    • 1 <= Σ products[i].length <= 2 * 10^4
    • products[i] 中所有的字符都是小写英文字母。
    • 1 <= searchWord.length <= 1000
    • searchWord 中所有字符都是小写英文字母。
    Related Topics
  • 字典树
  • 数组
  • 字符串

  • 👍 123
  • 👎 0
  • 字典树基本模板基本应用

    字典树基本模板先写出来,然后稍微改下就可以了

    1. String[] products遍历 构建字典树
    2. Node节点上保存对应的字符串,遍历到的时候直接把字符串塞过来,只保留3个,且按照字典序排序,这里借用到TreeSet结构的特性
    3. search的时候没遍历到一个节点就把对应Node上的字符串存下来
    4. 最终返回
    class Solution {
        List<List<String>> res;
        public List<List<String>> suggestedProducts(String[] products, String searchWord) {
            Trie trie = new Trie();
            for (String product : products) {
                trie.insert(product);
            }
            return trie.search(searchWord);
        }
    
        class Trie{
    
            Node root;
    
            public Trie(){
                root = new Node();
            }
    
            public void insert(String word){
                int idx = -1;
                Node cur = root;
                while (++idx < word.length()){
                    int i = word.charAt(idx) - 'a';
                    if (cur.children[i] == null){
                        cur.children[i] = new Node();
                    }
                    cur = cur.children[i];
                    cur.addWord(word);
                }
                cur.flag = true;
            }
    
            public List<List<String>> search(String word){
                List<List<String>> res = new ArrayList<>();
                int idx = -1;
                Node cur = root;
                while (++idx < word.length()){
                    List<String> list = new ArrayList<>();
                    int i = word.charAt(idx)-'a';
                    if (cur!=null){
                        cur = cur.children[i];
                        list = cur==null?new ArrayList<>():new ArrayList<>(cur.treeSet);
                    }
                    res.add(list);
                }
                return res;
            }
    
            class Node{
                boolean flag;
                Node[] children;
                TreeSet<String> treeSet;
    
                public Node(){
                    flag = false;
                    children = new Node[26];
                    treeSet = new TreeSet<>();
                }
    
                public void addWord(String word){
                    treeSet.add(word);
                    if (treeSet.size()>3){
                        treeSet.pollLast();
                    }
                }
            }
        }
    }