TLP-Task04学习笔记


本篇为Datawhale组队学习计划第21期LeetCode精选题目组Task04学习笔记。
初学,时间有点仓促,很多解法没有详细分析,未来会修改,见谅。
所有题目均参考了Datawhale学习文档,开源地址:
https://github.com/datawhalechina/team-learning-program/tree/master/LeetCodeTencent

016 最接近的三数之和

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum-closest

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

示例:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

思路

类似昨天的015三数之和,依然采用【排序+双指针】的解法。
假设确定了枚举的第一个数a,那么剩余两数的和需要尽量接近target-a。为了避免循环,我们先对数组进行排序(不妨按升序):

假设数组的长度为n,我们先枚举a,它在数组中的位置为i;
为了防止重复枚举,我们在位置 [i+1,n) 的范围内枚举b和c。

借助双指针,我们就可以对枚举的过程进行优化。分析过程类似上次011的长短板。

我们用 pb和 pc分别表示指向b,c的指针,初始时,pb指向位置i+1,即左边界,pc指向位置n−1即右边界。在每一步枚举的过程中,我们用 a+b+c来更新答案,并且:
如果 a+b+c ≥ target那么就将pc向左移动一个位置;
如果 a+b+c <t arget那么就将pb向右移动一个位置。

实际上,pb和pc就表示了我们当前可以选择的数的范围,而每一次枚举的过程中,我们尝试边界上的两个元素,根据它们与 target的值的关系,选择「抛弃」左边界的元素还是右边界的元素,从而减少了枚举的范围。这种思路与 11. 盛最多水的容器 中的双指针解法也是类似的。

Python实现

代码及注释来自官方题解,题解中也提到了其他的小优化。

class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        nums.sort()
        n = len(nums)
        best = 10**7
        
        # 根据差值的绝对值来更新答案
        def update(cur):
            nonlocal best
            if abs(cur - target) < abs(best - target):
                best = cur
        
        # 枚举 a
        for i in range(n):
            # 保证和上一次枚举的元素不相等
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            # 使用双指针枚举 b 和 c
            j, k = i + 1, n - 1
            while j < k:
                s = nums[i] + nums[j] + nums[k]
                # 如果和为 target 直接返回答案
                if s == target:
                    return target
                update(s)
                if s > target:
                    # 如果和大于 target,移动 c 对应的指针
                    k0 = k - 1
                    # 移动到下一个不相等的元素
                    while j < k0 and nums[k0] == nums[k]:
                        k0 -= 1
                    k = k0
                else:
                    # 如果和小于 target,移动 b 对应的指针
                    j0 = j + 1
                    # 移动到下一个不相等的元素
                    while j0 < k and nums[j0] == nums[j]:
                        j0 += 1
                    j = j0

        return best


#作者:LeetCode-Solution
#链接:https://leetcode-cn.com/problems/3sum-closest/solution/zui-jie-jin-de-san-shu-zhi-he-by-leetcode-solution/
#来源:力扣(LeetCode)
#著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

020 有效的括号

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/valid-parentheses

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。

注意空字符串可被认为是有效字符串。

示例:

输入: “()”
输出: true

输入: “()[]{}”
输出: true

输入: “(]”
输出: false

输入: “([)]”
输出: false

输入: “{[]}”
输出: true

思路

比较常见(和相对简单)的一道题,考察【栈】。官方题解

判断括号的有效性可以使用「栈」这一数据结构来解决。

我们对给定的字符串s进行遍历,当我们遇到一个左括号时,我们会期望在后续的遍历中,有一个相同类型的右括号将其闭合。由于后遇到的左括号要先闭合,因此我们可以将这个左括号放入栈顶。

当我们遇到一个右括号时,我们需要将一个相同类型的左括号闭合。此时,我们可以取出栈顶的左括号并判断它们是否是相同类型的括号。如果不是相同的类型,或者栈中并没有左括号,那么字符串s无效,返回False。为了快速判断括号的类型,我们可以使用哈希映射(HashMap)存储每一种括号。哈希映射的键为右括号,值为相同类型的左括号。

在遍历结束后,如果栈中没有左括号,说明我们将字符串s中的所有左括号闭合,返回True,否则返回False。

其他优化:

注意到有效字符串的长度一定为偶数,因此如果字符串的长度为奇数,我们可以直接返回False,省去后续的遍历判断过程。

Python实现

class Solution:
    def isValid(self, s: str) -> bool:
        if len(s) % 2 == 1:
            return False
        
        pairs = {
    
    
            ")": "(",
            "]": "[",
            "}": "{",
        }
        stack = list()
        for ch in s:
            if ch in pairs:
                if not stack or stack[-1] != pairs[ch]:
                    return False
                stack.pop()
            else:
                stack.append(ch)
        
        return not stack


#作者:LeetCode-Solution
#链接:https://leetcode-cn.com/problems/valid-parentheses/solution/you-xiao-de-gua-hao-by-leetcode-solution/
#来源:力扣(LeetCode)
#著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

021 合并两个有序链表

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-two-sorted-lists

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 :

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

输入:l1 = [], l2 = []
输出:[]
输入:l1 = [], l2 = [0]
输出:[0]

提示:

两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列

思路

这里我们用递归,官解里也讲述了迭代的方法。

我们可以如下递归地定义两个链表里的 merge 操作(忽略边界情况,比如空链表等):
list1[0] + merge(list1[1:],list2) ( if list1[0] < list2[0] )
list2[0] + merge(list1,list2[1:]) ( otherwise)
也就是说,两个链表头部值较小的一个节点与剩下元素的 merge 操作结果合并。

算法

我们直接将以上递归过程建模,同时需要考虑边界情况。

如果 l1 或者 l2 一开始就是空链表 ,那么没有任何操作需要合并,所以我们只需要返回非空链表。否则,我们要判断 l1 和 l2 哪一个链表的头节点的值更小,然后递归地决定下一个添加到结果里的节点。如果两个链表有一个为空,递归结束。

Python实现

class Solution:
    def mergeTwoLists(self, l1, l2):
        if l1 is None: # 判断是否为空/结束
            return l2
        elif l2 is None:
            return l1
        elif l1.val < l2.val: 
        	# 递归,将较小节点与剩下元素的操作合并
            l1.next = self.mergeTwoLists(l1.next, l2)
            return l1
        else:
            l2.next = self.mergeTwoLists(l1, l2.next)
            return l2


#作者:LeetCode-Solution
#链接:https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/he-bing-liang-ge-you-xu-lian-biao-by-leetcode-solu/
#来源:力扣(LeetCode)
#著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜你喜欢

转载自blog.csdn.net/cosima0/article/details/112642535