力扣100刷题笔记[python]

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

哈希

两数之和

思路:
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.回文链表(反转数组进行对比)

先将数值保存到数组中,反转数组进行对比,构造一个列表的反向副本
题解:
1、 遍历链表将数值保存到数组中
2、反转数组看是否相等

class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        vals = []
        current_node = head
        while current_node is not None:
            vals.append(current_node.val)
            current_node = current_node.next
        return vals == vals[::-1]  # 反转数组进行对比,构造一个列表的反向副本

作者:YingL
链接:https://leetcode.cn/problems/palindrome-linked-list/solutions/2679254/234-hui-wen-lian-biao-by-user5776-4lhf/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

141.环形链表(快慢指针)

思路:
快慢指针,快指针两倍速。如果有环,一定会相遇,如果没有相遇就无环。
(2vt-vt) = vt(当vt等于k时一定会相遇,而vt也一定会有等于k的时候)

注:其实我觉得这题也可以用hash表去做,使用一个set

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

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        slow = fast = head
        while fast:
            if fast.next:
                fast = fast.next.next
            else:
                break
            slow = slow.next
            if fast == slow: # 如果有环,一定会相遇,如果没有相遇就无环。
                return True
        return False

142.环形链表 II(二次相遇)

题解
1、第一次相遇时,快指针从头再次出发并降为慢指针,
2、第二次相遇时即为环的起始点

class Solution(object):
    def detectCycle(self, head):
        fast, slow = head, head
        while True:
            if not (fast and fast.next): return # 无环返回none
            fast, slow = fast.next.next, slow.next
            if fast == slow: break # 第一次相遇
        fast = head
        while fast != slow: # 第二次相遇
            fast, slow = fast.next, slow.next
        return fast

作者:YingL
链接:https://leetcode.cn/problems/linked-list-cycle-ii/solutions/2679389/142-huan-xing-lian-biao-ii-by-user5776-zup7/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

合并有序链表

题解:
1、使用一个额外的链表 cur = ListNode(-1)

class Solution(object):
    def mergeTwoLists(self, list1, list2):
        """
        :type list1: Optional[ListNode]
        :type list2: Optional[ListNode]
        :rtype: Optional[ListNode]
        """
        # 若其中任一个链表为空,返回另一个
        if not list1:
            return list2
        if not list2:
            return list1
        res = cur = ListNode(-1)
        while list1 and list2:
            if list1.val>list2.val:
                cur.next = list2
                list2 = list2.next
            else:
                cur.next = list1
                list1 = list1.next
            cur = cur.next
        cur.next = list1 if list1 else list2
        return res.next

两数相加

题解:
其实核心就是 v_new = n1.val + n2.val + target

class Solution:
    def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:

        cur = 0
        n1, n2 = l1, l2
        head = ListNode()
        node = head
        while n1 or n2 or cur:
            if n1:
                cur += n1.val
                n1 = n1.next
            if n2:
                cur += n2.val
                n2 = n2.next
            node.next = ListNode(cur % 10)
            node = node.next
            cur //= 10
        return head.next

删除倒数第N个节点

题解:
1 快慢指针,让一个指针提前走N步

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        dummy = ListNode(0, head)
        first = head
        second = dummy
        for i in range(n): # 将快指针往前多走n步
            first = first.next

        while first: # 两个指针齐头并进
            first = first.next
            second = second.next
        
        second.next = second.next.next # 最后删除特定节点
        return dummy.next

作者:YingL
链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/solutions/2680356/shan-chu-lian-biao-de-dao-shu-di-n-ge-ji-qujq/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

两两交换链表中的节点

题解:
1、使用递归。每次处理两个

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if head is None or head.next is None:  # 递归边界
            return head  # 不足两个节点,无需交换

        node1 = head  # 需要交换的左边节点
        node2 = head.next #需要交换的右边节点

        node3 = node2.next # 需要递归处理的后面节点

        # 交换节点
        node1.next = self.swapPairs(node3)  
        node2.next = node1

        return node2  # 返回交换后的链表头节点

作者:YingL
链接:https://leetcode.cn/problems/swap-nodes-in-pairs/solutions/2680374/liang-liang-jiao-huan-lian-biao-zhong-de-p9ej/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

K个一组翻转链表

题解:
1、使用栈来进行翻转。(易错点:注意将值放到stack里面,二不是把整个node放进去)
2、使用递归来连续翻转。

from typing import Optional
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        stack = []
        h = ListNode(0, head) # 这是一个辅助节点,方便写循环
        node1, node2 = h, h # node1是头部节点,node2是尾部节点
        # 向后移动,将
        for _ in range(k):
            if not node2.next or not node2:
                print_linked_list(head)
                return head
            node2 = node2.next
            stack.append(node2.val) # 注意指针的引用关系非常容易出错,所以这里保存值就行,不要保存指针
        node2.next = self.reverseKGroup(node2.next, k) # 递归调整
        for _ in range(k):
            node1.next.val = stack.pop()
            node1 = node1.next
        print_linked_list(head)
        return head

随机链表复制

排序链表

合并K个升序链表

LRU缓存

二叉树

二叉树的最大深度

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if root is None:
            return 0
        left_depth = self.maxDepth(root.left)
        right_depth = self.maxDepth(root.right)
        depth = max(left_depth, right_depth) + 1
        return depth

作者:YingL
链接:https://leetcode.cn/problems/maximum-depth-of-binary-tree/solutions/2709345/er-cha-shu-de-zui-da-shen-du-by-user5776-jee9/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

翻转二叉树

题解:
1、翻转左边
2、翻转右边
3、将左右进行交换

class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if root is None:
            return root
        self.invertTree(root.left)
        self.invertTree(root.right)
        root.left, root.right = root.right, root.left
        return root

对称二叉数(有点难)

这题其实是如何判断两个树是否对称,dfs齐头并进。中轴对称的意思是每一层是要对称的。而不是节点内部需要对称
判断两个树是否对称,使用dfs。这题容易被误解成判断两个树是相等的了

class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        if root is None:
            return True
        return self.dfs(root.left, root.right)

    # 判断两课树是否对称,相当于搞了两个指针,走到对应的节点时,判断左右是否对称
    def dfs(self, root1: Optional[TreeNode], root2: Optional[TreeNode])-> bool:
        if root1 is None and root2 is None:
            return True
        if root1 is None and root2 is not None:
            return False
        if root2 is None and root1 is not None:
            return False
        if root1.val != root2.val:
            return False
        return self.dfs(root1.left, root2.right) and self.dfs(root1.right, root2.left)

作者:YingL
链接:https://leetcode.cn/problems/symmetric-tree/solutions/2709422/101-dui-cheng-er-cha-shu-by-user5776-78u5/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

二叉树的直径(有技巧性)

题解
1、使用一个全局的 max_depth 保存全局的最大路径
2、但每次返回的当前节点的深度(从底向上的深度)


class Solution:
    def __init__(self):
        self.max_depth = 0
    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
        if root is None:
            return 0
        left_depth = self.dfs(root.left)
        right_depth = self.dfs(root.right)
        self.max_depth = max(self.max_depth, left_depth + right_depth)
        return self.max_depth

    def dfs(self, root: Optional[TreeNode]) -> int:
        '''
        获取当前节点的最大深度
        :param root:
        :return:
        '''
        if root is None:
            return 0
        left_dept = self.dfs(root.left)
        right_dept = self.dfs(root.right)
        self.max_depth = max(self.max_depth, left_dept+right_dept)  # 使用一个全局的  max_depth 保存全局的最大路径
        return max(left_dept, right_dept) + 1

作者:YingL
链接:https://leetcode.cn/problems/diameter-of-binary-tree/solutions/2709660/gooder-cha-shu-de-zhi-jing-by-user5776-um96/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

二叉树层次遍历

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

本题的难度主要在于如何识别每一层
题解:
1、 外层循环判断队列是否结束
2、里层循环只遍历队列长度

class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root: return []
        res, queue = [], collections.deque()
        queue.append(root)
        while queue: # 队列不为空
            tmp = []
            cur_len = len(queue)
            for _ in range(cur_len): # 虽然queue在一直增加,但是 len(queue) 首次计算之后就不会再变了
                node = queue.popleft()
                tmp.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            res.append(tmp)
        return res

作者:YingL
链接:https://leetcode.cn/problems/binary-tree-level-order-traversal/solutions/2712963/102-er-cha-shu-de-ceng-xu-bian-li-by-use-j773/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

108. 将有序数组转换为二叉搜索树(构思巧妙)

平衡二叉树的特征是左右深度差不能超过1
二叉搜索树的特点是左小右大,二叉搜索树的中序遍历是升序序列
平衡二叉搜索树的特点是 ,它的中序遍历为升序序列,且根节点在升序序列的中间。基于这个特点,每次取中间的节点做根节点(保证左边的左右深度差不超过1),然后递归构建一棵树即可。

这里构建的方法有三种,最后构建出的都是平衡二叉搜索树:
方法一:中序遍历,总是选择中间位置左边的数字作为根节点
方法二:中序遍历,总是选择中间位置右边的数字作为根节点
方法二:中序遍历,随机选择中间位置左边或者右边数字作为根节点

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        def dfs(left, right):
            if left > right:
                return None

            # 总是选择中间位置左边的数字作为根节点
            mid = (left + right) // 2

            root = TreeNode(nums[mid])
            root.left = dfs(left, mid - 1)
            root.right = dfs(mid + 1, right)
            return root

        return helper(0, len(nums) - 1)

作者:力扣官方题解
链接:https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/solutions/312607/jiang-you-xu-shu-zu-zhuan-huan-wei-er-cha-sou-s-33/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


作者:YingL
链接:https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/solutions/2712989/108-jiang-you-xu-shu-zu-zhuan-huan-wei-e-k2ef/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

验证二叉搜索树

使用递归,遍历每一个节点时,要求是左节点要小于当前节点,右节点要大于当前节点

class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        def helper(node, lower = float('-inf'), upper = float('inf')) -> bool:
            if not node:
                return True
            
            val = node.val
            if val <= lower or val >= upper:
                return False

            if not helper(node.right, val, upper):
                return False
            if not helper(node.left, lower, val):
                return False
            return True

        return helper(root)


230. 二叉搜索树中第K小的元素

思路:
在二叉搜索树中,任意子节点都满足“左子节点 <<< 根节点 <<< 右子节点”的规则。因此二叉搜索树具有一个重要性质:二叉搜索树的中序遍历为递增序列。
所以本题就是中序遍历第k个元素

代码

class Solution:
    def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
        def dfs(root):
            if not root: return
            dfs(root.left)
            if self.k == 0: return
            self.k -= 1
            if self.k == 0: self.res = root.val
            dfs(root.right)

        self.k = k
        dfs(root)
        return self.res

作者:Krahets
链接:https://leetcode.cn/problems/kth-smallest-element-in-a-bst/solutions/2361685/230-er-cha-sou-suo-shu-zhong-di-k-xiao-d-n3he/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

199. 二叉树的右视图

题解:
其实就是层次遍历,使用层次遍历获取层次遍历的最后一个点
1 队列中保存值和值的深度
queue = deque([(root, 0)])
2、rightmost_value_at_depth[depth] = node.val 字典中保存每个depth的最后节点值

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def rightSideView(self, root: TreeNode) -> List[int]:
        rightmost_value_at_depth = dict() # 深度为索引,存放节点的值
        max_depth = -1

        queue = deque([(root, 0)])
        while queue:
            node, depth = queue.popleft()

            if node is not None:
                # 维护二叉树的最大深度
                max_depth = max(max_depth, depth)

                # 由于每一层最后一个访问到的节点才是我们要的答案,因此不断覆盖 rightmost_value_at_depth[depth] = val
                rightmost_value_at_depth[depth] = node.val

                queue.append((node.left, depth + 1))
                queue.append((node.right, depth + 1))

        return [rightmost_value_at_depth[depth] for depth in range(max_depth + 1)]



将二叉树转化为链表

这种做法性能超过68%的人

题解:
1、使用数组获取先序遍历
2、将先序遍历数组拼成链表

class Solution:
    def flatten(self, root: TreeNode) -> None:
        preorderList = list()

        def preorderTraversal(root: TreeNode):
            if root:
                preorderList.append(root)
                preorderTraversal(root.left)
                preorderTraversal(root.right)
        
        preorderTraversal(root)
        size = len(preorderList)
        for i in range(1, size):
            prev, curr = preorderList[i - 1], preorderList[i]
            prev.left = None
            prev.right = curr


105. 从前序与中序遍历序列构造二叉树

参考:https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solutions/2361558/105-cong-qian-xu-yu-zhong-xu-bian-li-xu-4lvkz

题解
1、先序遍历的第一个节点就是根节点
2、定位根节点的index,以该节点位置为界划分左右子树。
注:这里先遍历一遍中序遍历的数列,提前缓存所有节点的index值

class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
def recur(root, left, right):
if left > right: return # 递归终止
node = TreeNode(preorder[root]) # 建立根节点
i = dic[preorder[root]] # 划分根节点、左子树、右子树
node.left = recur(root + 1, left, i - 1) # 开启左子树递归
node.right = recur(i - left + root + 1, i + 1, right) # 开启右子树递归
return node # 回溯返回根节点

    dic, preorder = {}, preorder
    for i in range(len(inorder)):
        dic[inorder[i]] = i
    return recur(0, 0, len(inorder) - 1)

路径总和(困难 确实细节很多)

前缀和+回溯+hashtabe+两数之和

题解:
1、使用前序遍历。每遍历到一个节点时,计算当前路径的路径和。并把当前和加到一个hashtable种,使用两数之和的方法求解是否存在一个target
2、基于当前路径和 currSum 与 prefixSum 开始回溯左分支和右分支
3、完成当前节点的处理后,进行回溯,即将当前前缀和的计数减1。要将自己的痕迹抹除

class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> int:
        # 初始化路径计数器
        self.count = 0
        # 初始化前缀和字典,用于记录各个前缀和出现的次数
        prefixSum = {0: 1}  # 前缀和为0的路径默认有一个,即不选择任何节点的情况
        
        def dfs(node, currSum):
            # 如果当前节点为空,直接返回,不做任何处理
            if not node:
                return
            
            # 更新当前路径的累计和
            currSum += node.val
            # 计算从当前节点开始,是否存在一条路径的和为targetSum
            oldSum = currSum - targetSum
            # 如果oldSum在前缀和字典中,说明存在一条有效路径,将其出现次数加到总计数器中
            self.count += prefixSum.get(oldSum, 0)
            
            # 将当前前缀和加入到字典中,或更新其出现次数
            prefixSum[currSum] = prefixSum.get(currSum, 0) + 1
            
            # 递归访问当前节点的左右子节点
            dfs(node.left, currSum)
            dfs(node.right, currSum)
            
        
            prefixSum[currSum] -= 1
        
        # 从根节点开始进行深度优先搜索
        dfs(root, 0)
        return self.count


二叉树的最近公共祖先

题解:
1、dfs(root, p, q) 递归判断 p,q是不是root的子孙

class Solution:

    def __init__(self):
        self.parent = None

    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        def dfs(root: 'TreeNode', p: 'TreeNode', q: 'TreeNode'):
            # 判断当前root是否为p或q的祖宗节点
            if not root:
                return False
            if root.val == p.val or root.val == q.val:
                return True
            is_left = dfs(root.left, p, q)
            is_right = dfs(root.right, p, q)
            if is_left and is_right:  # 如果正好是公共节点就把结果保存下来,这个结果是唯一的
                self.parent = root
            return is_left or is_right  # 左右节点只要有一个包含就说明是他的祖宗

        # 如果p是q的子孙节点,那么q是公共节点
        if dfs(q, p, p): self.parent = q
        if dfs(p, q, q): self.parent = p
        dfs(root, p, q)
        return self.parent

贪心算法

买卖股票的最佳时机(1次遍历)

思路:
没用贪心,一次遍历。每次都保存迄今为止的最大值和最小值

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        min_val = 10000
        max_val = 0
        for item in prices:
            max_val = max(max_val, item-min_val)
            min_val = min(min_val, item)
        
        return max_val

作者:YingL
链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/solutions/2745769/mai-mai-gu-piao-de-zui-jia-shi-ji-1ci-bi-zqqb/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

跳跃游戏

思路
动态规划,没用贪心
判断之前的每一个点是否有跳跃能


class Solution:
    def canJump(self, nums: List[int]) -> bool:
        n = len(nums)
        dp = [False] * len(nums)
        dp[0] = True  # 注意初始化
        for j in range(1, n):
            for i in range(1, j+1):
                # print(j-i, nums[j-i], dp[j-i]) # 这里适合打断点调试
                if nums[j-i] >= i and dp[j-i] == True:
                    dp[j] = True
                    break

        return dp[-1]


作者:YingL
链接:https://leetcode.cn/problems/jump-game/solutions/2745890/55-tiao-yue-you-xi-dong-tai-gui-hua-by-u-t86t/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

跳跃游戏 II

思路:
1、每次在上次能跳到的范围(end)内选择一个能跳的最远的位置(其实就是遍历到end之前)
2、当 i 到达 end的时候,下次起跳的最大位置也确定下来了。你不用管是从哪个点跳过去的,反正是只跳一步就行。此时 step++,代表我往后跳了。注意这里并不代表是从end跳的,而是从上一个范围中跳的
3、继续往后遍历。如果i走完了,end还没达到,那就说明上一次的跳跃就已经可以跳到最后了

我的写法
class Solution:
    def jump(self, nums: List[int]) -> int:
        if len(nums) == 1:
            return 0
        step , end_index = 0, 0
        max_index = 0
        for i, val in enumerate(nums):
            max_index = max(nums[i]+i, max_index)
            if i == end_index:
                end_index = max_index
                step += 1 # 表示先跳一步
                if end_index >= len(nums)-1:
                    # 如果上次跳跃可以到达数据结尾,那就无需再遍历了
                    return step
        return step


作者:YingL
链接:https://leetcode.cn/problems/jump-game-ii/solutions/2750768/tiao-yue-you-xi-ii-by-user5776-i4bl/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

763. 划分字母区间

思路
在一个分组的字母有这样一个规律,最后一个位置一定是分组中的某个字母的最后一次出现位置。
本题只需要一次遍历即可

算法步骤:
1 遍历所有元素,每次遍历都求取当前字母的最后一个位置,一旦index = last就说明,当前的分组刚好结束。
2、当index = last时 初始化所有条件继续遍历

注意求组字母的最后一个位置时,从后往前寻找

class Solution:
    def partitionLabels(self, s: str) -> List[int]:
        def find_last(s, target):
            for i in range(len(s)-1, -1,-1):
                if s[i] == target:
                    return i
            return -1
        index = 0
        re = []
        result = []
        last = find_last(s, s[index])
        while index < len(s):
            tmp = find_last(s, s[index])
            last = max(last, tmp)
            result.append(s[index])
            if index == last:
                re.append("".join(result))
                result = []
            index += 1
        return [len(item) for item in re]

作者:YingL
链接:https://leetcode.cn/problems/partition-labels/solutions/2758473/763-hua-fen-zi-mu-qu-jian-by-user5776-e3gh/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

动态规划

70. 爬楼梯

思路:
1、 dp[n]表示第n级台阶有多少种方法。要么从n-1跳,要么从n-2跳,所以有
dp[i] = dp[i-1] + dp[i - 2]
2、 引入一个dp[0] = 1

class Solution:
    def climbStairs(self, n: int) -> int:
        dp = [1] * (n+1)
        for i in range(2, n+1):
            dp[i] = dp[i-1] + dp[i - 2]
        return dp[n]

118. 杨辉三角

思路
感觉不像是一个正儿八经的动态规划

1 每一层的数量其实是确定的,如第三层有3个,第四层有4个 . result = [0] * (i+1) #每一层的数量其实是确定的
2 每次都将首尾两个值赋为1 result[0] = 1 # 将首尾两个值赋为1
3 然后进行迭代即可 result[j] = re[i-1][j-1] + re[i-1][j]

class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        re = [[1], [1,1]]
        if numRows <= 2:
            return re[:numRows]
        for i in range(2, numRows):
            result = [0] * (i+1) #每一层的数量其实是确定的
            result[0] = 1 # 将首尾两个值赋为1
            result[-1] = 1
            for j in range(1,i):
                result[j] = re[i-1][j-1] + re[i-1][j]
            re.append(copy.deepcopy(result))
        return re

作者:YingL
链接:https://leetcode.cn/problems/pascals-triangle/solutions/2755007/118-yang-hui-san-jiao-by-user5776-vxfq/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

198. 打家劫舍

思路
1 dp[i]表示打劫到i间房子的时候最大值为多少,则状态转移如下
dp[i] = max(dp[j]+nums[i], dp[i]),这里的j从[0, i-2],即非相邻的房子跳过来

class Solution:
    def rob(self, nums: List[int]) -> int:
        if len(nums) == 0:
            return  0
        dp = copy.deepcopy(nums)
        max_val = dp[0]
        for i in range(1, len(nums)):
            for j in range(0, i-1):
                dp[i] = max(dp[j]+nums[i], dp[i])
            max_val = max(dp[i], max_val)
        return max_val

作者:YingL
链接:https://leetcode.cn/problems/house-robber/solutions/2755057/198-da-jia-jie-she-by-user5776-yxnw/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

完全平方数

思路
本题和零钱组合很像,但也有个显著差别。零钱是累加,本题不是累加
1、dp初始值为0 dp = [0] * (n+1)
2、遍历n以内的所有平方数jj,min_val = min(dp[i-jj] + 1, min_val)

class Solution:
    def numSquares(self, n: int) -> int:
        dp = [0] * (n+1) # 初始值为0
        dp[1] = 1
        for i in range(1,n+1):
            min_val =  inf # 注意这里需要获取的是 min([np[i-j**2]+1]),所以要使用一个中间变量
            for j in range(1, int((n)**(0.5))+1): # 根号n
                if (i-j*j) < 0:
                    continue
                min_val = min(dp[i-j*j] + 1, min_val) 
            dp[i] = min_val
        return dp[n]



作者:YingL
链接:https://leetcode.cn/problems/perfect-squares/solutions/2755208/279-wan-quan-ping-fang-shu-by-user5776-kvpt/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

零钱兑换

思路:
我们可以使用状态 dp[i] 表示凑成金额 i 所需的最少硬币个数。
如果我们枚举不同面额的硬币 coins[j],那么 dp[i] = 1 + dp[i - coins[j]]。由于 j 有多种取值,
因此 dp[i] = min(1 + dp[i - coins[j]])。

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount + 1] * (amount + 1)    # dp[i]表示凑成金额i所需的最少硬币个数,初始都为-1表示不可凑
        dp[0] = 0      # 金额0需要0枚硬币
        # 枚举每一个金额
        for a in range(1, amount + 1):
            # 枚举每一种硬币
            for c in coins:
                # 假设使用了硬币c,那么最少硬币数就由a-c转移来
                if a - c < 0: continue # 说明当前面值已经超过了amount
                dp[a] = min(dp[a], dp[a - c] + 1)

        return dp[amount] if dp[amount] != amount+1 else -1 # 修正结果

作者:YingL
链接:https://leetcode.cn/problems/coin-change/solutions/2755139/322-ling-qian-dui-huan-by-user5776-lpeb/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最长递增子序列

题解:dp
1、dp[i]表示以i结尾的最长子序列
2、遍历前面的dp[0-i],取他们当中最大的值+1即可


class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if not nums: return 0
        dp = [1] * len(nums)
        for i in range(len(nums)):
            for j in range(i):
                if nums[j] < nums[i]: # 如果要求非严格递增,将此行 '<' 改为 '<=' 即可。
                    dp[i] = max(dp[i], dp[j] + 1) # 注意这里必须要用max 因为可能会有多个递增序列
        return max(dp)

乘积最大子数组


题解:

这里都是整数数组 ,但含有0
1、如果都是正数,则一定有 dp[i] = max(dp[i-1] * nums[i], num[i]) ,为什么呢?如果 12034.从4的角度来看,4要么乘以3 要么不乘,乘3的时候也有两种情况,就是3所能积累的最大值,所以 dp[4] = max(dp[3]x3 ,3)
2 因为这里有正反,所有就有下面这种动态转移方程
3 另外这里省去了dp[i-1]因为他们只需要用一次就行了

class Solution:
    def maxProduct(self, nums: List[int]) -> int:  
        maxF = nums[0]  
        minF = nums[0]  
        ans = nums[0]  
          
        for i in range(1, len(nums)):  
            mx = maxF  
            mn = minF  
            maxF = max(mx * nums[i], max(nums[i], mn * nums[i]))  
            minF = min(mn * nums[i], min(nums[i], mx * nums[i]))  
            ans = max(maxF, ans)  
          
        return ans

二分法

搜索插入位置

有三种写法 :https://leetcode.cn/problems/search-insert-position/solutions/2023391/er-fen-cha-zhao-zong-shi-xie-bu-dui-yi-g-nq23
题解: 闭区间写法

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:

        left, right = 0, len(nums)-1
        while left <= right:
            mid = left + (right - left) // 2
            if nums[mid] == target:
                return mid
            if nums[mid] < target:
                left = mid +1
            if nums[mid] > target:
                right = mid -1
        if nums[mid] > target:
            return mid
        else:
            return mid + 1


二维矩阵搜索

题解:
从右上角开始搜索,往一边会变小,往另一边会变大,找能不能遇到target

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

在排序数组中查找第一个和最后一个

首个大于等于四种策略

本题有两种思路,1、二分法+while 2、两次二分法
题解:两次二分法
1、结束条件发生了变化
2、right/left的变更条件也发生了变化

from typing import List


class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        def searchLeft(nums, target):
            left, right = 0, len(nums)-1
            while left <= right:
                mid = left + (right - left) // 2

                if nums[mid] == target and (mid == 0 or nums[mid - 1] < target):
                    return mid

                if nums[mid] < target:
                    left = mid +1
                elif nums[mid] >= target:
                    right = mid -1

            return -1
        def searchRight(nums, target):
            left, right = 0, len(nums)-1
            while left <= right:
                mid = left + (right - left) // 2
                if nums[mid] == target and (mid == len(nums)-1 or nums[mid + 1] > target):
                    return mid
                if nums[mid] <= target:
                    left = mid +1
                elif nums[mid] > target:
                    right = mid -1

            return -1

        return [searchLeft(nums, target), searchRight(nums, target)]

搜索旋转排序数组

题解:双重二分法
1、左右区间端点不一样,先判断在左区间还是右区间,左右区间端点不一样
2、在左区间,永远和 nums[0] 比, 但是 mid = (l + r) // 2,其他和二分法一样
3、右区间永远和 nums[len(nums) - 1] 比 但是 mid = (l + r) // 2, 其他和二分法一样

from typing import List
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if not nums:
            return -1
        l, r = 0, len(nums) - 1
        while l <= r:
            mid = (l + r) // 2
            if nums[mid] == target:
                return mid
            # 在左区间
            if nums[0] <= nums[mid]: # 在左区间,永远和 nums[0] 比
                if nums[0] <= target < nums[mid]:
                    r = mid - 1
                else:
                    l = mid + 1
            # 在右区间
            else:
                if nums[mid] < target <= nums[len(nums) - 1]:
                    l = mid + 1
                else:
                    r = mid - 1
        return -1


搜索旋转数组最小值

题解:
变形的二分查找法
1、根据左右区间适当变更 左右指针
2、当mid=low=high的时候就说明在最低点重合了

class Solution:
  def findMin(self, nums: List[int]) -> int:
      low, high = 0, len(nums) - 1
      while low <= high:
          mid = low + (high - low) // 2
          if nums[mid] > nums[high]:
              # 在左区间 此时 low=mid+1因为mid绝对不可能是最小值
              low = mid + 1
          elif nums[low] == nums[high]:
              # 相等的情况下一定是在最低点相遇
              return nums[mid]
          else:
              # 在右区间此时 hign = mid因为high有可能是最小值
              high = mid
      return nums[low]

回朔

全排列

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        result = []
        re = []
        visited = [False] * len(nums)
        def dfs(result):
            if len(result) == len(nums):
                re.append(copy.deepcopy(result))
                return
            for i in range(len(nums)):
                if visited[i]:
                    continue
                result.append(nums[i])
                visited[i] = True
                dfs(result)
                result.pop()
                visited[i] = False

        dfs(result)
        return re

子集

题解:
1、使用strat将起始点往后移动
2、

1 12 123
2 23
3


class Solution:

    def subsets(self, nums: List[int]) -> List[List[int]]:
        result = []
        visited = [False]*len(nums)
        re = [[]]
        def dfs(start):
            for i in range(start, len(nums)):
                if not visited[i]:
                    result.append(nums[i])
                    re.append(copy.deepcopy(result))
                    visited[i] = True
                    dfs(i+1)
                    result.pop()
                    visited[i] = False
        dfs(0)
        return re


电话号码组合

题解:
1 这题和全排列很像,但是没有visited


class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        letter_map = {
            '2': 'abc',
            '3': 'def',
            '4': 'ghi',
            '5': 'jkl',
            '6': 'mno',
            '7': 'pqrs',
            '8': 'tuv',
            '9': 'wxyz'
        }
        reslut = []
        re = []
        def backtracking(i):
            if len(reslut) == len(digits):
                re.append("".join(reslut))
                return
            sss = letter_map[digits[i]]
            for j in range(len(sss)):
               reslut.append(sss[j])
               backtracking(i+1)
               reslut.pop()

        if len(digits) > 0:
            backtracking(0)
        return re

组合总数

题解:
1、这题和全排列很像,但是没有visited

class Solution:


    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        result = []
        re = []
        def get_sum(result):
            sum_num = 0
            for item in result:
                sum_num+=item
            return sum_num
        def backtrack():
            if get_sum(result) == target:
                re.append(copy.deepcopy(result))
            elif get_sum(result) > target:
                return
            for i in range(len(candidates)):
                result.append(candidates[i])
                backtrack()
                result.pop()

        backtrack()

        ddd = list(map(lambda item: tuple(sorted(item)), re))
        ss = list(set(ddd))
        ss2 = list(map(lambda item: list(item), ss))
        return ss2


单词搜索

题解:
1 记录是否访问,遍历上下左右四个方向,然后回退自己的修改
2、每次回溯都从底向上判断是否满足。对于每条搜索链必须每个回溯的点都是true
3、最后的结果数组中必须至少存在一条是满足的

import copy
from typing import List


class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:

        visited = [[False] * len(board[0]) for _ in range(len(board))]
        def dfs(i, j, k):
            if not 0 <= i < len(board) or not 0 <= j < len(board[0]) or board[i][j] != word[k] or visited[i][j]:
                return False
            if k == len(word) - 1:
                return True
            #从底向上  遍历四个方向必须有一个为true才行
            visited[i][j] = True
            res = dfs(i + 1, j, k + 1) or dfs(i - 1, j, k + 1) or dfs(i, j + 1, k + 1) or dfs(i, j - 1, k + 1)
            visited[i][j] = False
            return res

        for i in range(len(board)):
            for j in range(len(board[0])):
                if dfs(i, j, 0): return True
        return False


22. 括号生成

题解:回溯+剪枝
1、这里的减枝就是,记录当前的左括号的数量和右括号的数量,然后判断 if right > left: return

from typing import List
class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        res = []
        cur_str = ''
        def dfs(cur_str, left, right):
            if left == n and right == n:
                res.append(cur_str)
                return
            if right > left: # 剪枝
                return
            if left < n:
                dfs(cur_str + '(', left + 1, right)
            if right < n:
                dfs(cur_str + ')', left, right + 1)

        dfs(cur_str, 0, 0)
        return res


分割回文串(感觉很难)

题解:枚举子串结束位置
1、回溯切割 如 abc将有如下切割方法
[a b c], [a bc]
[ab c]
[abc ]
2、if t == t[::-1]: # 判断是否回文

from typing import List


class Solution:
    def partition(self, s: str) -> List[List[str]]:
        ans = []
        path = []
        n = len(s)
        def dfs(i: int) -> None:
            if i == n: # 这个结束判断很关键
                ans.append(path.copy())  # 复制 path
                return
            for j in range(i, n):  # 枚举子串的结束位置
                t = s[i: j + 1]
                if t == t[::-1]:  # 判断是否回文
                    path.append(t)
                    dfs(j + 1)
                    path.pop()  # 恢复现场
        dfs(0)
        return ans

图论

岛屿数量

dfs
1 遍历数组,遇见1就进行深度搜索
2、每一次深度搜索都把相连的1都修改为‘#’

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        sum = 0
        def dfs(i, j):
            if grid[i][j] != '1':
                return
            grid[i][j] = '#'
            directions = [[1,0],[0,1],[-1,0],[0,-1]]
            for ii,jj in directions:
                i_new, j_new = i + ii,j+jj
                if i_new in range(n) and j_new in range(m):
                    dfs(i_new, j_new)

        n,m = len(grid), len(grid[0])
        for i in range(n):
            for j in range(m):
                if grid[i][j] == '1':
                    dfs(i, j)
                    sum +=1
        return sum

腐烂的橘子

Problem: 994. 腐烂的橘子

目录

思路

使用层次遍历的方式去做

1、先将所有腐烂的橘子入队列
2、计算队列的长度len,出队len次,将相关的新鲜橘子赋值为2然后入队列
3、再次计算队列的长度len,出队len次,再次将相关的新鲜橘子赋值为2然后入队列
4、重复3直到队列为空。判断是否全部腐烂

Code


from collections import deque
import heapq
from typing import List

class Solution:
    def orangesRotting(self, grid: List[List[int]]) -> int:
        que= deque()
        sum = 0 # 最终腐烂的橘子数量
        orange_num = 0 #所有的橘子数量
        mintues = 0  # 分钟数
        direction = [[1,0], [0, 1], [-1,0],[0, -1]]
        n, m = len(grid), len(grid[0])
        visited = [[False for _ in range(m)] for _ in range(n)]
        for i in range(0, n):
            for j in range(0, m):
                if grid[i][j] == 2:
                    que.append((i, j))
                    visited[i][j] = True
                if grid[i][j] != 0:
                    orange_num += 1
        while que:
            length = len(que)
            sum += length
            mintues += 1
            for _ in range(length):
                curi, curj = que.popleft()
                for i, j in direction:
                    curi_new, curi_new = curi + i, curj+j
                    if curi_new in range(0, n) and curi_new in range(0, m) and grid[curi_new][curi_new] == 1 and not visited[curi_new][curi_new]:
                        grid[curi + i][curj + j] = 2
                        que.append((curi+i, curj+j))
                        visited[curi+i][curj+j] = True
        if orange_num == 0:
            return 0
        if sum != orange_num:
            return -1
        return mintues-1



if __name__ == '__main__':
    grid = [[2,1,1],[1,1,0],[0,1,1]]
    Solution().orangesRotting(grid)


课程表

拓扑排序
1、ru_map = {item:0 for item in range(numCourses) } # 字典推导,这里初始化值为0 # 优化点 du = [0]*numCourses #存放每个结点的入读
2、ddd = [[] for _ in range(numCourses)] #存放每个结点的子结点
3、result = [] # 进入过队列的所有点

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        ru_map = {item:0 for item in range(numCourses) }  # 这里初始化值为0 # 优化点 du = [0]*numCourses  #存放每个结点的入读
        ddd = [[] for _ in range(numCourses)]    #存放每个结点的子结点
        result = []  # 进入过队列的所有点
        for item in prerequisites:
            n1, n2 = item
            ru_map[n1] += 1
            ddd[n2].append(n1)
        que = deque([key for key,val in ru_map.items() if val == 0])
        while que:
            cur = que.popleft()
            result.append(cur)
            for item in ddd[cur]:
                ru_map[item] -= 1
                if ru_map[item] == 0:
                    que.append(item)
        return len(result) == numCourses

作者:YingL
链接:https://leetcode.cn/problems/course-schedule/solutions/2745954/207-ke-cheng-biao-by-user5776-6nxn/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

前缀树

插入过程:
遍历所有的字符,如果已经在树上就获取该节点的儿子节点,如果不在就创建一个新的节点。

注: self.son = defaultdict(Node) 这行代码有两个作用。1、新增节点时会创建一个新的node 2 代表每个节点可能有多个子节点且关键字不一样

查询过程:
遍历所有的字符,如果已经在树上就获取该节点的儿子节点,继续找,如果找不到就报false,如果全找到了但不是最终节点,也会报错

startwith过程:
和查询过程一样,如果全找到了但不是最终节点,不会报错

作者:YingL
链接:https://leetcode.cn/problems/implement-trie-prefix-tree/solutions/2754175/208-shi-xian-trie-qian-zhui-shu-by-user5-smyf/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


class Node:
    __slots__ = 'son','isend'
    def __init__(self):
        self.son = defaultdict(Node)  # 子节点。每个节点可能有多个子节点且关键字不一样
        self.isend = False  # 标记该节点是否为尾节点

class Trie:
    def __init__(self):
        self.root = Node()        

    def insert(self, word: str) -> None:
        cur = self.root
        for char in word:
            cur = cur.son[char] # 新增节点时会创建一个新的node
        cur.isend = True # 将最后一个节点的isend赋值为true
        
    def search(self, word: str) -> bool:
        cur = self.root
        for char in word:
            if char not in cur.son:
                return False
            cur = cur.son[char]
        if cur.isend:
            return True
        else:
            return False

    def startsWith(self, prefix: str) -> bool:
        cur = self.root
        for char in prefix:
            if char not in cur.son:
                return False
            cur = cur.son[char]
        return True

作者:YingL
链接:https://leetcode.cn/problems/implement-trie-prefix-tree/solutions/2754175/208-shi-xian-trie-qian-zhui-shu-by-user5-smyf/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

多维动态规划

62. 不同路径

法一: 多维动态规划
每一个格子的位数等于其左边和上边的位数之和。

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        dp = [[1]*m for _ in range(n)]

        for i in range(1,n):
            for j in range(1,m):
                dp[i][j] = dp[i-1][j] + dp[i][j-1]
        return dp[-1][-1]

作者:小天才才
链接:https://leetcode.cn/problems/unique-paths/solutions/2705807/62-bu-tong-lu-jing-by-xtcc-2doc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最小路径和

题解:
多维动态规划
1、dp[i][j]代表到当前格点所走的最小值
2、dp[i][j]的值由上节点或者左节点过来

import copy
from typing import List


class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        n, m = len(grid), len(grid[0])
        dp = copy.deepcopy(grid)

        # 初始化边缘
        for j in range(1, m):
            dp[0][j] = dp[0][j] + dp[0][j-1]
            
        # 初始化边缘
        for i in range(1, n):
            dp[i][0] = dp[i][0] + dp[i-1][0]

        # 每一个格点代表到当前格点所走的最小值
        for i in range(1, n):
            for j in range(1, m):
                dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + dp[i][j]
        return dp[-1][-1]

最长回文字串(中心扩展)

题解:
中心扩展法

class Solution:
    def expandAroundCenter(self, s, left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return left + 1, right - 1

    def longestPalindrome(self, s: str) -> str:
        start, end = 0, 0
        for i in range(len(s)):
            left1, right1 = self.expandAroundCenter(s, i, i)
            left2, right2 = self.expandAroundCenter(s, i, i + 1)
            
            # 取最长
            if right1 - left1 > end - start:
                start, end = left1, right1
            if right2 - left2 > end - start:
                start, end = left2, right2
        return s[start: end + 1]


最长公共子序列[(n + 1) * (m+1)的二维数组 ]

题解:
1617411822-KhEKGw-image

dp 为 (n + 1) * (m+1)的二维数组
1、dp[i][j] 表示 text1[0:i]和 text2[0:j]的最长公共子序列的长度。
2、状态转移方程
dp[i][j] = dp[i - 1][j - 1] + 1 当 text1[i - 1] == text2[j - 1]:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) 当 text1[i - 1] != text2[j - 1]:
3 边界均为0, 注这里的 dp 为 (n + 1) * (m+1)的二维数组

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        m, n = len(text1), len(text2)
        dp = [[0] * (n + 1) for _ in range(m + 1)]
        
        for i in range(1, m + 1):
            for j in range(1, n + 1):
                if text1[i - 1] == text2[j - 1]:
                    dp[i][j] = dp[i - 1][j - 1] + 1
                else:
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
        
        return dp[m][n]

作者:力扣官方题解
链接:https://leetcode.cn/problems/longest-common-subsequence/solutions/696763/zui-chang-gong-gong-zi-xu-lie-by-leetcod-y7u0/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

编辑距离[(n + 1) * (m+1)的二维数组 ]

题解:
计算每一步的最小步数,然后慢慢转移
多维dp
1、dp[i][j] 代表 word1 到 i 位置转换成 word2 到 j 位置需要最少步数(word1[0:i] 和 word2[0:j])
2、 状态转移
当 word1[i] == word2[j],dp[i][j] = dp[i-1][j-1];(无需任何操作)
当 word1[i] != word2[j],dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1 (替换,删除,插入任选一种)

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        n1 = len(word1)
        n2 = len(word2)
        dp = [[0] * (n2 + 1) for _ in range(n1 + 1)]
        # 第一行
        for j in range(1, n2 + 1):
            dp[0][j] = dp[0][j-1] + 1
        # 第一列
        for i in range(1, n1 + 1):
            dp[i][0] = dp[i-1][0] + 1
        for i in range(1, n1 + 1):
            for j in range(1, n2 + 1):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    dp[i][j] = min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1] ) + 1
        #print(dp)      
        return dp[-1][-1]

作者:powcai
链接:https://leetcode.cn/problems/edit-distance/solutions/6455/zi-di-xiang-shang-he-zi-ding-xiang-xia-by-powcai-3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

技巧

136. 只出现一次的数字

方法1:位预算
异或运算:相同为0,不同为1,且满足交换率


class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        x = 0
        for num in nums:  # 1. 遍历 nums 执行异或运算
            x ^= num      
        return x;         # 2. 返回出现一次的数字 x

作者:Krahets
链接:https://leetcode.cn/problems/single-number/solutions/2361995/136-zhi-chu-xian-yi-ci-de-shu-zi-wei-yun-iyd0/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

169. 多数元素

取中位数即可

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        nums.sort()
        return nums[(len(nums)//2)]

颜色分类

单指针一次遍历
遇到0删掉放最左边,指针+1
遇到1不操作,指针+1
遇到2删掉,放最右边,指针不加,因为删了一位。
一开始在列表加了一个3,遇到3代表可以停止了,后面都是2。

作者:-刚好中意♥
链接:https://leetcode.cn/problems/sort-colors/solutions/1732931/by-lx666-87rz4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
https://leetcode.cn/problems/sort-colors/solutions/1732931/by-lx666-87rz4

from typing import List


class Solution:
    def sortColors(self, nums: List[int]) -> None:
        # 使用Counter来计数
        color_count = Counter(nums)

        # 排序颜色,这里我们假设只有三种颜色,0, 1, 2
        # 实际情况中,你可能需要根据具体情况调整排序逻辑
        nums2 = []
        for color in sorted(color_count.keys()):
            # 对于每种颜色,按照计数重复写入数组
            nums2.extend([color] * color_count[color])
        nums[:] = nums2
				
作者:-刚好中意♥
链接:https://leetcode.cn/problems/sort-colors/solutions/1732931/by-lx666-87rz4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

下一个排列

题解:
以排列 [4,5,2,6,3,1]为例:
1、当我们完成交换后排列变为 [4,5,3,6,2,1]
2、重排「较小数」右边的序列6 2 1 ,序列变为 [4,5,3,1,2,6]

class Solution:
		def nextPermutation(self, nums: List[int]) -> None:
				i = len(nums) - 2
				// 从后向前找到凸点
				while i >= 0 and nums[i] >= nums[i + 1]:
						i -= 1


				if i >= 0:
						// 找到升序序列中最后一个小于降序序列中的值 2  < 3
						j = len(nums) - 1
						while j >= 0 and nums[i] >= nums[j]:
								j -= 1
						// 交换这两个值
						nums[i], nums[j] = nums[j], nums[i]

				// 反转后面的序列即可
				left, right = i + 1, len(nums) - 1
				while left < right:
						nums[left], nums[right] = nums[right], nums[left]
						left += 1
						right -= 1

作者:力扣官方题解
链接:https://leetcode.cn/problems/next-permutation/solutions/479151/xia-yi-ge-pai-lie-by-leetcode-solution/
	来源:力扣(LeetCode)
	著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

寻找重复数

题解:
1、从小到大遍历i,如果没有重复,可以将数据通过交换的方式排列成123
2、如何有重复,会发生循环交换的问题。且此时i>num[i]

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        i = 0
        # 如果没有重复,可以将数据通过交换的方式排列成123
        while i < len(nums):
            if nums[i] == i:
                i += 1
                continue
            if nums[nums[i]] == nums[i]: return nums[i] # 此时i>num[i]
            nums[nums[i]], nums[i] = nums[i], nums[nums[i]]  # 将i处的值换到它对应idx = num[i]的地方去,一直到i == num[i]为止
        return -1


posted @ 2024-05-07 21:35  英击长空  阅读(553)  评论(0)    收藏  举报