力扣hot100(python解析)

数组双指针哈希

1. 俩数之和

俩数之和
数组、哈希表

题目:
在这里插入图片描述
思路:
在这里插入图片描述
代码:

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        map_dict = dict()
        for index,value in enumerate(nums):
            if (target - value) in map_dict:
                return [map_dict[target - value],index]
            else:
                map_dict[value] = index
        return []

2. 移动零

移动零
数组、双指针

题目:
在这里插入图片描述
代码:

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        fast = 0
        slow = 0
        while fast < n:
            if nums[fast] != 0:
                nums[slow] = nums[fast]
                slow += 1
            fast += 1
        while slow < n:
            nums[slow] = 0
            slow += 1

3. 无重复字符的最长子串

无重复字符的最长字串
哈希表、字符串、滑动窗口

题目:
在这里插入图片描述
思路:
采用集合来判断是否有重复字符

代码:

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        occ = set()
        result = 0
        rk = 0
        for i in range(len(s)):
            if i != 0:
                occ.remove(s[i-1])
            while rk < len(s) and s[rk] not in occ:
                occ.add(s[rk])
                rk += 1
            result = max(result,rk - i)
        return result 

4. 和为K的子数组

和为K的子数组
数组
哈希表
前缀和

题目:
在这里插入图片描述
思路:
前缀和+哈希表

代码:

'''
1. collections.defaultdict(int):defaultdict 是 dict 的一个子类,它允许指定一个默认的工厂函数,用来生成默认值。
   在这里,我们指定的默认值工厂函数是 int,意味着当你访问一个不存在的键时,会返回一个 int 类型的默认值,即 0。
'''
class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        ans = 0
        presum = collections.defaultdict(int)   # 创建了一个默认值为 0 的 defaultdict 对象,用于存储前缀和及其出现的次数
        presum[0] = 1   # 初始化前缀和为 0 的个数为 1
        cur_presum = 0  # 初始化当前前缀和为 0,用来记录当前位置的前缀和

        for n in nums:
            cur_presum += n
            ans += presum[cur_presum-k] # 累加 cur_presum - k 在 presum 中出现的次数
            presum[cur_presum] += 1     # 更新 presum[cur_presum] 的值,表示当前前缀和出现的次数加 1
        return ans

5. 最大子数组和

最大子数组和
数组
分治
动态规划

题目:
在这里插入图片描述
思路:

采用贪心策略,如果 -2 1 在一起,计算起点的时候,一定是从 1 开始计算,因为负数只会拉低总和,这就是贪心贪的地方!
局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。
全局最优:选取最大“连续和”

代码:

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        result = float('-inf')
        count = 0
        for i in range(len(nums)):
            count += nums[i]
            if count > result:
                result = count
            if count <= 0:
                count = 0
        return result

6. 矩阵置零

矩阵置零
数组
哈希表
矩阵

题目:
在这里插入图片描述
思路:
在这里插入图片描述
代码:

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        m = len(matrix)
        n = len(matrix[0])
        m_list = [False] * m
        n_list = [False] * n
        for i in range(m):
            for j in range(n):
                if matrix[i][j] == 0:
                    m_list[i] = True
                    n_list[j] = True
        for i in range(m):
            for j in range(n):
                if m_list[i] or n_list[j]:
                    matrix[i][j] = 0

7. 相交链表

相交链表
题目:
在这里插入图片描述
思路:
在这里插入图片描述
在这里插入图片描述
代码:

# 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) -> Optional[ListNode]:
        A = headA
        B = headB
        while A != B:
            if A:
                A = A.next
            else:
                A = headB
            if B:
                B = B.next
            else:
                B = headA               
        return A

8. 字母异位词分组

字母异位词分组
题目:
在这里插入图片描述
代码:

'''
思路:
1.先对列表中的字符串进行排序
2.将具有相同字母的字符串以列表的形式放入字典中
3.最后将字典中的值以列表的形式输出
'''

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        n = len(strs)
        record = dict()
        if n == 0:return [strs]
        for i in strs:
            s = str(sorted(i))
            if s not in record:
                record[s] = [i]
            else:
                record[s].append(i)
            
        return list(record.values())

9. 最长连续序列

最长连续序列
数组 哈希表

题目:
在这里插入图片描述
思路:
在这里插入图片描述
代码:

class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        ans = 0     # 记录最长连续序列的长度
        record = set(nums)     # 记录nums中的所有数值
        for num in record:
            # 如果当前的数是一个连续序列的起点,统计这个连续序列的长度
            if (num - 1) not in record:
                count = 1     # 连续序列的长度,初始为1
                while (num + 1) in record:
                    count += 1
                    num += 1    # 不断查找连续序列,直到num的下一个数不存在于数组中
                ans = max(ans, count)     # 更新最长连续序列长度
        return ans

10. 盛最多水的容器

盛最多水的容器
贪心 数组 双指针

题目:
在这里插入图片描述
思路:
在这里插入图片描述
在这里插入图片描述
代码:

class Solution:
    def maxArea(self, height: List[int]) -> int:
        left = 0
        right = len(height) - 1
        res = 0
        while left < right:
            if height[left] < height[right]:
                res = max(res, height[left] * (right - left))
                left += 1
            else:
                res = max(res, height[right] * (right - left))
                right -= 1
        return res

11. 三数之和

三数之和
数组
双指针
排序

题目:
在这里插入图片描述
思路:

双指针法
1)首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。
2)依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。
3)接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
4)如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
时间复杂度:O(n^2)
空间复杂度: O(1)

代码:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        n = len(nums)
        res = []
        for i in range(n):
            if nums[0] > 0:
                return []
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            left = i + 1
            right = n - 1
            while left < right:
                if nums[i] + nums[left] + nums[right] > 0:
                    right -= 1
                elif nums[i] + nums[left] + nums[right] < 0:
                    left += 1
                else:
                    res.append([nums[i],nums[left],nums[right]])
                    while left < right and nums[left + 1] == nums[left]:
                        left += 1
                    while left < right and nums[right - 1] == nums[right]:
                        right -= 1
                    left += 1
                    right -= 1
        return res

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

找到字符串中所有字母异位词
哈希表
字符串
滑动窗口

题目:
在这里插入图片描述
思路:
在这里插入图片描述
在这里插入图片描述
代码:

class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        res = []
        s_num = [0] * 26
        p_num = [0] * 26
        s_len = len(s)
        p_len = len(p)
        if s_len < p_len: return []
        for i in range(p_len):
            s_num[ord(s[i]) - ord('a')] += 1
            p_num[ord(p[i]) - ord('a')] += 1
        if s_num == p_num:res.append(0)
        for i in range(p_len,s_len):
            s_num[ord(s[i]) - ord('a')] += 1
            s_num[ord(s[i - p_len]) - ord('a')] -= 1
            if s_num == p_num:res.append(i - p_len + 1)
        return res

13. 合并区间

合并区间
数组 排序
在这里插入图片描述
思路:
在这里插入图片描述
代码:

class Solution:
    def merge(self, intervals):
        result = []
        if len(intervals) == 0:
            return result  # 区间集合为空直接返回

        intervals.sort()  # 默认按照区间的左边界进行排序

        result.append(intervals[0])  # 第一个区间可以直接放入结果集中

        for i in range(1, len(intervals)):
            if result[-1][1] >= intervals[i][0]:  # 发现重叠区间
                # 合并区间,只需要更新结果集最后一个区间的右边界,因为根据排序,左边界已经是最小的
                result[-1][1] = max(result[-1][1], intervals[i][1])
            else:
                result.append(intervals[i])  # 区间不重叠
        return result

14. 轮转数组

轮转数组
数组
题目:
在这里插入图片描述
思路:
题目的前提是需要在原数组上进行修改,没有返回值
先对数组进行切片,对切片后的数组进行遍历,对原数组进行值的修改

代码:

class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        n = len(nums)
        k = k % n    # 有可能k值会大于数组长度,因此需要对k除n取余
        result1 = nums[n - k:]
        result2 = nums[:n-k]
        for i in range(len(result1)):
            nums[i] = result1[i]       
        for j in range(len(result2)):
            nums[k + j] = result2[j]

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

除自身以为数组的乘积
数组、前缀和
题目:
在这里插入图片描述
思路:

本题的难点在于 不能使用除法 ,即需要 只用乘法 生成数组 ans 。根据题目对 ans[i] 的定义,可列出下图所示的表格。

根据表格的主对角线(全为 1 ),可将表格分为 上三角下三角 两部分。分别迭代计算下三角和上三角两部分的乘积,即可 不使用除法 就获得结果。

下图中 A=numsA  , B=ansB 

在这里插入图片描述
算法流程:
在这里插入图片描述
代码:

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        ans, tmp = [1] * len(nums), 1
        for i in range(1, len(nums)):
            ans[i] = ans[i - 1] * nums[i - 1] # 下三角
        for i in range(len(nums) - 2, -1, -1):
            tmp *= nums[i + 1]                # 上三角
            ans[i] *= tmp                     # 下三角 * 上三角
        return ans

矩阵

1. 螺旋矩阵

螺旋矩阵
数组、矩阵、模拟

题目:
在这里插入图片描述
代码:

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if not matrix: return []
        l, r, t, b, res = 0, len(matrix[0]) - 1, 0, len(matrix) - 1, []
        while True:
            for i in range(l, r + 1): res.append(matrix[t][i]) # left to right
            t += 1
            if t > b: break
            for i in range(t, b + 1): res.append(matrix[i][r]) # top to bottom
            r -= 1
            if l > r: break
            for i in range(r, l - 1, -1): res.append(matrix[b][i]) # right to left
            b -= 1
            if t > b: break
            for i in range(b, t - 1, -1): res.append(matrix[i][l]) # bottom to top
            l += 1
            if l > r: break
        return res

2. 旋转图像

旋转图像
数组、数学、矩阵

题目:
在这里插入图片描述
思路:
利用辅助矩阵
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码:

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        n = len(matrix)
        # 深拷贝 matrix -> tmp
        tmp = copy.deepcopy(matrix)
        # 根据元素旋转公式,遍历修改原矩阵 matrix 的各元素
        for i in range(n):
            for j in range(n):
                matrix[j][n - 1 - i] = tmp[i][j]

3. 搜索二维矩阵 II

搜索二维矩阵||
数组、二分查找、分治、矩阵

题目:
在这里插入图片描述
在这里插入图片描述
思路:
在这里插入图片描述
在这里插入图片描述
代码:

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

链表

1. 反转链表

反转链表
链表

题目:
在这里插入图片描述
思路:
双指针迭代

代码:

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        cur = head
        pre = None
        while cur:
            temp = cur.next
            cur.next = pre
            pre = cur
            cur = temp
        return pre

2. 回文链表

回文链表
栈、递归、链表、双指针

题目:
在这里插入图片描述
思路:
首先将链表的中的值转化为数组,然后在数组上判断是否为回文数组

代码:

class Solution:
    def isPalindrome(self, head: Optional[ListNode]) -> bool:
        if not head:return False
        if not head.next:return True
        cur = head
        record = []
        while cur:
            record.append(cur.val)
            cur = cur.next
        return record == record[::-1]

3. 环形链表

环形链表
链表
双指针

题目:
在这里插入图片描述
思路:
定义俩个指针,一个慢指针一个快指针,慢指针一次移动一个节点,快指针一次移动俩个节点,如果存在环形,那会俩个指针必定会第二次相遇。

代码:

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        slow = fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:return True
        return False

4. 环形链表||

环形链表||
链表
双指针

题目:
在这里插入图片描述
思路:
在这里插入图片描述

代码:

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        fast = head
        slow = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                index1 = head
                index2 = fast
                while index1 != index2:
                    index1 = index1.next
                    index2 = index2.next
                return index1
        return None 

5. 合并两个有序链表

合并俩个有序链表
链表

题目:
在这里插入图片描述
代码:

class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        if not list1 and not list2:return None
        if list1 and not list2:
            return list1
        if list2 and not list1:
            return list2

        cur1 = list1
        cur2 = list2
        if cur1.val < cur2.val:
            head = cur1
            cur1 = cur1.next
        else:
            head = cur2
            cur2 = cur2.next
        tmp = head
        while cur1 and cur2:
            if cur1.val <= cur2.val:
                tmp.next = cur1
                tmp = tmp.next
                cur1 = cur1.next
            elif cur1.val > cur2.val:
                tmp.next = cur2
                tmp = tmp.next
                cur2 = cur2.next

        while cur2:
            tmp.next = cur2
            tmp = cur2
            cur2 = cur2.next
        while cur1:
            tmp.next = cur1
            tmp = cur1
            cur1 = cur1.next

        return head

6. 俩数相加

俩数相加
链表
数学

题目:
在这里插入图片描述
思路:

  1. 同时遍历俩个链表
  2. 采用一个记录标签tmp

代码:

class Solution:
    def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        cur_l1 = l1
        cur_l2 = l2
        node = ListNode()
        head = node
        cur = head
        tmp = 0
        while cur_l1 and cur_l2:
            node = ListNode()
            if cur_l1.val + cur_l2.val + tmp >= 10:
                node.val = (cur_l1.val + cur_l2.val + tmp) % 10
                tmp = 1
            else:
                node.val = cur_l1.val + cur_l2.val + tmp
                tmp = 0
            cur.next = node
            cur = cur.next
            cur_l1 = cur_l1.next
            cur_l2 = cur_l2.next
        while cur_l1:
            node = ListNode()
            if cur_l1.val + tmp >= 10:
                node.val = (cur_l1.val + tmp) % 10
                tmp = 1
            else:
                node.val = cur_l1.val + tmp
                tmp = 0
            cur.next = node
            cur = cur.next
            cur_l1 = cur_l1.next
        while cur_l2:
            node = ListNode()
            if cur_l2.val + tmp >= 10:
                node.val = (cur_l2.val + tmp) % 10
                tmp = 1
            else:
                node.val = cur_l2.val + tmp
                tmp = 0
            cur.next = node
            cur = cur.next
            cur_l2 = cur_l2.next
        if tmp == 1:
            node = ListNode(1)
            cur.next = node
            cur = cur.next
            cur = cur.next
        return head.next

7. 删除链表的倒数第 N 个结点

删除链表的倒数第 N 个结点
链表
双指针

题目:
在这里插入图片描述
思路:

  1. 滑动窗口

代码:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        node = ListNode()
        node.next = head
        cur_pre = node
        head2 = node
        cur = head
        tmp = cur_pre
        while n > 0:
            tmp = tmp.next
            n -= 1
        while tmp.next:
            tmp = tmp.next
            cur_pre = cur
            cur = cur.next
        cur_pre.next = cur.next
        return head2.next

8. 两两交换链表中的节点

两两交换链表中的节点

题目:
在这里插入图片描述
思路:
在这里插入图片描述
代码:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy_head = ListNode(next=head)
        current = dummy_head
        
        # 必须有cur的下一个和下下个才能交换,否则说明已经交换结束了
        while current.next and current.next.next:
            temp = current.next # 防止节点修改
            temp1 = current.next.next.next
            current.next = current.next.next
            current.next.next = temp
            temp.next = temp1
            current = current.next.next
        return dummy_head.next

9. 排序链表

排序链表

题目:
在这里插入图片描述
思路:

  1. 将链表转为数组
  2. 对数组进行排序
  3. 将排序的数组转为链表

代码:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head or not head.next:return head
        cur = head
        record = []
        while cur:
            record.append(cur.val)
            cur = cur.next
        record.sort()
        node = ListNode()
        head1 = node
        cur1 = head1
        for i in range(len(record)):
            node = ListNode(val = record[i])
            cur1.next = node
            cur1 = cur1.next
        return head1.next

二叉树

1. 二叉树的中序遍历

二叉树的中序遍历

题目:
在这里插入图片描述
代码:

# 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 inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:return []
        left = self.inorderTraversal(root.left)
        right = self.inorderTraversal(root.right)
        return left + [root.val] + right

2. 二叉树的最大深度

二叉树的最大深度

题目:
在这里插入图片描述
思路:

本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。
二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)
本题中我们通过后序求的根节点高度来求的二叉树最大深度。
【注】在解二叉树的题目时,优先考虑能否使用后序遍历

代码:

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if not root: return 0
        left = self.maxDepth(root.left)  # 左
        right = self.maxDepth(root.right)  # 右
        height = 1 + max(left,right)  # 根
        return height

3. 翻转二叉树

翻转二叉树

题目:
在这里插入图片描述
思路:

采用前序遍历,交换左右子树

代码:

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

4. 对称二叉树

对称二叉树

题目:
在这里插入图片描述
思路:
对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。

本题遍历只能是“后序遍历”,因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。

代码:

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        if not root:
            return True
        return self.compare(root.left, root.right) 
    def compare(self, left, right):
        #首先排除空节点的情况
        if left == None and right != None: return False
        elif left != None and right == None: return False
        elif left == None and right == None: return True
        #排除了空节点,再排除数值不相同的情况
        elif left.val != right.val: return False
        
        #此时就是:左右节点都不为空,且数值相同的情况
        #此时才做递归,做下一层的判断
        outside = self.compare(left.left, right.right) #左子树:左、 右子树:右
        inside = self.compare(left.right, right.left) #左子树:右、 右子树:左
        isSame = outside and inside #左子树:中、 右子树:中 (逻辑处理)
        return isSame  

5. 二叉树的直径

二叉树的直径

题目:
在这里插入图片描述
在这里插入图片描述
代码:

class Solution:
    def diameterOfBinaryTree(self, root: TreeNode) -> int:
        self.ans = 1
        def depth(root):
            if not root: return 0
            L = depth(root.left)
            R = depth(root.right)
            self.ans = max(self.ans, L + R + 1)
            return max(L, R) + 1
        depth(root)
        return self.ans - 1

6. 二叉树的层序遍历

二叉树的层序遍历

题目:
在这里插入图片描述
思路:

层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。

需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。

代码:

class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        queue = collections.deque([root])
        result = []
        while queue:
            level = []
            for _ in range(len(queue)):
                cur = queue.popleft()
                level.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            result.append(level)
        return result

7. 二叉树的直径

二叉树的直径

题目:
在这里插入图片描述
在这里插入图片描述
思路:
在这里插入图片描述
代码:

# 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 diameterOfBinaryTree(self, root: TreeNode) -> int:
        ans = 0
        def dfs(root):
            if not root:
                return -1
            left_len = dfs(root.left)
            right_len = dfs(root.right)
            nonlocal ans
            ans = max(ans,left_len + right_len + 2)
            return max(left_len,right_len) + 1 
        dfs(root)
        return ans

8. 将有序数组转换为二叉搜索树

将有序数组转换为二叉搜索树
题目:
在这里插入图片描述
思路:
本质就是寻找分割点,分割点作为当前节点,然后递归左区间和右区间。

分割点就是数组的中间节点

代码:

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
        def traversal(nums,left,right):
            if left > right:
                return None
            mid = left + (right - left) // 2
            root = TreeNode(nums[mid])
            root.left = traversal(nums,left,mid - 1)
            root.right = traversal(nums,mid + 1,right)
            return root
        return traversal(nums,0,len(nums) - 1)

9. 验证二叉搜索树

验证二叉搜索树
题目:
在这里插入图片描述
在这里插入图片描述
思路:
中序遍历二叉搜索树得到的数组结果是有序递增数组。采用双指针来判断节点的值是否符合二叉搜索树。同时要保证根节点要大于左子树所有节点,小于右子树所有节点。

代码:

class Solution:
    def __init__(self):
        self.pre = None
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        if not root: 
            return True
        left = self.isValidBST(root.left)
        if self.pre is not None and self.pre.val >= root.val:
            return False
        self.pre = root
        right = self.isValidBST(root.right)
        return left and right

10. 二叉搜索树中第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

11. 二叉树的右视图

二叉树的右视图
题目:
在这里插入图片描述
思路:
在这里插入图片描述
代码:

class Solution:
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        ans = []
        def f(node,depth):
            if not node:
                return
            if depth == len(ans):
                ans.append(node.val)
            f(node.right,depth + 1)
            f(node.left,depth + 1)
        f(root,0)
        return ans

12. 二叉树展开为链表

二叉树展开为链表
题目:
在这里插入图片描述
在这里插入图片描述
思路:
在这里插入图片描述
代码:

class Solution:
    def flatten(self, root: Optional[TreeNode]) -> None:
        # 定义一个辅助函数来处理展开,接收当前节点和前一个处理的节点
        def flattenTree(node, prev):
            if not node:
                return prev
            
            # 由于需要首先连接右侧节点,所以先处理右子树
            prev = flattenTree(node.right, prev)
            # 然后处理左子树
            prev = flattenTree(node.left, prev)
            
            # 更新当前节点的右节点为前一个节点,左节点为 None
            node.right = prev
            node.left = None
            # 将当前节点设为新的前一个节点
            return node
        
        flattenTree(root, None)

13. 从中序与后序遍历序列构造二叉树

从中序与后序遍历序列构造二叉树

  • 题目
    在这里插入图片描述
  • 思路

以 后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素。
在这里插入图片描述
在这里插入图片描述
中序数组的切割点怎么找?
根据后序数组最后一个元素来进行切割
后序数组的切割点怎么找?
后序数组没有明确的切割元素来进行左右切割,不像中序数组有明确的切割点,切割点左右分开就可以了。

  • 代码
class Solution:
    def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
        # 第一步: 特殊情况讨论: 树为空. (递归终止条件)
        if not postorder:
            return None

        # 第二步: 后序遍历的最后一个就是当前的中间节点.
        root_val = postorder[-1]
        root = TreeNode(root_val)

        # 第三步: 找切割点.
        separator_idx = inorder.index(root_val)

        # 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
        inorder_left = inorder[:separator_idx]
        inorder_right = inorder[separator_idx + 1:]

        # 第五步: 切割postorder数组. 得到postorder数组的左,右半边.
        # ⭐️ 重点1: 中序数组大小一定跟后序数组大小是相同的.
        postorder_left = postorder[:len(inorder_left)]
        postorder_right = postorder[len(inorder_left): len(postorder) - 1]

        # 第六步: 递归
        root.left = self.buildTree(inorder_left, postorder_left)
        root.right = self.buildTree(inorder_right, postorder_right)
         # 第七步: 返回答案
        return root

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

  • 思路:
    跟13.思路一样,这里先根据前序数组找到根节点来切分中序数组,然后根据切分的中序数组再切分前序数组。
  • 代码:
class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        # 第一步: 特殊情况讨论: 树为空. 或者说是递归终止条件
        if not preorder:
            return None

        # 第二步: 前序遍历的第一个就是当前的中间节点.
        root_val = preorder[0]
        root = TreeNode(root_val)

        # 第三步: 找切割点.
        separator_idx = inorder.index(root_val)

        # 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
        inorder_left = inorder[:separator_idx]
        inorder_right = inorder[separator_idx + 1:]

        # 第五步: 切割preorder数组. 得到preorder数组的左,右半边.
        # ⭐️ 重点1: 中序数组大小一定跟前序数组大小是相同的.
        preorder_left = preorder[1:1 + len(inorder_left)]
        preorder_right = preorder[1 + len(inorder_left):]

        # 第六步: 递归
        root.left = self.buildTree(preorder_left, inorder_left)
        root.right = self.buildTree(preorder_right, inorder_right)
        # 第七步: 返回答案
        return root

15. 路径总和 III

路径总和III

  • 题目:
    在这里插入图片描述
    在这里插入图片描述
  • 思路:
    在这里插入图片描述
    在这里插入图片描述
  • 代码:
class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> int:
        ans = 0
        cnt = defaultdict(int)
        cnt[0] = 1

        def dfs(node: Optional[TreeNode], s: int) -> None:
            if node is None:
                return
            nonlocal ans
            s += node.val
            ans += cnt[s - targetSum]
            cnt[s] += 1
            dfs(node.left, s)
            dfs(node.right, s)
            cnt[s] -= 1  # 恢复现场

        dfs(root, 0)
        return ans

16. 二叉树的最近公共祖先

二叉树的最近公共祖先

  • 题目:
    在这里插入图片描述
    在这里插入图片描述
  • 思路:
    在这里插入图片描述
  • 代码:
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if root == p or root == q or root is None:
            return root
        left = self.lowestCommonAncestor(root.left,p,q)
        right = self.lowestCommonAncestor(root.right,p,q)
        if left is not None and right is not None:
            return root
        if left is None and right is not None:
            return right
        elif left is not None and right is None:
            return left
        else:
            return None

回溯

1. 全排列

全排列

  • 题目:
    在这里插入图片描述
  • 思路:
    树形结构图:
    在这里插入图片描述
    取得的结果集在叶子节点,回溯的终止条件中要加return,如果是取每个节点的值则不需要加return
    注:采用一个数组来记录数值是否已经使用过
  • 代码:
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        path = []
        result = []
        used = [False] * len(nums)
        def backtracking(nums,used,path,result):
            if len(path) == len(nums):
                result.append(path[:])
                return
            for i in range(len(nums)):
                if used[i]:
                    continue
                used[i] = True
                path.append(nums[i])
                backtracking(nums,used,path,result)
                path.pop()
                used[i] = False
        backtracking(nums,used,path,result)
        return result

2. 子集

子集

  • 题目:
    在这里插入图片描述
  • 思路:
    子集的抽取树形图:
    在这里插入图片描述
  • 代码:
class Solution:
    def subsets(self, nums):
        result = []
        path = []
        self.backtracking(nums, 0, path, result)
        return result

    def backtracking(self, nums, startIndex, path, result):
        result.append(path[:])  # 收集子集,要放在终止添加的上面,否则会漏掉自己
        # if startIndex >= len(nums):  # 终止条件可以不加
        #     return
        for i in range(startIndex, len(nums)):
            path.append(nums[i])
            self.backtracking(nums, i + 1, path, result)
            path.pop()

3. 电话号码的字母组合

电话号码的字母组合

  • 题目:
    在这里插入图片描述
  • 思路:
    回溯,递归
  • 代码:
class Solution:
    def __init__(self):
        self.letterMap = [
            "",     # 0
            "",     # 1
            "abc",  # 2
            "def",  # 3
            "ghi",  # 4
            "jkl",  # 5
            "mno",  # 6
            "pqrs", # 7
            "tuv",  # 8
            "wxyz"  # 9
        ]
        self.result = []
        self.s = ""
    
    def backtracking(self, digits, index):
        if index == len(digits):
            self.result.append(self.s)
            return
        digit = int(digits[index])    # 将索引处的数字转换为整数
        letters = self.letterMap[digit]    # 获取对应的字符集
        for i in range(len(letters)):
            self.s += letters[i]    # 处理字符
            self.backtracking(digits, index + 1)    # 递归调用,注意索引加1,处理下一个数字
            self.s = self.s[:-1]    # 回溯,删除最后添加的字符
    
    def letterCombinations(self, digits):
        if len(digits) == 0:
            return self.result
        self.backtracking(digits, 0)
        return self.result

4. 组合总和

组合总和

  • 题目:
    在这里插入图片描述
  • 思路:
    注意for循环的起始值和终止值
  • 代码:
class Solution:

    def backtracking(self, candidates, target, total, startIndex, path, result):
        if total > target:
            return
        if total == target:
            result.append(path[:])
            return

        for i in range(startIndex, len(candidates)):
            total += candidates[i]
            path.append(candidates[i])
            self.backtracking(candidates, target, total, i, path, result)  # 不用i+1了,表示可以重复读取当前的数
            total -= candidates[i]
            path.pop()

    def combinationSum(self, candidates, target):
        result = []
        self.backtracking(candidates, target, 0, 0, [], result)
        return result

二分查找

1. 搜索插入位置

搜索插入位置

  • 题目
    在这里插入图片描述
  • 代码:
class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
        return left

2. 搜索二维矩阵

搜索二维矩阵

  • 题目:
    在这里插入图片描述
  • 代码:
class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        m,n = len(matrix),len(matrix[0])
        left,right = 0,m*n - 1
        while left <= right:
            mid = (left + right) // 2
            x = matrix[mid // n][mid % n]
            if x == target:
                return True
            if x < target:
                left = mid + 1
            else:
                right = mid - 1
        return False

3. 在排序数组中查找元素的第一个和最后一个位置

在排序数组中查找元素的第一个和最后一个位置

  • 题目:
    在这里插入图片描述
  • 代码:
class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        def search(nums,target):
            left,right = 0,len(nums) - 1
            while left <= right:
                mid = (left + right) // 2
                if nums[mid] < target:
                    left = mid + 1
                else:
                    right = mid - 1
            return left
        start = search(nums,target)
        if start == len(nums) or nums[start] != target:
            return [-1,-1]
        end = search(nums,target + 1) - 1
        return [start, end]
  • 思路:
    先用二分法找到目标值的起始位置,然后在寻找末位置

4. 搜索旋转排序数组

搜索旋转排序数组

  • 题目
    在这里插入图片描述
  • 思路:
    在这里插入图片描述
  • 代码:
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        def findMin(nums):# 寻找旋转排序数组中的最小值
            left,right = -1,len(nums) - 1
            while left + 1 < right:
                mid = (left + right) // 2
                if nums[mid] < nums[-1]:
                    right = mid
                else:
                    left = mid 
            return right
        def binary_search(nums,target,left,right):# 二分查找
            while left + 1 < right:
                mid = (left + right) // 2
                if nums[mid] < target:
                    left = mid
                else:
                    right = mid
            return right if nums[right] == target else -1
        i = findMin(nums) # 获取最小值的下标
        if target > nums[-1]:
            return binary_search(nums,target,-1,i)  # 在第一段
        return binary_search(nums, target, i-1, len(nums))  # 在第二段

5. 寻找旋转排序数组中的最小值

寻找旋转排序数组中的最小值

  • 题目:
    在这里插入图片描述
  • 思路:
    在这里插入图片描述
  • 代码:
class Solution:
    def findMin(self, nums: List[int]) -> int:
        left,right = -1,len(nums) - 1
        while left + 1 < right:
            mid = (left + right) // 2
            if nums[mid] < nums[-1]:
                right = mid 
            else:
                left = mid 
        return nums[right]
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值