力扣热题100刷题笔记[python]

letcode100

题录地址:
https://leetcode.cn/studyplan/top-100-liked/

注:另外有记忆精简版 [LeetCode热题100_记忆版.md](file:///D:/yingl/文件/notes_-yl/技术精品文章/编程基本功/算法资料汇总/LeetCode热题100_记忆版.md)

哈希

两数之和

思路:
0、用 hash_table ={1: 0, 2:1} 保存值与下标
1、遍历所nums,如果 target - val 不在hash_table中,将当前val和index 增加或刷新到hash_table中;如果 target - val 在hash_table中,就直接返回

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        hash_table = defaultdict(int)
        for index, val in enumerate(nums):
            if target - val in hash_table:
                return [hash_table[target - val], index] # 如果在hash_table中就直接返回
            if val not in hash_table:
                hash_table[val] = index

49 字母异位词分组

思路:
1 使用 hash_dict = defaultdict(list) 保存hash相同的list
2 使用hash_val += hash(item)*hash(item)*hash(item) 的方式来计算hash

from collections import defaultdict

class Solution(object):
    def groupAnagrams(self, strs):
        """
        :type strs: List[str]
        :rtype: List[List[str]]
        """
        re = []
        def calHash(stri):
            hash_val = 0
            for item in stri:
                hash_val += hash(item)*hash(item)*hash(item)
            return hash_val

        hash_dict = defaultdict(list)
        for item in strs:
            hash_val = calHash(item)
            hash_dict[hash_val].append(item)

        for key, val_list in hash_dict.items():
            re.append(val_list)
        return re

128. 最长连续序列

思路:
1、遍历所有元素。以每一个元素为起点连续搜索,直到没有。搜索的时候用 key in的方式去做。可以使用hash表也可以直接使用一个set
2、去重的好处是防止重复遍历
3、此外这里使用 if num - 1 not in num_set: 来进行剪枝,这段代码的意思是判断是否为起始节点

class Solution(object):
    def longestConsecutive(self, nums):
        longest_streak = 0 # 保存当前最大的连续序列
        num_set = set(nums)

        for num in num_set:
            if num - 1 not in num_set:
                current_num = num
                current_streak = 1

                while current_num + 1 in num_set:
                    current_num += 1
                    current_streak += 1 # 保存当前的最大连续序列长度

                longest_streak = max(longest_streak, current_streak)

        return longest_streak

双指针

移动零(简单题)

思路:
1、此题有点像插入排序。左指针维护一个非零的队列,右指针维护一个0的队列
2、

class Solution:
    def moveZeroes(self, nums):
        """
        :type nums: List[int]
        :rtype: None Do not return anything, modify nums in-place instead.
        """
        left = 0 # 第一个指针,left
        for right in range(len(nums)):  # 第二个指针,right,在for循环中实现移动
            # 核心的交换步骤:如果当前right不为0,则交换到左侧,把非0数往左侧移动就对了
            if nums[right]: 
                nums[left], nums[right] = nums[right], nums[left]
                left += 1 # 交换后也移动left

作者:庸才程序员
链接:https://leetcode.cn/problems/move-zeroes/solutions/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

乘最多水的容器

  1. 盛最多水的容器
    我的思路:(但我觉得还是不严谨)
    1 如果将两个指针放在两端,那么 width 只能减少,但问题是左右指针怎么移动
    2 左右指针的移动目标就是朝着 height 增大的方向去移动,所有选择移动其中小的指针

具体算法
构建一个搜索路径,保证搜索的单向性,使用排除法
收尾两个指针,向中间移动,永远移动小指针,直到收尾指针重合

这篇题解和我的思路一模一样:
https://leetcode.cn/problems/container-with-most-water/solutions/11491/container-with-most-water-shuang-zhi-zhen-fa-yi-do

class Solution(object):
    def maxArea(self, height):
        """
        :type height: List[int]
        :rtype: int
        """
        left = 0
        right = len(height) -1
        global_area  = 0
        while left < right and right < len(height):
            currrnt_area = min(height[left], height[right]) * (right - left)
            global_area = max(global_area, currrnt_area)
            if height[left] < height[right]:
                left = left+1
            else:
                right = right - 1
        return global_area

if __name__ == '__main__':
    slution = Solution()
    re = slution.maxArea([1,1])
    print(re)

三数之和

使用双指针
1 首先排序
2 不重复第一个元素(第一个指针) 保证第一个元素不重复
3 不重复的遍历第二个元素(第二个指针,从第一个指针往后), 找到第三个元素
注:其实我觉得 if cur + nums[l] + nums[r] > 0 之后 r可以连续走的

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        if len(nums) < 3: return []
        nums, res = sorted(nums), []
        for i in range(len(nums) - 2):
            cur, l, r = nums[i], i + 1, len(nums) - 1
            if res != [] and res[-1][0] == cur: continue # Drop duplicates for the first time.

            while l < r:
                if cur + nums[l] + nums[r] == 0:
                    res.append([cur, nums[l], nums[r]])
                    while l < r - 1 and nums[l] == nums[l + 1]:
                        l += 1
                    while r > l + 1 and nums[r] == nums[r - 1]:
                        r -= 1
                if cur + nums[l] + nums[r] > 0: # 注:这里为什么不连续接着走呢,其实我觉得是可以的
                    r -= 1
                else:
                    l += 1
        return res

作者:代码随想录
链接:https://leetcode.cn/problems/3sum/solutions/1670261/dai-ma-sui-xiang-lu-dai-ni-gao-ding-ha-x-jzkx/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

滑动窗口

无重复最长子串

题解:
用一个滑窗和hash
具体算法
1、while循环(左窗口<=窗口 and 右窗口小于字符串长度),判断右窗口指向的元素是否重复,如果不在更新最大值,右窗口++。否则左窗口++,窗口内的元素–
注:
先判断再加进滑窗,不要每次循环都加
先想好伪代码,再开始写代码

from collections import defaultdict
class Solution(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        left, right = 0, 0
        hashmap = defaultdict(int)
        res = 0
        if len(s) == 0:
            return 0
        while left <= right and right < len(s):
            if hashmap[s[right]] > 0:
                hashmap[s[left]] -=1
                left += 1
            else:
                hashmap[s[right]] += 1  # 注:先判断再加,不要先加再判断
                res = max(res, right - left +1)
                right += 1
        return res


438. 找到字符串中所有字母异位词(有技巧性)

题解:
用一个滑窗和hash(hash使用26个字母的编号)
具体算法
1、滑窗长度固定,慢慢平移。每滑动一格重新计算滑窗的编码判断是否相等

注:本题最关键的点在于如何让设计hash,注26个编码的实现方法,使用一个26个字母的list,
或者使用dict但是需要提前把所有的值都赋值好,不能动态增加

class Solution(object):
    def findAnagrams(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: List[int]
        """
        def get_hash(strp):
            '''
            返回26个字母的编码
            :param s:
            :return:
            '''
            re_hash = [0]*26
            for i in range(0, len(strp)):
                re_hash[ord(strp[i])-ord('a')] += 1
            return re_hash
        re = []
        p_hash = get_hash(p)
        left = 0
        right = len(p)-1
        s_hash = get_hash(s[left:right+1])
        while right < len(s):
            if left > 0:
                s_hash[ord(s[right])-ord('a')] += 1
            if s_hash == p_hash:
                re.append(left)
            s_hash[ord(s[left])-ord('a')] -= 1
            right += 1
            left += 1
        return re

子串

和为k的子数组 ( 前缀和)

  1. 和为 K 的子数组
    题解:
    1、先求解前缀和 : pre_sum_list[i] = pre_sum_list[i-1] + nums[i-1]
    2、最后就变成了在前缀和序列中求解两数之差为k的数量 ps:区间和:pre_sum_list[j]-pre_sum_list[i] = [i, j)

注:前缀和
pre_sum_list[0] 为 0
pre_sum_list[1] 为 num[0]
pre_sum_list[2] 为 num[0] + num[1]
pre_sum_list[5] 为 num[0] num[1] num[2] num[3] num[4]

区间和:pre_sum_list[j]-pre_sum_list[i] = [i, j)

class Solution(object):
    def subarraySum(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        re_sum = 0

        # 获取前缀和 pre_sum_list[1] = pre_sum_list[0] + nums[0]
        pre_sum_list = [0]*(len(nums)+1)
        for i in range(1, len(pre_sum_list)):
            pre_sum_list[i] = pre_sum_list[i-1] + nums[i-1]

        # 获取两数之和 pre_sum_list[j]-pre_sum_list[i] = [i, j)
        pre_sum_map = defaultdict(int) # 使用dict而不是set
        for i in range(0, len(pre_sum_list)):
            target = pre_sum_list[i] - k
            re_sum += pre_sum_map[target] 
            pre_sum_map[pre_sum_list[i]] += 1
        return re_sum

滑动窗口最大值(困难 单调队列 双端队列)

使用:单调队列
参考资料:https://www.bilibili.com/video/BV1bM411X72E/?vd_source=d8ef884ad74a2afc063f9ca90338ca3c
题解:
这里的单调队列有一个特点,只有后来的且比他小的才能加进去。因为前面这些值一定不是解

1、遍历nums,每遍历一个元素,将队列中比它小的全从尾部弹出来,将该元素入队
2、将超出k的左边部分出队,小于k时不需要出队
3、队首就是解。将队首加入结果数组

from collections import deque


class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        ans = []
        q = deque()
        for i, x in enumerate(nums):
            
            #右入 将比当前小的元素都弹出来,右边弹出
            while q and nums[q[-1]] <= x:
                q.pop()
            q.append(i)

            # 左出
            if i-q[0]>=k:  # 将超出滑窗范围的元素弹出,这种做法是因为小于k时不需要出队
                q.popleft() # 左边弹出
            if i >= k-1: # 刚开始滑窗大小未到k
                ans.append(nums[q[0]]) # 将当前
        return ans

最小覆盖子串(困难 )

参考这个题解
https://leetcode.cn/problems/minimum-window-substring/solutions/258513/tong-su-qie-xiang-xi-de-miao-shu-hua-dong-chuang-k

题解:
1、不断增加j使滑动窗口增大,直到窗口包含了T的所有元素
2、不断增加i使滑动窗口缩小,因为是要求最小字串,所以将不必要的元素排除在外,使长度减小,直到碰到一个必须包含的元素,这个时候不能再扔了,再扔就不满足条件了,记录此时滑动窗口的长度,并保存最小值
3 让i再增加一个位置,这个时候滑动窗口肯定不满足条件了,那么继续从步骤一开始执行,寻找新的满足条件的滑动窗口,如此反复,直到j超出了字符串S范围。

from collections import Counter

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        ans_left, ans_right = -1, len(s)
        left = 0
        cnt_s = Counter()  # s 子串字母的出现次数,初始化方法
        cnt_t = Counter(t)  # t 中字母的出现次数
        for right, c in enumerate(s):  # 移动子串右端点
            cnt_s[c] += 1  # 右端点字母移入子串
            while cnt_s >= cnt_t:  # 涵盖
                if right - left < ans_right - ans_left:  # 找到更短的子串
                    ans_left, ans_right = left, right  # 记录此时的左右端点
                cnt_s[s[left]] -= 1  # 左端点字母移出子串
                left += 1  # 移动子串左端点
        return "" if ans_left < 0 else s[ans_left: ans_right + 1]

普通数组

53. 最大子数组和

动态规划
核心公式:dp[i] = max(nums[i], dp[i-1]+nums[i])

class Solution(object):
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        dp = [0] * len(nums)
        dp[0] = nums[0]
        max_sum = nums[0]
        for i in range(1, len(nums)):
            dp[i] = max(nums[i], dp[i-1]+nums[i])
            max_sum = max(max_sum, dp[i])
        return  max_sum

作者:YingL
链接:https://leetcode.cn/problems/maximum-subarray/solutions/2676294/53-zui-da-zi-shu-zu-he-by-user5776-6kds/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

合并区间

题解:
1、排序
2、依次加入数组,加入之前和最后一个元素进行对比。如果有交叉就合并然后加入数组

class Solution(object):
    def merge(self, intervals):
        if len(intervals) == 0:
            return []

        intervals.sort()
        re = []
        re.append(intervals[0])
        for i, item in enumerate(intervals):
            if item[0] <= re[-1][1]:
                last = re.pop()
                re.append([last[0], max(last[1], item[1])]) # 合并然后加入数组
            else:
                re.append(item)
        return re

轮转数组

题解:
1、保存前面的的n-k个元素
2、删除前面的n-k个元素, 只剩下k个元素了

注意:不能使用 nums = last_k 这样无法修改nums中的内容

import copy
from collections import Counter

class Solution:
    def rotate(self, nums, k):
        k = k % len(nums)
        retote = nums[:len(nums)-k] #  保存前面的的n-k个元素
        last_k = nums[len(nums)-k:] #  保存前面的的n-k个元素
        last_k.extend(retote)
        nums[:] = last_k # 注意:不能使用 nums =  last_k

238. 除自身以外数组的乘积

使用前缀乘积和后缀乘积。
题解:
1、 提前将从左到右的所有乘积和从右到左的所有乘积保存下来,然后进行相乘。

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        # 前缀乘积
        pre, sum = [1], 1
        for i in nums:
            sum = sum * i
            pre.append(sum)
        # 后缀乘积
        suf, sum = [1], 1
        for i in range(len(nums)):
            sum = sum * nums[len(nums) - i - 1]
            suf.append(sum)
        # 求解
        ans = []
        for i in range(len(nums)):
            total = pre[i] * suf[len(nums) - i - 1]
            ans.append(total)
        return ans

作者:Nebula
链接:https://leetcode.cn/problems/product-of-array-except-self/solutions/1820552/238-by-nebula-a0-c06n/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


作者:YingL
链接:https://leetcode.cn/problems/product-of-array-except-self/solutions/2676286/238-chu-zi-shen-yi-wai-shu-zu-de-cheng-j-1pnp/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

缺失的第一个正整数(困难-我感觉很简单)

数组长度定了,正整数就定了,所以就可以从小到大遍历正整数,看看哪个最先不在原数组中。具体而言,假设我的数组长度为5,那么就应该出现12345,遍历12345,看哪个不在数组中,如果都在就是满足条件,返回6
题解;
1、遍历一遍数组,使用hash_table 保存所有出现过值,用于判断该值是否存在
2、从1数到len(num) 看哪个值不存在

class Solution(object):
    def firstMissingPositive(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        s = defaultdict(int)
        for item in nums:
            s[item] += 1

        for i in range(1,len(nums)+1):
            if s[i] == 0:
                return i
        return len(nums)+1

作者:YingL
链接:https://leetcode.cn/problems/first-missing-positive/solutions/2678840/que-shi-de-di-yi-ge-zheng-shu-by-user577-vmna/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

矩阵

矩阵置零

题解:
1、遍历一遍矩阵,记录有0的行与列
2、将相关行与列全部置为0

class Solution:
    def setZeroes(self, matrix):
        m, n = len(matrix), len(matrix[0])
        row, col = [False] * m, [False] * n

        for i in range(m):
            for j in range(n):
                if matrix[i][j] == 0:
                    row[i] = col[j] = True
        
        # 一次遍历即可
        for i in range(m):
            for j in range(n):
                if row[i] or col[j]: # 如果在目标行或者目标列,就置为0
                    matrix[i][j] = 0

作者:YingL
链接:https://leetcode.cn/problems/set-matrix-zeroes/solutions/2678862/ju-zhen-zhi-ling-by-user5776-u6gd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

54.螺旋矩阵(这个题代码量较大)

讲述看到这一题的思路
两个方向,四个边界
模拟寻路
1、右边界,上边界下移,j不变,i向下
2、下边界,右边界左移,i不变,j向左
3、左边界,下边界上移,j不变,i向上
4、上边界,左边界右移,i不变,j向右

作者:YingL
链接:https://leetcode.cn/problems/spiral-matrix/solutions/2679067/54-luo-xuan-ju-zhen-by-user5776-e2em/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution(object):
    def spiralOrder(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        i_index, j_index = 0, 0
        re = []
        letf_bound, righ_bound = 0, len(matrix[0])-1
        up_bound, down_bound = 0, len(matrix)-1
        drj , dri = 1,0 #  drj左右方向 dri上下方向
        while letf_bound <= righ_bound and up_bound <= down_bound:
            re.append(matrix[i_index][j_index])
            # 右边界,上边界下移,j不变,i向下
            if drj ==1 and j_index == righ_bound:
                    drj = 0 
                    dri = 1
                    up_bound += 1
            # 下边界,右边界左移,i不变,j向左
            if dri == 1 and  i_index == down_bound:
                    drj = -1
                    dri = 0
                    righ_bound -= 1
            # 左边界,下边界上移,j不变,i向上
            if drj ==-1 and j_index == letf_bound:
                    drj = 0
                    dri = -1
                    down_bound -= 1

            # 上边界,左边界右移,i不变,j向右
            if dri == -1 and  i_index == up_bound:
                    drj = 1
                    dri = 0
                    letf_bound += 1

            i_index = i_index + dri
            j_index = j_index + drj
        return re


if __name__ == '__main__':
    re=Solution().spiralOrder([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
    print(re)

作者:YingL
链接:https://leetcode.cn/problems/spiral-matrix/solutions/2679067/54-luo-xuan-ju-zhen-by-user5776-e2em/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

48.旋转图像(找规律)

题解:
1、找规律 第一行变成倒数第一列
matrix_new[j][n - i - 1] = matrix[i][j]

注:最后返回的时候要用切片法 matrix[:] = matrix_new

class Solution:
    def rotate(self, matrix):
        n = len(matrix)
        # Python 这里不能 matrix_new = matrix 或 matrix_new = matrix[:] 因为是引用拷贝
        matrix_new = [[0] * n for _ in range(n)]
        for i in range(n):
            for j in range(n):
                matrix_new[j][n - i - 1] = matrix[i][j]
        # 不能写成 matrix = matrix_new
        matrix[:] = matrix_new

作者:YingL
链接:https://leetcode.cn/problems/rotate-image/solutions/2679100/48-xuan-zhuan-tu-xiang-by-user5776-tx9x/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

搜索二维矩阵(从右上角的点)

讲述看到这一题的思路
1、从右上角的点开始找,如果大于target就向左,小于target就向下

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        m, n = len(matrix), len(matrix[0])
        x, y = 0, n - 1
        while x < m and y >= 0:
            if matrix[x][y] == target:
                return True
            if matrix[x][y] > target:
                y -= 1
            else:
                x += 1
        return False

作者:YingL
链接:https://leetcode.cn/problems/search-a-2d-matrix-ii/solutions/2679113/240-sou-suo-er-wei-ju-zhen-by-user5776-8aka/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

链表

160.相交链表(使用hash方法)

双指针法不太好懂。这里推荐使用hash方法
题解:
1、遍历A,将所有的节点使用set存起来,
2、顺序遍历B 判断是第一个在set中的节点返回

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:

        # 创建一个散列表,用以记录遍历经过的节点
        visited = set()
        
        # 遍历第一个list,并将所有node放入set
        temp = headA
        while temp:
            visited.add(temp)
            temp = temp.next

        # 遍历第二个list,并检查是否有任何node在set中
        temp = headB
        while temp:
            if temp in visited:
                # 发现交叉,返回交叉
                return temp
            temp = temp.next

        # 没有发现交叉
        return None

作者:YingL
链接:https://leetcode.cn/problems/intersection-of-two-linked-lists/solutions/2679230/160-xiang-jiao-lian-biao-by-user5776-gnj6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

206.反转链表(平行赋值法)

题解:
1、使用python的语法糖 cur.next, pre, cur = pre, cur, cur.next #平行赋值法
2、一次遍历i即可

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        cur, pre = head, None
        while cur:
            cur.next, pre, cur = pre, cur, cur.next #平行赋值法
        return pre

作者:YingL
链接:https://leetcode.cn/problems/reverse-linked-list/solutions/2679241/206-fan-zhuan-lian-biao-by-user5776-46zl/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

234.回文链表(反转数组进行对比)

### Python LeetCode Practice Problems and Solutions #### Example Problem: Subsets (LeetCode 78) A common problem encountered when practicing algorithms with Python on LeetCode involves generating all possible subsets of a given set of distinct integers. The provided code snippet demonstrates an effective method for solving this issue through backtracking. ```python from typing import List class Solution: def subsets(self, nums: List[int]) -> List[List[int]]: def backtrack(first=0, curr=[]): # If the combination is completed if len(curr) == k: output.append(curr[:]) return for i in range(first, n): # Add nums[i] into the current combination curr.append(nums[i]) # Use next integers to complete the combination backtrack(i + 1, curr) # Backtrack curr.pop() output = [] n = len(nums) for k in range(n + 1): backtrack() return output ``` This implementation uses recursion alongside iteration to explore every potential subset while maintaining efficiency[^3]. When approaching such problems, one should consider multiple approaches ranging from brute-force methods to more optimized ones. For instance, discussing alternative strategies can include mentioning how another approach might offer trade-offs between time and space complexities[^2]. For example, instead of using backtracking, one could also implement iterative logic based on bit manipulation or dynamic programming principles depending upon specific constraints mentioned within each challenge description. In addition to mastering these techniques, participating actively in online communities dedicated to coding challenges like Stack Overflow or Reddit's r/learnprogramming forums provides valuable insights as well as opportunities for collaborative learning experiences where individuals share their knowledge freely under open-source values[^1].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值