力扣热门算法题 75. 颜色分类,76. 最小覆盖子串,77. 组合

58 篇文章 1 订阅
55 篇文章 0 订阅

75. 颜色分类,76. 最小覆盖子串,77. 组合,每题做详细思路梳理,配套Python&Java双语代码, 2024.03.21 可通过leetcode所有测试用例

目录

75. 颜色分类

解题思路

完整代码

Python

Java

76. 最小覆盖子串

解题思路

完整代码

Python

Java

77. 组合

解题思路

完整代码

Python

Java


75. 颜色分类

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

必须在不使用库内置的 sort 函数的情况下解决这个问题。

示例 1:

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

示例 2:

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

提示:

  • n == nums.length
  • 1 <= n <= 300
  • nums[i] 为 01 或 2

解题思路

其核心思想是使用三个指针来分别跟踪 0、1 和 2。我们可以将数组中的元素分为三部分:红色区(0),白色区(1),以及蓝色区(2)。以下是具体的步骤:

  1. 初始化三个指针lowmidhighlow 指针跟踪红色区的最后一个元素的下一个位置,mid 指针用于遍历整个数组,high 指针跟踪蓝色区的第一个元素的前一个位置。初始时,lowmid 都设置为数组的起始位置,high 设置为数组的末尾位置。

  2. 遍历数组:当 mid 指针小于等于 high 指针时,进行遍历:

    • 如果 nums[mid] 等于 0(红色),则将其与 nums[low] 交换,并将 lowmid 指针都向前移动一位。
    • 如果 nums[mid] 等于 1(白色),则不需要交换,只需将 mid 指针向前移动一位。
    • 如果 nums[mid] 等于 2(蓝色),则将其与 nums[high] 交换,并将 high 指针向后移动一位。此时不移动 mid 指针,因为交换后的元素还未被检查。
  3. 重复步骤 2,直到 mid 指针超过 high 指针。

完整代码

Python
class Solution:
    def sortColors(self, nums: List[int]) -> None:
        low, mid, high = 0, 0, len(nums) - 1

        while mid <= high:
            if nums[mid] == 0:
                nums[low], nums[mid] = nums[mid], nums[low]
                low += 1
                mid += 1
            elif nums[mid] == 1:
                mid += 1
            else:
                nums[mid], nums[high] = nums[high], nums[mid]
                high -= 1
Java
class Solution {
    public void sortColors(int[] nums) {
        int low = 0, mid = 0, high = nums.length - 1;

        while (mid <= high) {
            switch (nums[mid]) {
                case 0:
                    // 交换 low 和 mid 的值
                    int temp = nums[low];
                    nums[low] = nums[mid];
                    nums[mid] = temp;
                    low++;
                    mid++;
                    break;
                case 1:
                    mid++;
                    break;
                case 2:
                    // 交换 mid 和 high 的值
                    temp = nums[mid];
                    nums[mid] = nums[high];
                    nums[high] = temp;
                    high--;
                    break;
            }
        }
    }
}

76. 最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

示例 2:

输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。

示例 3:

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

解题思路

我们可以使用滑动窗口的方法。基本思想是使用两个指针 leftright 来表示 s 中的一个窗口,并在这个窗口中寻找包含 t 所有字符的最小子串。这个过程分为几个步骤:

  1. 初始化:创建两个哈希表,一个用于存储字符串 t 中字符的频率(称为 need),另一个用于存储当前窗口中字符的频率(称为 window)。还需要两个指针 leftright 表示当前窗口的左右边界,初始化为 0。定义 valid 变量来记录窗口中满足 need 条件的字符数量。

  2. 扩大窗口:移动右指针 right 扩大窗口,直到窗口中包含了 t 的所有字符。

  3. 更新结果:当窗口包含了 t 的所有字符后,尝试移动左指针 left 缩小窗口,同时更新最小子串的长度和起始位置。如果当前窗口的长度小于之前找到的最小子串长度,更新最小子串长度和起始位置。

  4. 缩小窗口:继续移动左指针 left 缩小窗口,直到窗口不再满足 t 的所有字符。此时再次扩大窗口,重复步骤 2 和 3。

  5. 返回结果:根据记录的最小子串起始位置和长度返回最终结果。

完整代码

Python
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        from collections import Counter
        need, window = Counter(t), {}
        left = right = 0
        valid = 0
        start, length = 0, float('inf')
        
        while right < len(s):
            # c 是将移入窗口的字符
            c = s[right]
            # 右移窗口
            right += 1
            # 进行窗口内数据的一系列更新
            if c in need:
                window[c] = window.get(c, 0) + 1
                if window[c] == need[c]:
                    valid += 1
            
            # 判断左侧窗口是否要收缩
            while valid == len(need):
                # 更新最小覆盖子串
                if right - left < length:
                    start = left
                    length = right - left
                # d 是将移出窗口的字符
                d = s[left]
                # 左移窗口
                left += 1
                # 进行窗口内数据的一系列更新
                if d in need:
                    if window[d] == need[d]:
                        valid -= 1
                    window[d] -= 1
        
        return "" if length == float('inf') else s[start:start+length]
Java
class Solution {
    public String minWindow(String s, String t) {
        HashMap<Character, Integer> need = new HashMap<>();
        HashMap<Character, Integer> window = new HashMap<>();
        for (char c : t.toCharArray()) need.put(c, need.getOrDefault(c, 0) + 1);

        int left = 0, right = 0;
        int valid = 0;
        int start = 0, length = Integer.MAX_VALUE;
        
        while (right < s.length()) {
            char c = s.charAt(right);
            right++;
            if (need.containsKey(c)) {
                window.put(c, window.getOrDefault(c, 0) + 1);
                if (window.get(c).equals(need.get(c))) valid++;
            }

            while (valid == need.size()) {
                if (right - left < length) {
                    start = left;
                    length = right - left;
                }
                char d = s.charAt(left);
                left++;
                if (need.containsKey(d)) {
                    if (window.get(d).equals(need.get(d))) valid--;
                    window.put(d, window.get(d) - 1);
                }
            }
        }
        
        return length == Integer.MAX_VALUE ? "" : s.substring(start, start + length);
    }
}

 

77. 组合

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

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

示例 2:

输入:n = 1, k = 1
输出:[[1]]

解题思路

我们可以使用回溯法,这是一种通过探索所有可能的候选解来找出所有解的解决方案的算法。当搜索到一个可能解时,回溯法会先记录下这个解,然后撤销到上一步状态,尝试另一种选择,继续探索直到所有的选择都尝试过了。以下是具体的步骤:

  1. 定义回溯函数:创建一个回溯函数 backtrack,它接受当前的组合 combination 和下一个要添加到组合中的数字 start

  2. 递归的终止条件:当 combination 的长度等于 k 时,将其添加到结果列表中,然后返回。

  3. 遍历所有可能的选择:从 startn 遍历所有可能的数字。对于每一个数字,将其添加到当前组合中,然后递归地调用 backtrack 函数进入下一层。

  4. 回溯:递归调用返回后,撤销上一步的操作,即从 combination 中移除上一步添加的数字,尝试下一个可能的数字。

  5. 调用回溯函数并返回结果:初始化一个空列表来存储所有可能的组合,然后从数字 1 开始调用回溯函数。

完整代码

Python
class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        result = []
        combination = []

        def backtrack(start):
            if len(combination) == k:
                result.append(combination[:])
                return
            for i in range(start, n + 1):
                combination.append(i)
                backtrack(i + 1)
                combination.pop()

        backtrack(1)
        return result
Java
class Solution {
    public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> result = new ArrayList<>();
        List<Integer> combination = new ArrayList<>();

        void backtrack(int start) {
            if (combination.size() == k) {
                result.add(new ArrayList<>(combination));
                return;
            }
            for (int i = start; i <= n; i++) {
                combination.add(i);
                backtrack(i + 1);
                combination.remove(combination.size() - 1);
            }
        }

        backtrack(1);
        return result;
    }
}

  • 26
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昊昊该干饭了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值