Coupang leetcode 刷题宝典

题目1:340. 至多包含 K 个不同字符的最长子串

给你一个字符串 s 和一个整数 k ,请你找出 至多 包含 k 个 不同 字符的最长

子串

,并返回该子串的长度。

示例 1:

输入:s = "eceba", k = 2
输出:3
解释:满足题目要求的子串是 "ece" ,长度为 3 。

示例 2:

输入:s = "aa", k = 1
输出:2
解释:满足题目要求的子串是 "aa" ,长度为 2 。

提示:

  • 1 <= s.length <= 5 * 104
  • 0 <= k <= 50
class Solution {
    public int lengthOfLongestSubstringKDistinct(String s, int k) {
        if (k == 0) {
            return 0;
        }
        
        // 使用 HashMap 记录窗口内每个字符出现的次数
        Map<Character, Integer> charCount = new HashMap<>();
        int maxLength = 0;
        int left = 0;
        
        // 右指针遍历字符串
        for (int right = 0; right < s.length(); right++) {
            char c = s.charAt(right);
            // 将当前字符加入 HashMap
            charCount.put(c, charCount.getOrDefault(c, 0) + 1);
            
            // 当 HashMap 中不同字符的数量超过 k 时,需要收缩窗口
            while (charCount.size() > k) {
                char leftChar = s.charAt(left);
                charCount.put(leftChar, charCount.get(leftChar) - 1);
                if (charCount.get(leftChar) == 0) {
                    charCount.remove(leftChar);
                }
                left++;
            }
            
            // 更新最大长度
            maxLength = Math.max(maxLength, right - left + 1);
        }
        
        return maxLength;
    }
}

 

题目2:724. 寻找数组的中心下标

给你一个整数数组 nums ,请计算数组的 中心下标 

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

示例 1:

输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:
中心下标是 3 。
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。

示例 2:

输入:nums = [1, 2, 3]
输出:-1
解释:
数组中不存在满足此条件的中心下标。

示例 3:

输入:nums = [2, 1, -1]
输出:0
解释:
中心下标是 0 。
左侧数之和 sum = 0 ,(下标 0 左侧不存在元素),
右侧数之和 sum = nums[1] + nums[2] = 1 + -1 = 0 。

提示:

  • 1 <= nums.length <= 104
  • -1000 <= nums[i] <= 1000
class Solution {
    public int pivotIndex(int[] nums) {
        // 计算数组总和
        int total = 0;
        for (int num : nums) {
            total += num;
        }
        
        // 从左向右遍历,计算左侧和
        int leftSum = 0;
        for (int i = 0; i < nums.length; i++) {
            // 右侧和 = 总和 - 左侧和 - 当前元素
            int rightSum = total - leftSum - nums[i];
            
            // 如果左侧和等于右侧和,找到中心下标
            if (leftSum == rightSum) {
                return i;
            }
            
            // 更新左侧和
            leftSum += nums[i];
        }
        
        // 没有找到中心下标,返回-1
        return -1;
    }
}

 

题目3:776. 拆分二叉搜索树

给你一棵二叉搜索树(BST)的根结点 root 和一个整数 target 。请将该树按要求拆分为两个子树:其中第一个子树结点的值都必须小于等于给定的目标值;另一个子树结点的值都必须大于目标值;树中并非一定要存在值为 target 的结点。

除此之外,树中大部分结构都需要保留,也就是说原始树中父节点 p 的任意子节点 c ,假如拆分后它们仍在同一个子树中,那么结点 p 应仍为 c 的父结点。

按顺序返回 两个子树的根结点的数组 。

示例 1:

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

示例 2:

输入: root = [1], target = 1
输出: [[1],[]]

提示:

  • 二叉搜索树节点个数在 [1, 50] 范围内
  • 0 <= Node.val, target <= 1000
/**
 * 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[] splitBST(TreeNode root, int target) {
        // 结果数组:result[0]存储小于等于target的子树,result[1]存储大于target的子树
        TreeNode[] result = new TreeNode[]{null, null};
        
        // 处理空树的情况
        if (root == null) {
            return result;
        }
        
        // 如果根节点值小于等于target
        if (root.val <= target) {
            // 递归处理右子树
            result = splitBST(root.right, target);
            // 当前节点属于小于等于target的子树
            root.right = result[0];
            result[0] = root;
            return result;
        } 
        // 如果根节点值大于target
        else {
            // 递归处理左子树
            result = splitBST(root.left, target);
            // 当前节点属于大于target的子树
            root.left = result[1];
            result[1] = root;
            return result;
        }
    }
}

题目4:78. 子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的

子集

(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

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

示例 2:

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

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        int resultSize = 1<<nums.length;
        List<List<Integer>> result = new ArrayList<>(resultSize);

        //init the result set
        result.add(Collections.emptyList());

        for (int i=0; i<nums.length; i++) {
            // if nums[i] not in set, has been existed, skip it.
            
            // if nums[i] in set, we need to copy it and add the element into result
            int currentLen = result.size();
            for(int j=0; j<currentLen; j++) {
                List<Integer> list = result.get(j);
                List<Integer> newList = new ArrayList<>(list.size()+1);
                newList.addAll(list);
                newList.add(nums[i]);
                result.add(newList);     
            }
        }

        return result;
    }

    public List<List<Integer>> subsets(int[] nums, int end) {
        List<List<Integer>> result = new ArrayList<>();

        if (end <= 1) {
            // no elements
            result.add(Collections.emptyList());
            // one element
            result.add(List.of(nums[0]));
        } else {
            List<List<Integer>> resultN1 = subsets(nums, end-1);

            // the last element not in set
            result.addAll(resultN1);

            // the last element in set
            int lastEle = nums[end-1];
            for(List<Integer> list: resultN1) {
                List<Integer> newList = new ArrayList<>(list.size()+1);
                newList.addAll(list);
                newList.add(lastEle);
                result.add(newList);
            }
        }

        return result;
    }
}

题目5:394. 字符串解码

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

示例 1:

输入:s = "3[a]2[bc]"
输出:"aaabcbc"

示例 2:

输入:s = "3[a2[c]]"
输出:"accaccacc"

示例 3:

输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"

示例 4:

输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"

提示:

  • 1 <= s.length <= 30
  • s 由小写英文字母、数字和方括号 '[]' 组成
  • s 保证是一个 有效 的输入。
  • s 中所有整数的取值范围为 [1, 300] 
class Solution {
    public String decodeString(String s) {
        StringBuilder sb = new StringBuilder();
        int len = s.length();

        for (int index=0; index<len; index++) {
            Character ch = s.charAt(index);
            if (ch == ']') {
                decode(sb);
            } else {
                sb.append(ch);
            }
        }

        return sb.toString();
    }

    /**
     * 2str, 2a, 2abc
     */
    public void decode(StringBuilder builder) {
        int size = builder.length();

        int kEnd = -1, index;
        String subStr="";
        for (index=size-1; index>=0; index--) {
            Character ch = builder.charAt(index);
            if (ch == '[' && kEnd == -1) {
                subStr = builder.substring(index+1);
                kEnd = index;
            } else if (kEnd != -1 && (ch <'0' || ch > '9')) { // not number
                break;
            }
        }

        if (kEnd != -1) {
            Integer k = Integer.valueOf(builder.substring(index+1, kEnd));
            // replace 2a,2abc with aa, abcabc.
            builder.setLength(index+1);
            for (int j=1; j<=k; j++) {
                builder.append(subStr);
            }
        }
    }
}

题目6: 139. 单词拆分

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true

注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。

示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。
     注意,你可以重复使用字典中的单词。

示例 3:

输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

提示:

  • 1 <= s.length <= 300
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 20
  • s 和 wordDict[i] 仅由小写英文字母组成
  • wordDict 中的所有字符串 互不相同
class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        // 将 wordDict 转换为 Set,便于快速查找
        Set<String> wordSet = new HashSet<>(wordDict);
        
        // dp[i] 表示字符串 s 的前 i 个字符是否可以被拆分成 wordDict 中的单词
        boolean[] dp = new boolean[s.length() + 1];
        
        // 空字符串可以被拆分
        dp[0] = true;
        
        // 遍历所有可能的字符串长度
        for (int i = 1; i <= s.length(); i++) {
            // 遍历所有可能的拆分点
            for (int j = 0; j < i; j++) {
                // 如果前 j 个字符可以被拆分,并且剩余部分在字典中存在
                if (dp[j] && wordSet.contains(s.substring(j, i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        
        return dp[s.length()];
    }
}

 

题目7:715. Range 模块

Range模块是跟踪数字范围的模块。设计一个数据结构来跟踪表示为 半开区间 的范围并查询它们。

半开区间 [left, right) 表示所有 left <= x < right 的实数 x 。

实现 RangeModule 类:

  • RangeModule() 初始化数据结构的对象。
  • void addRange(int left, int right) 添加 半开区间 [left, right),跟踪该区间中的每个实数。添加与当前跟踪的数字部分重叠的区间时,应当添加在区间 [left, right) 中尚未跟踪的任何数字到该区间中。
  • boolean queryRange(int left, int right) 只有在当前正在跟踪区间 [left, right) 中的每一个实数时,才返回 true ,否则返回 false 。
  • void removeRange(int left, int right) 停止跟踪 半开区间 [left, right) 中当前正在跟踪的每个实数。

示例 1:

输入
["RangeModule", "addRange", "removeRange", "queryRange", "queryRange", "queryRange"]
[[], [10, 20], [14, 16], [10, 14], [13, 15], [16, 17]]
输出
[null, null, null, true, false, true]

解释
RangeModule rangeModule = new RangeModule();
rangeModule.addRange(10, 20);
rangeModule.removeRange(14, 16);
rangeModule.queryRange(10, 14); 返回 true (区间 [10, 14) 中的每个数都正在被跟踪)
rangeModule.queryRange(13, 15); 返回 false(未跟踪区间 [13, 15) 中像 14, 14.03, 14.17 这样的数字)
rangeModule.queryRange(16, 17); 返回 true (尽管执行了删除操作,区间 [16, 17) 中的数字 16 仍然会被跟踪)

提示:

  • 1 <= left < right <= 109
  • 在单个测试用例中,对 addRange 、  queryRange 和 removeRange 的调用总数不超过 104 次

class RangeModule {
    private TreeMap<Integer, Integer> intervals;

    public RangeModule() {
        intervals = new TreeMap<>();
    }
    
    public void addRange(int left, int right) {
        // 找到可能重叠的区间
        Integer start = intervals.floorKey(left);
        Integer end = intervals.floorKey(right);
        
        if (start != null && intervals.get(start) >= left) {
            // 左边界可以和已有区间合并
            left = start;
        }
        
        if (end != null && intervals.get(end) > right) {
            // 右边界可以和已有区间合并
            right = intervals.get(end);
        }
        
        // 删除范围内的所有区间
        intervals.subMap(left, true, right, true).clear();
        // 添加新的合并后的区间
        intervals.put(left, right);
    }
    
    public boolean queryRange(int left, int right) {
        // 找到左边最近的区间
        Integer start = intervals.floorKey(left);
        if (start == null) {
            return false;
        }
        // 检查是否完全包含查询区间
        return intervals.get(start) >= right;
    }
    
    public void removeRange(int left, int right) {
        // 找到可能重叠的区间
        Integer start = intervals.floorKey(left);
        Integer end = intervals.floorKey(right);
        
        // 处理右侧重叠
        if (end != null && intervals.get(end) > right) {
            intervals.put(right, intervals.get(end));
        }
        
        // 处理左侧重叠
        if (start != null && intervals.get(start) > left) {
            intervals.put(start, left);
        }
        
        // 删除范围内的所有区间
        intervals.subMap(left, true, right, false).clear();
    }
}

题目8:990. 等式方程的可满足性

给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:"a==b" 或 "a!=b"。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。

只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 true,否则返回 false。 

    示例 1:

    输入:["a==b","b!=a"]
    输出:false
    解释:如果我们指定,a = 1 且 b = 1,那么可以满足第一个方程,但无法满足第二个方程。没有办法分配变量同时满足这两个方程。
    

    示例 2:

    输入:["b==a","a==b"]
    输出:true
    解释:我们可以指定 a = 1 且 b = 1 以满足满足这两个方程。
    

    示例 3:

    输入:["a==b","b==c","a==c"]
    输出:true
    

    示例 4:

    输入:["a==b","b!=c","c==a"]
    输出:false
    

    示例 5:

    输入:["c==c","b==d","x!=z"]
    输出:true
    

    提示:

    1. 1 <= equations.length <= 500
    2. equations[i].length == 4
    3. equations[i][0] 和 equations[i][3] 是小写字母
    4. equations[i][1] 要么是 '=',要么是 '!'
    5. equations[i][2] 是 '='
    class Solution {
        class UnionFind {
            private int[] parent;
            private int[] rank;
            
            public UnionFind(int size) {
                parent = new int[size];
                rank = new int[size];
                for (int i = 0; i < size; i++) {
                    parent[i] = i;
                }
            }
            
            public int find(int x) {
                if (parent[x] != x) {
                    parent[x] = find(parent[x]); // 路径压缩
                }
                return parent[x];
            }
            
            public void union(int x, int y) {
                int rootX = find(x);
                int rootY = find(y);
                
                if (rootX != rootY) {
                    if (rank[rootX] < rank[rootY]) {
                        parent[rootX] = rootY;
                    } else if (rank[rootX] > rank[rootY]) {
                        parent[rootY] = rootX;
                    } else {
                        parent[rootY] = rootX;
                        rank[rootX]++;
                    }
                }
            }
            
            public boolean isConnected(int x, int y) {
                return find(x) == find(y);
            }
        }
        
        public boolean equationsPossible(String[] equations) {
            // 创建并查集,26个小写字母
            UnionFind uf = new UnionFind(26);
            
            // 第一次遍历:处理所有相等关系
            for (String equation : equations) {
                if (equation.charAt(1) == '=') {
                    int x = equation.charAt(0) - 'a';
                    int y = equation.charAt(3) - 'a';
                    uf.union(x, y);
                }
            }
            
            // 第二次遍历:检查所有不等关系
            for (String equation : equations) {
                if (equation.charAt(1) == '!') {
                    int x = equation.charAt(0) - 'a';
                    int y = equation.charAt(3) - 'a';
                    if (uf.isConnected(x, y)) {
                        return false;
                    }
                }
            }
            
            return true;
        }
    }

    题目9:224. 基本计算器 

    给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

    注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。

     

    示例 1:

    输入:s = "1 + 1"
    输出:2
    

    示例 2:

    输入:s = " 2-1 + 2 "
    输出:3
    

    示例 3:

    输入:s = "(1+(4+5+2)-3)+(6+8)"
    输出:23
    

     

    提示:

    • 1 <= s.length <= 3 * 105
    • s 由数字、'+''-''('')'、和 ' ' 组成
    • s 表示一个有效的表达式
    • '+' 不能用作一元运算(例如, "+1" 和 "+(2 + 3)" 无效)
    • '-' 可以用作一元运算(即 "-1" 和 "-(2 + 3)" 是有效的)
    • 输入中不存在两个连续的操作符
    • 每个数字和运行的计算将适合于一个有符号的 32位 整数
    class Solution {
        private int index = 0;  // 用于遍历字符串的指针
        
        public int calculate(String s) {
            Stack<Integer> stack = new Stack<>();
            int num = 0;
            int sign = 1;  // 1表示正号,-1表示负号
            int result = 0;
            
            while (index < s.length()) {
                char c = s.charAt(index);
                
                if (Character.isDigit(c)) {
                    // 处理多位数字
                    num = num * 10 + (c - '0');
                    
                } else if (c == '+') {
                    // 处理前一个数字
                    result += sign * num;
                    num = 0;
                    sign = 1;
                    
                } else if (c == '-') {
                    // 处理前一个数字
                    result += sign * num;
                    num = 0;
                    sign = -1;
                    
                } else if (c == '(') {
                    // 遇到左括号,递归计算括号内的值
                    index++;  // 跳过左括号
                    int subResult = calculate(s);
                    result += sign * subResult;
                    sign = 1;
                    num = 0;
                    
                } else if (c == ')') {
                    // 遇到右括号,处理最后一个数字并返回
                    result += sign * num;
                    return result;
                }
                
                index++;
            }
            
            // 处理最后一个数字
            result += sign * num;
            return result;
        }
    }

     

    题目10:54. 螺旋矩阵

    给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

    示例 1:

    输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
    输出:[1,2,3,6,9,8,7,4,5]
    

    示例 2:

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

    提示:

    • m == matrix.length
    • n == matrix[i].length
    • 1 <= m, n <= 10
    • -100 <= matrix[i][j] <= 100
    class Solution {
        public List<Integer> spiralOrder(int[][] matrix) {
            List<Integer> result = new ArrayList<>();
            if (matrix == null || matrix.length == 0) {
                return result;
            }
            
            int top = 0;
            int bottom = matrix.length - 1;
            int left = 0;
            int right = matrix[0].length - 1;
            
            while (top <= bottom && left <= right) {
                // 从左到右遍历上边
                for (int i = left; i <= right; i++) {
                    result.add(matrix[top][i]);
                }
                top++;
                
                // 从上到下遍历右边
                for (int i = top; i <= bottom && left <= right; i++) {
                    result.add(matrix[i][right]);
                }
                right--;
                
                // 从右到左遍历下边
                for (int i = right; i >= left && top <= bottom; i--) {
                    result.add(matrix[bottom][i]);
                }
                bottom--;
                
                // 从下到上遍历左边
                for (int i = bottom; i >= top && left <= right; i--) {
                    result.add(matrix[i][left]);
                }
                left++;
            }
            
            return result;
        }
    }

     

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值