Python LeetCode题

1、双遍历

给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 k ,请你返回满足 0 <= i < j < n ,nums[i] == nums[j] 且 (i * j) 能被 k 整除的数对 (i, j) 的 数目 。

我的解答

class Solution(object):
    def countPairs(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        count = 0
        n = len(nums)
        for i in range(n):
            for j in range(i+1, n):
                if nums[i] == nums[j] and (i * j) % k == 0:
                    count += 1
        print(count)
        return count



if __name__ == "__main__":
    nums = input("请输入整数数组(用空格分隔): ")
    k = int(input("请输入整数 k: "))
    solution = Solution()
    solution.countPairs(nums, k)

运行错误,原因如下

输入处理错误:输入的数组未被正确转换为整数列表,导致后续比较和索引计算错误。

 nums = list(map(int, input("请输入整数数组(用空格分隔): ").split()))

2、两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

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

我的答案:


class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        for i in range(len(nums)):
            j = target - nums[i]
            for j in range(len(nums)):
                if i != j:
                    return i,j

错误:

在代码中,j 被重新定义为循环变量,这会覆盖之前计算出的 j = target - nums[i] 的值,导致无法正确找到目标值对。


class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        for i in range(len(nums)):
            j = target - nums[i]
            # 遍历数组寻找 j 的值
            for k in range(len(nums)):
                if k != i and nums[k] == j:
                    return [i, k]
        return []

3、字母异位词分组

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

思路:

初始化一个空的 defaultdict,称之为 d。
遍历输入字符串数组 strs:
对于第一个字符串 "eat",排序后为 "aet",将它加入到键为 "aet" 的列表中。
对于第二个字符串 "tea",排序后也为 "aet",因此也加入到键为 "aet" 的列表中。
对于第三个字符串 "tan",排序后为 "ant",将它加入到键为 "ant" 的列表中。
对于第四个字符串 "ate",排序后为 "aet",同样加入到键为 "aet" 的列表中。
对于第五个字符串 "nat",排序后为 "ant",同样加入到键为 "ant" 的列表中。
对于最后一个字符串 "bat",排序后为 "abt",将它加入到键为 "abt" 的列表中。
最终,我们得到的 d 字典如下所示:
{
'aet': ['eat', 'tea', 'ate'],
'ant': ['tan', 'nat'],
'abt': ['bat']
}
将 d 字典的所有值转换为列表并返回,即 [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]。

将排序后的字符串作为哈希表的键,原始字符串作为值分组。

重点:

  • sorted(s):将字符串s的字符按ASCII值排序,返回列表(如"cab" → ['a', 'b', 'c'])。

  • ''.join(...):将排序后的字符列表合并为字符串(如['a', 'b', 'c'] → "abc")。

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        #创建一个默认值为空列表的字典 d
        d = defaultdict(list)
        for s in strs:
            # 对字符串排序生成唯一键
            key = ''.join(sorted(s))
            d[key].append(s)
        return list(d.values())

4、最长连续序列

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

思路:

  1. 哈希集合初始化:将输入数组转换为集合 num_set,以便快速查询数字是否存在。

  2. 遍历集合元素:逐个检查集合中的每个数字。

  3. 确定起点:若当前数字的前一个数字不在集合中,则该数字是一个连续序列的起点。

  4. 计算序列长度:从起点开始,逐步检查后续数字是否存在,统计当前连续序列的长度。

  5. 更新最大值:每次找到更长的序列时,更新最大长度。

class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        num_set = set(nums)
        max_length = 0
        
        for num in num_set:
            # 检查当前数字是否是连续序列的起点
            if num - 1 not in num_set:
                current_num = num
                current_length = 1
                
                # 向后查找连续的数字
                while current_num + 1 in num_set:
                    current_num += 1
                    current_length += 1
                
                # 更新最大长度
                max_length = max(max_length, current_length)
        
        return max_length

5、移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

思路:

两个新数组,遍历数组nums,遇到非0加入数组nums1,遇到0加入数组num2,最后连接nums1和nums2。

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        slow = 0
        nums1 = []
        for i in range(len(nums)):
            if nums[i] != 0:
                nums1.append(nums[i])
                slow += 1
        for s in range(slow,len(nums)):
            nums1.append(0)
        for i in range(len(nums)):
            nums[i] = nums1[i]

该思路正确且运行测试成果,但用到了新的数组且使用了三次for循环,下面的方法会更好。

class Solution:
    def moveZeroes(self, nums: list[int]) -> None:
        # 双指针法:将非零元素前移,剩余位置补零
        slow = 0
        # 遍历数组,将非零元素移到前面
        for fast in range(len(nums)):
            if nums[fast] != 0:
                nums[slow] = nums[fast]
                slow += 1
        # 将剩余位置填充零
        for i in range(slow, len(nums)):
            nums[i] = 0

6、 盛最多水的容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

思路:考虑底部长度和高度的乘积为容量,计算最大值。

class Solution:
    def maxArea(self, height: List[int]) -> int:
        capability = 0
        for i in range(len(height)):
            for j in range(i,len(height)):
                if height[i] > height[j]:
                    min = height[j]
                else:
                    min = height[i]
                result = (j - i) * min
                if result > capability:
                    capability = result
        return capability
        

缺点时间复杂度太高了,优化思路为双指针法

  • 使用两个指针 leftright,分别指向数组的起始位置和结束位置。

  • 每次计算两个指针之间的面积,更新最大面积。

  • 然后移动较短的指针(因为移动较短的指针可能找到更高的柱子,从而增加面积)

class Solution:
    def maxArea(self, height: List[int]) -> int:
        left, right = 0, len(height) - 1
        max_area = 0
        
        while left < right:
            # 计算当前左右指针之间的面积
            current_area = (right - left) * min(height[left], height[right])
            max_area = max(max_area, current_area)
            
            # 移动较短的指针
            if height[left] < height[right]:
                left += 1
            else:
                right -= 1
        
        return max_area

7、三数之和

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

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

me思路:三次循环找到符合条件的三元组。

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        for i in range(len(nums)-2):
            j = i+1
            k = j+1
            for j in range(i+1,len(nums)-1):
                for k in range(j+1,len(nums)):
                    if nums[i] + nums[j] + nums[k] == 0:
                        return [i,j,k]

存在问题:

1. 返回的是索引而不是数值三元组。

2. 三层循环效率低下,且没有处理重复组合。

3. 在找到第一个符合条件的组合后就返回,无法收集所有可能的结果。

4. 没有对数组进行排序,导致重复组合难以处理。

正确的解决方案应该是:

1. 先对数组进行排序。

2. 遍历数组,固定第一个数nums[i]。

3. 跳过重复的nums[i]。

4. 使用双指针法在i+1到末尾之间寻找另外两个数,使得三数之和为0。

5. 当找到符合条件的组合时,加入结果列表,并跳过重复的左右指针值。

6. 处理边界情况,如数组长度不足3的情况。

一定要考虑一下双指针,综上发现双指针思想主要为两端考虑,可以减小时间复杂度。比如该题确定第一个数后,剩下两个数使用双指针,一个为i+1,一个从n-1开始,结合实际情况判断要移动哪个指针,这样可节省很多时间。

成功代码:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()  # 先排序
        n = len(nums)
        result = []
        for i in range(len(nums)-2):
            if i>0 and nums[i] == nums[i-1]:  #确定第一个元素,略过相同元素
                continue
            j = i+1
            k = n-1
            while j<k:
                if nums[i] + nums[j] + nums[k] < 0:
                    j +=1
                elif nums[i] + nums[j] + nums[k] > 0:
                    k -=1
                else:
                    result.append([nums[i], nums[j], nums[k]])
                    while j<k and nums[j] == nums[j+1]:  #跳过相同左元素
                        j +=1
                    while j<k and nums[k] == nums[k-1]:  #跳过相同右元素
                        k -=1
                    j += 1  #移动元素
                    k -= 1
        return result

8、接雨水(难)

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

示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

me思路:

遍历,若第i个高度大于第i+1个的高度,把第i个加入新数组,之后按序加入新数组,直到找到大于第i个高度的柱子为止,该数组结束。

计算高度,若左大于右,则雨水量是左柱子高度-右柱子高度。若右>左,则雨水量是右柱子高度-左柱子高度,和即为该坑位的雨水量。计算完毕把雨水量加入总雨水量中,继续遍历下一个数组

错误代码

class Solution:
    def trap(self, height: List[int]) -> int:
        temp = []
        sum = 0
        for i in range(len(height)):
            if len(temp) ==0:
                if height[i]>height[i+1]:
                    temp.append(height[i])
            elif height[i] < temp[0]:
                temp.append(height[i])
            elif height[i] > temp[0]:
                temp.append(height[i])
                #这一轮数组结束,计算水容量后temp清空
                for s in range(len(temp)-1):
                    if temp[s] > temp[s+1] :
                        sum+= temp[s]-temp[s+1]
                    else:
                        sum+= temp[s+1] - temp[s]
                temp=[]
        return sum

错误之处

  1. 索引越界
    当 i = len(height)-1 时,height[i+1] 会导致数组越界(如 i 遍历到最后一个元素时)。

  2. 错误的雨水计算逻辑
    原代码试图通过比较相邻元素的高度差累加雨水,这与接雨水问题的核心逻辑(由两侧最高柱子决定水位)完全不符。

  3. temp 列表的误用
    temp 的逻辑混乱,无法正确记录左右边界,导致无法计算有效区域的雨水。

修改代码

接雨水的核心规则:一个位置能存储的雨水量 = 其左右两侧最高柱子中较矮的那个高度 - 当前柱子高度
(若左右最高柱子中较矮的比当前柱子高,才能存水)

class Solution:
    def trap(self, height: List[int]) -> int:
        if not height:
            return 0
        
        left, right = 0, len(height)-1
        left_max = right_max = 0
        result = 0
        
        while left < right:
            # 移动较小高度的指针
            if height[left] < height[right]:
                # 更新左侧最大值或计算雨水
                if height[left] >= left_max:
                    left_max = height[left]
                else:
                    result += left_max - height[left]
                left += 1
            else:
                # 更新右侧最大值或计算雨水
                if height[right] >= right_max:
                    right_max = height[right]
                else:
                    result += right_max - height[right]
                right -= 1
        
        return result

双指针小结

双指针法的核心思想是维护左右两个指针和左右最大高度,逐步向中间移动。每次移动较小高度的指针,因为储水量由较小高度的一侧决定。具体步骤如下:

  1. 初始化左右指针和左右最大高度。

  2. 当左指针小于右指针时,比较左右当前高度。

  3. 移动较小高度的指针,更新该侧的最大高度,并计算当前指针位置的储水量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值