二十天刷leetcode【hot100】算法- day2[算法python]

222 阅读7分钟

指针

6.三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

a0c8f56e515ea7b452a1eae2cfaa322.png

leetcode 15. 三数之和

题解

该题需要先从小到大排序,后需要建立三个指针,从头到尾完成遍历,第一层遍历决定第一个元素(first),第二层遍历决定第二个元素(second = first + 1)跟第三个元素(third,初始化为最大的元素)。其中对于确认了first后,就要对secondthird进行首尾遍历。原则为:三者相加,大于target则左移third,小于则右移second。另外需要注意,不能输出相同的元素,又因为该数组一开始就经过排序,则可以跳过相等的两个元素。

class Solution:
    def threeSum(self, nums) -> list[list[int]]:
        res = []
        # 先进行排序
        nums.sort()
        for first in range(len(nums)):
            # 第一层遍历
            if first > 0 and nums[first] == nums[first - 1]:
                # 相邻的两个元素相等,则跳过
                continue

            # 第三个元素初始化为排列后的最后一个,即数组最大值
            third = len(nums) - 1
            # target = -nums[first]
            target = -nums[first]

            # 第二个元素初始化为第一个元素的后一个元素
            for second in range(first + 1, len(nums) - 1):
                # 对数组进行《首尾收缩遍历》
                if second > first + 1 and nums[second] == nums[second - 1]:
                    # 相邻的两个元素相等,则跳过
                    continue

                while second < third and nums[second] + nums[third] > target:
                    # 第二第三元素相加大于target,则向头部收缩
                    third -= 1

                if second == third:
                    # 相等了,则直接跳出内层循环
                    break
                elif nums[second] + nums[third] == target:
                    res.append([nums[first], nums[second], nums[third]])
        return res

7.最接近的两数和 - 字节-上海

给出一个数组和一个目标值,找出两个和最接近目标值的子项

题解

数组从大到小排序,双指针首尾收缩遍历,当前两者相加大于目标则收缩尾部,小于则收缩头部,用res记录最接近目标值的值

def testNear(arrNear, targetNear) -> int:
    # 排序
    arrNear.sort()
    left = 0
    right = len(arrNear) - 1
    res = float('inf') # 初始化为正无穷大,用于比较寻找最接近的值

    while left < right:
        # 《首尾收缩遍历》
        temp = arrNear[left] + arrNear[right]
        if temp == targetNear:
            # 如果找到了精确匹配,返回这个和(但根据原逻辑,这里实际上不会执行,因为res未更新)
            # 但由于题目要求返回最接近的值,且这里逻辑上存在问题,我们假设仅更新res
            res = temp
            break # 可选择跳出循环,如果找到精确解则不再继续
        elif temp > targetNear:
            # 尾部收缩
            right -= 1
        else:
            # 头部收缩
            left += 1

        # 更新最接近的值
        if abs(targetNear - temp) < abs(targetNear - res):
            res = temp

    return res

# 测试用例
arrNear = [24, 69, 14, 37]
targetNear = 60
nearP = testNear(arrNear, targetNear)
print({"nearP": nearP}) # 输出: {'nearP': 61} 或其他最接近的值,取决于数组和目标值

8 接雨水 - 腾讯cdg

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

3af1bc90ae8a2f9f4d214a5a15968f0.png leetcode 42. 接雨水

题解

一:动态规划

dp数组记录下标i及其左(leftMax )右(rightMax)边的所有柱子的最大高度。dp数组初始化,leftMax[0] = height[0];rightMax[len - 1] = height[len - 1];。dp数组遍历过程中,左右侧的值与当值的高度进行对比,更新dp数组

leftMax[i] = Math.max(leftMax[i - 1], height[i]);
rightMax[j] = Math.max(rightMax[j + 1], height[j]);

左右侧最高的两条柱子中,矮的那条减自身高度,即为当前柱子能接的水

class Solution:
    def trap(self, height: List[int]) -> int:
        # 动态规划
        length = len(height)
        if length == 0:
            return 0

        # 下标i及其左边的所有柱子的最大高度
        left_max: list[int] = [0] * length
        # 下标i及其右边的所有柱子的最大高度
        right_max: list[int] = [0] * length

        # 初始化
        left_max[0] = height[0]
        right_max[length - 1] = height[length - 1]

        # 更新左侧最大高度
        for i in range(1, length):
            left_max[i] = max(left_max[i - 1], height[i])

        # 更新右侧最大高度
        for j in range(length - 2, -1, -1):
            right_max[j] = max(right_max[j + 1], height[j])

        ans: int = 0

        # 计算每个位置能接的水量
        for k in range(length):
            # 左右侧最高的两条柱子中,矮的那条减自身高度,即为当前柱子能接的水
            ans += min(left_max[k], right_max[k]) - height[k]

        return ans

# 测试用例
# e = trap_dp([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 'a'])
# print({"e": e}) # 输出: {'e': 6}
二、双指针

头尾双指针收缩遍历,详细见代码解

class Solution:
    def trap(self, height: List[int]) -> int:
        # 双指针
        len_height = len(height)

        left = 0
        right = len_height - 1
        # 初始化左右最大值
        left_max = height[0]
        right_max = height[len_height - 1]

        ans = 0
        while left < right:
            # 左侧最大值与左指针当前值相比,更新最大值
            left_max = max(left_max, height[left])
            # 右侧最大值与右指针当前值相比,更新最大值
            right_max = max(right_max, height[right])

            # 移动较矮的指针(因为矮的指针决定能存多少水--木桶原理),累加可以积累的雨水
            if height[left] < height[right]:
                ans += left_max - height[left]
                left += 1
            else:
                ans += right_max - height[right]
                right -= 1

        return ans

# 测试用例
# f = trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1])
# print({"f": f}) # 输出: {'f': 6}

9 无重复字符串的最长子串

给定一个字符串s ,请你找出其中不含有重复字符的 最长 子串的长度。

3696ddbc487bba37d8516621a4debf1.png leetcode 3.无重复字符串的最长子串

题解

双指针搭配set, 用set去重,左指针移动, set移除元素,右指针移动,且右指针元素不存在于set中,则加入set,最后根据左右指针位置更新最大长度

def length_of_longest_substring(s: str) -> int:  
    # 左右指针  
    # 用set去重  
    set_new = set()  
    left = 0  # 初始化左指针  
    right = -1  # 右指针从-1开始,方便处理第一个字符  
    ans = 0  
  
    while right + 1 < len(s):  
        # 尝试移动右指针  
        if s[right + 1] not in set_new:  
            set_new.add(s[right + 1])  
            right += 1  
            # 更新最长无重复字符子串的长度  
            ans = max(ans, right - left + 1)  
        else:  
            # 当右指针指向的字符已存在于set中时,移动左指针  
            set_new.remove(s[left])  
            left += 1  
  
    return ans  
  
# 测试用例  
g = length_of_longest_substring('abcabcbb')  
print({"g": g})  # 输出: {'g': 3}

10 找到字符串中所有字母异位词

给定两个字符串 sp,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

a484cc3bc15eb0ca3263d5fe3d554af.png leetcode 438.找到字符串中所有字母异位词

题解

使用《位置数组》记录当前值在英文字母中的顺序。初始化遍历先把p字符串和s下标0pLen - 1的子串遍历完。如这个时候如果位置数组相等,则把下标0推进数组。后将s剩下的数据遍历完成。

def find_anagrams(s: str, p: str) -> list:  
    s_len = len(s)  
    p_len = len(p)  

    if s_len < p_len:  
        # s长度小于p长度,s中不存在p的异位词子串  
        return []  

    ans = []  
    # 初始化位置数组(字符计数)  
    s_count = [0] * 26  
    p_count = [0] * 26  

    # 初始化p和s的前p_len个字符的计数  
    for i in range(p_len):  
        s_count[ord(s[i]) - ord('a')] += 1  
        p_count[ord(p[i]) - ord('a')] += 1  

    # 检查初始窗口是否是异位词  
    if s_count == p_count:  
        ans.append(0)  

    # 滑动窗口检查s的剩余部分  
    for i in range(1, s_len - p_len + 1):  
        # 移除窗口最左边的字符  
        s_count[ord(s[i - 1]) - ord('a')] -= 1  
        # 添加窗口最右边的字符(注意索引)  
        s_count[ord(s[i + p_len - 1]) - ord('a')] += 1  

        # 检查新窗口是否是异位词  
        if s_count == p_count:  
            ans.append(i)  

    return ans  

# 测试用例  
arr = find_anagrams('cbaebabacd', 'abc')  
print({"arr": arr})  # 输出: {'arr': [0, 6]}

关注我的公众号,回复 100905A2 获取hot100算法在线链接

公众号.jpg