首页 > 编程语言 >力扣大厂热门面试算法题 39-41

力扣大厂热门面试算法题 39-41

时间:2024-03-17 18:04:52浏览次数:32  
标签:39 target nums int 41 力扣 candidates result 数字

39. 组合总和,40. 组合总和 II,41. 缺失的第一个正数,每题做详细思路梳理,配套Python&Java双语代码, 2024.03.17 可通过leetcode所有测试用例

目录

39. 组合总和

解题思路

完整代码

Python

Java

40. 组合总和 II

解题思路

完整代码

Python

Java

41. 缺失的第一个正数

解题思路

完整代码

Python

Java


39. 组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

提示:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • candidates 的所有元素 互不相同
  • 1 <= target <= 40

解题思路

        对于这个问题,我们可以从空解开始,尝试每一个 candidates 中的数字,看它是否能够构成目标和。如果当前选择的数字和之前选择的数字的总和小于目标 target,我们可以继续选择相同的数字或下一个数字,因为题目中说明了同一个数字可以无限次重复选择。如果总和等于目标 target,则将当前解添加到结果列表中。如果总和大于 target,则回溯,尝试下一个数字。

解题步骤如下:

  1. candidates 进行排序,以便我们可以在总和超过 target 时提前终止搜索。
  2. 使用回溯法构建一个辅助函数来生成所有可能的组合。这个函数将会接收当前的组合、当前的总和、当前位置和目标 target
  3. 在每次调用中,如果当前总和等于目标 target,则将当前组合添加到结果列表中。如果当前总和大于 target,则终止当前分支。
  4. 从当前位置开始,递归地尝试每一个可能的选项,将当前数字添加到当前组合中,并更新当前总和。每次递归调用后,撤销上一次选择,以尝试下一个数字。
  5. 最终返回结果列表。

完整代码

Python
class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        def backtrack(start, path, target):
            if target == 0:
                result.append(list(path))
                return
            if target < 0:
                return
            for i in range(start, len(candidates)):
                path.append(candidates[i])
                backtrack(i, path, target - candidates[i])  # 不增加i的值来允许数字重复选择
                path.pop()  # 回溯

        result = []
        candidates.sort()  # 排序有助于提前终止搜索
        backtrack(0, [], target)
        return result
Java
public class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(candidates); // 排序有助于提前终止搜索
        backtrack(result, new ArrayList<>(), candidates, target, 0);
        return result;
    }

    private void backtrack(List<List<Integer>> result, List<Integer> tempList, int[] candidates, int remain, int start) {
        if (remain == 0) {
            result.add(new ArrayList<>(tempList));
        } else if (remain < 0) {
            return;
        } else {
            for (int i = start; i < candidates.length; i++) {
                tempList.add(candidates[i]);
                backtrack(result, tempList, candidates, remain - candidates[i], i); // 不增加i的值来允许数字重复选择
                tempList.remove(tempList.size() - 1); // 回溯
            }
        }
    }
}

40. 组合总和 II

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

注意:解集不能包含重复的组合。 

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

提示:

  • 1 <= candidates.length <= 100
  • 1 <= candidates[i] <= 50
  • 1 <= target <= 30

解题思路

这个问题与之前的问题类似,但有一个关键的区别:每个数字在每个组合中只能使用一次。我们仍然可以使用回溯法来解决这个问题,但需要添加一些额外的步骤来确保不会有重复的组合。

解题步骤如下:

  1. candidates 进行排序,这样有助于我们在递归时跳过重复的数字,以避免产生重复的组合。
  2. 使用回溯法构建一个辅助函数来生成所有可能的组合。这个函数将会接收当前的组合、当前的总和、当前位置和目标 target
  3. 在每次调用中,如果当前总和等于目标 target,则将当前组合添加到结果列表中。如果当前总和大于 target,则终止当前分支。
  4. 从当前位置的下一个位置开始,递归地尝试每一个可能的选项,将当前数字添加到当前组合中,并更新当前总和。这一步确保了每个数字在每个组合中只使用一次。
  5. 为了避免重复的组合,每次在递归的同一层级中,如果遇到与之前相同的数字,则跳过这个数字。
  6. 每次递归调用后,撤销上一次选择,以尝试下一个数字。
  7. 最终返回结果列表。

完整代码

Python
class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        def backtrack(start, path, target):
            if target == 0:
                result.append(list(path))
                return
            if target < 0:
                return
            for i in range(start, len(candidates)):
                if i > start and candidates[i] == candidates[i - 1]:
                    continue  # 跳过重复的数字
                path.append(candidates[i])
                backtrack(i + 1, path, target - candidates[i])  # i + 1 确保每个数字只用一次
                path.pop()  # 回溯

        result = []
        candidates.sort()  # 排序有助于提前终止搜索
        backtrack(0, [], target)
        return result
Java
public class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(candidates); // 排序有助于提前终止搜索
        backtrack(result, new ArrayList<>(), candidates, target, 0);
        return result;
    }

    private void backtrack(List<List<Integer>> result, List<Integer> tempList, int[] candidates, int remain, int start) {
        if (remain == 0) {
            result.add(new ArrayList<>(tempList));
        } else if (remain < 0) {
            return;
        } else {
            for (int i = start; i < candidates.length; i++) {
                if (i > start && candidates[i] == candidates[i - 1]) continue; // 跳过重复的数字
                tempList.add(candidates[i]);
                backtrack(result, tempList, candidates, remain - candidates[i], i + 1); // i + 1 确保每个数字只用一次
                tempList.remove(tempList.size() - 1); // 回溯
            }
        }
    }
}

41. 缺失的第一个正数

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为  O(n) 并且只使用常数级别额外空间的解决方案。

示例 1:

输入:nums = [1,2,0]
输出:3
解释:范围 [1,2] 中的数字都在数组中。

示例 2:

输入:nums = [3,4,-1,1]
输出:2
解释:1 在数组中,但 2 没有。

示例 3:

输入:nums = [7,8,9,11,12]
输出:1
解释:最小的正数 1 没有出现。

解题思路

        这个问题可以通过 "原地哈希" 的方式来解决。原地哈希通常指的是直接在输入数组上进行操作,而不使用额外的空间来存储数据。由于我们只关心最小的未出现的正整数,我们可以尝试将数组中所有的正整数 n 放到数组的 n-1 位置上。这样,如果数组是完全按顺序的(即 nums[i] = i + 1),我们就知道数组中包含了所有从 1n 的正整数,因此未出现的最小正整数就是 n + 1

解题步骤如下:

  1. 遍历数组,对于每个数字,如果它在 1n 的范围内,并且它不在正确的位置上(即 nums[i] != nums[nums[i] - 1]),则将其与正确位置上的数字交换。这个步骤可能需要多次交换才能将一个数字放到正确的位置。
  2. 再次遍历数组,第一个不满足 nums[i] = i + 1 的位置 i,其对应的最小未出现正整数就是 i + 1
  3. 如果所有位置都满足 nums[i] = i + 1,则数组中的最小未出现的正整数是 n + 1

完整代码

Python
class Solution:
    def firstMissingPositive(self, nums: List[int]) -> int:
        n = len(nums)
        for i in range(n):
            while 1 <= nums[i] <= n and nums[nums[i] - 1] != nums[i]:
                nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]
        
        for i in range(n):
            if nums[i] != i + 1:
                return i + 1
        return n + 1
Java
public class Solution {
    public int firstMissingPositive(int[] nums) {
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {
                int temp = nums[nums[i] - 1];
                nums[nums[i] - 1] = nums[i];
                nums[i] = temp;
            }
        }

        for (int i = 0; i < n; i++) {
            if (nums[i] != i + 1) {
                return i + 1;
            }
        }
        return n + 1;
    }
}

标签:39,target,nums,int,41,力扣,candidates,result,数字
From: https://blog.csdn.net/qq_52213943/article/details/136761067

相关文章

  • Exynos4412 Uart Controller
    参考视频:https://www.bilibili.com/video/BV14o4y1Y7A1?p=4&vd_source=432ba293ecfc949a4174ab91ccc526d6 寄存器描述来自Exynos4412User'sManualuart寄存器需要关注的点有:1、如何设置帧格式?2、如何设置uart接收和发送模式?3、如何设置uart的波特率?4、发送和接收都是哪......
  • 力扣周赛第01弹----思维 与 dp
    力扣周赛第01弹----思维与dp一、成为K特殊字符串需要删除的最少字符数题目给你一个字符串word和一个整数k。如果|freq(word[i])-freq(word[j])|<=k对于字符串中所有下标i和j都成立,则认为word是k特殊字符串。此处,freq(x)表示字符x在word中的出现频......
  • 【每日算法】常见AIGC模型; 刷题:力扣单调栈
    上期文章【每日算法】理论:生成模型基础;刷题:力扣单调栈文章目录上期文章一、上期问题二、理论问题1、stablediffusion模型的网络架构2、T5的网络架构(Text-To-TextTransferTransformer模型)3、SDXL模型4、DALLE5、BPE编码6、为什么DDPM加噪声的幅度是不一致的?三、力......
  • 【NC14399】素数判断
    题目素数判断分解质因数思路题目很直接,给你一个数,判断其是不是素数,如果是,输出一句话和它本身,如果不是,输出一句话和它的质因数,需要注意的是质因数要从小到大输出。我们知道,一个素数的质因数就是它本身,所以抛开素数判断,直接对一个数分解质因数就行了。怎么对一个数......
  • IntelliJ IDEA 2023.3 最新发布啦!盘点精彩亮点(关注公众号‘精品应用分享’,输入'idea'
    IntelliJIDEA2023.3的发布标志着AIAssistant的持续发展,它现已超越技术预览阶段,并具有许多令人兴奋的改进。在其他领域,该版本包括对最新Java21功能的全面支持,引入了具有编辑操作的直观浮动工具栏,并添加了“运行到光标”嵌入选项以增强调试工作流程。IntelliJIDEAUltima......
  • Offer必备算法14_哈希表_五道力扣题详解(由易到难)
    目录①力扣1.两数之和解析代码②力扣面试题01.02.判定是否互为字符重排解析代码③力扣217.存在重复元素解析代码④力扣219.存在重复元素II解析代码⑤力扣49.字母异位词分组解析代码本篇完。①力扣1.两数之和1.两数之和难度简单给定一个整数数组 nu......
  • 滴水逆向笔记系列-c++总结3-39.模板-40.引用_友元_运算符重载
    第三十八课c++6模板1.冒泡排序和折半查找voidSort(int*arr,intnLength) { inti; intk; for(i=0;i<nLength-1;i++) { for(k=0;k<nLength-1-i;k++) { if(arr[k]>arr[k+1]) { inttemp=arr[k]; a......
  • 滴水逆向笔记系列-c++总结4-41.new-delete-vector-42.链表
    第四十课c++8new-delete-vector1.内存空间复习在类外函数外的变量就是全局变量,程序一编译地址就已经确定了的临时数据,参数和局部变量就是在堆栈里而使用malloc函数动态申请的则是在堆里2.跟踪调试反汇编函数我们调用malloc函数申请内存,但是不是malloc一个函数完成整个......
  • LibreOJ 4114 「联合省选 2024」迷宫守卫
    因为最后的比较方式是字典序,可以明确肯定是贪心的先让第一个数尽量大,然后是第二个数尽量大,以此类推。考虑到如果选出了第一个数,那么按照其\(\text{dfs}\)的方式,相当于是把根到这个树的链删掉了,变成了许多颗子树,然后在按照\(\text{dfs}\)遍历这几个子树的顺序依次在子树中类似......
  • NEW CONCEPT ENGLISH 41 - 50
    NEWCONCEPTENGLISH41-50Lesson41 Penny'sbagKeywordsandexpressionscheese n. 乳酪,干酪bread n. 面包soap n. 肥皂chocolate n. 巧克力sugar n. 糖coffee n. 咖啡tea n. 茶tobacco n. 烟草,烟丝LanguagepointsIsthatbagheavy,Penny......