《剑指offer》第二版

剑指offer

  • 03. 数组中重复的数字
  • 04. 二维数组中的查找
  • 05. 替换空格
  • 06. 从尾到头打印链表
  • 07. 重建二叉树()
  • 剑指 Offer 09. 用两个栈实现队列
  • 10- I. 斐波那契数列( 自己实现LRU)
  • 10- II. 青蛙跳台阶问题
  • 11. 旋转数组的最小数字()
  • 12. 矩阵中的路径()
  • 14- I. 剪绳子
  • 14- II. 剪绳子 II
  • 15. 二进制中1的个数
  • 16. 数值的整数次方( 快速幂)
  • 17. 打印从1到最大的n位数
  • 18. 删除链表的节点
  • 21. 调整数组顺序使奇数位于偶数前面
  • 22. 链表中倒数第k个节点
  • 24. 反转链表
  • 25. 合并两个排序的链表
  • 26. 树的子结构(两个函数递归调用)
  • 27. 二叉树的镜像
  • 28. 对称的二叉树
  • 30. 包含min函数的栈
  • 31. 栈的压入、弹出序列
  • 32 - I. 从上到下打印二叉树
  • 32 - II. 从上到下打印二叉树 II
  • 32 - III. 从上到下打印二叉树 III
  • 33. 二叉搜索树的后序遍历序列()
  • 34. 二叉树中和为某一值的路径( 回溯)
  • 35. 复杂链表的复制
  • 38. 字符串的排列(回溯)
  • 39. 数组中出现次数超过一半的数字
  • 40. 最小的k个数( 快排、堆)
  • 42. 连续子数组的最大和
  • 46. 把数字翻译成字符串()
  • 47. 礼物的最大价值
  • 48. 最长不含重复字符的子字符串
  • 50. 第一个只出现一次的字符
  • 52. 两个链表的第一个公共节点
  • 插一道非剑指Offer:42. 接雨水
  • II 100. 三角形中最小路径之和
  • 53 - I. 在排序数组中查找数字 I
  • 53 - II. 0~n-1中缺失的数字
  • 54. 二叉搜索树的第k大节点
  • 55 - I. 二叉树的深度
  • 55 - II. 平衡二叉树()
  • 56 - I. 数组中数字出现的次数()
  • 56 - II. 数组中数字出现的次数 II
  • 57. 和为s的两个数字
  • 57 - II. 和为s的连续正数序列
  • 58 - I. 翻转单词顺序
  • 58 - II. 左旋转字符串
  • 59 - I. 滑动窗口的最大值()
  • 62. 圆圈中最后剩下的数字
  • 63. 股票的最大利润
  • 64. 求1+2+…+n
  • 66. 构建乘积数组
  • 68 - I. 二叉搜索树的最近公共祖先
  • 68 - II. 二叉树的最近公共祖先()
  • 59 - II. 队列的最大值
  • 61. 扑克牌中的顺子

03. 数组中重复的数字

class Solution:
    def findRepeatNumber(self, nums: List[int]) -> int:
        visited = set()
        for num in nums:
            if num in visited:
                return num
            visited.add(num)

04. 二维数组中的查找

站在右上角看

class Solution:
    def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
        if not matrix:
            return False
        m, n = len(matrix), len(matrix[0])

        i, j = 0, n - 1

        while 0 <= i < m and 0 <= j < n:
            if matrix[i][j] == target:
                return True
            elif matrix[i][j] > target:
                j -= 1
            elif matrix[i][j] < target:
                i += 1
        
        return False

二分法的话可以对每一行二分,但是还不如按照二叉搜索树来找。

05. 替换空格

class Solution:
    def replaceSpace(self, s: str) -> str:
        words = s.split(' ')
        return '%20'.join(words)

06. 从尾到头打印链表

class Solution:
    def reversePrint(self, head: ListNode) -> List[int]:
        nums = []
        cur = head
        while cur:
            nums.append(cur.val)
            cur = cur.next
        
        return nums[::-1]

07. 重建二叉树()

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        def recur(root, left, right):
            '''
            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 = {}
        for i, num in enumerate(inorder):
            dic[num] = i
        
        return recur(0, 0, len(inorder) - 1)

剑指 Offer 09. 用两个栈实现队列

class CQueue:

    def __init__(self):
        self.stack1 = []
        self.stack2 = []

    def appendTail(self, value: int) -> None:
        self.stack1.append(value)

    def deleteHead(self) -> int:
        if self.stack2:
            return self.stack2.pop()
        
        while self.stack1:
            self.stack2.append(self.stack1.pop())
        
        if self.stack2:
            return self.stack2.pop()
        return -1
class CQueue:

    def __init__(self):
        self.stack1 = []
        self.stack2 = []

    def appendTail(self, value: int) -> None:
        self.stack1.append(value)

    def deleteHead(self) -> int:
        if self.stack2:
            return self.stack2.pop()
        if not self.stack1: return -1
        while self.stack1:
            self.stack2.append(self.stack1.pop())
        
        return self.stack2.pop()

10- I. 斐波那契数列( 自己实现LRU)

class Solution:
    def fib(self, n: int) -> int:
        # 迭代
        if n < 2: return n
        a, b = 0, 1

        for _ in range(n-1):
            c = int((a + b) % (1e9+7))
            a = b
            b = c
        
        return c
class Solution:
    def fib(self, n: int) -> int:
        # 强行递归,会超时
        if n < 2: return n
        return (self.fib(n-1) + self.fib(n-2)) % (1e9+7)
class Solution:
    @lru_cache
    def fib(self, n: int) -> int:
        # 强行递归,会超时
        if n < 2: return n
        return int((self.fib(n-1) + self.fib(n-2)) % (1e9+7))

自己实现LRU_Cache

import collections

class LRU_Cache:
    def __init__(self, func):
        self.func = func
        self.cache = {}
    def __call__(self, *args):
        if args in self.cache:
            return self.cache[args]
        else:
            self.cache[args] = self.func(*args)
            return self.cache[args]


def fib(n):
    if n < 2: return n
    return fib(n-1) + fib(n-2)

print(fib(10))

10- II. 青蛙跳台阶问题

class Solution:
    def numWays(self, n: int) -> int:
        if n == 0: return 1
        if n <= 2: return n
        a, b = 1, 1

        for _ in range(n-1):
            c = int((a + b) % (1e9 + 7))
            a = b
            b = c
        
        return c

11. 旋转数组的最小数字()

class Solution:
    def minArray(self, numbers: List[int]) -> int:
        # 二分法
        n = len(numbers)
        l, r = 0, n - 1

        while l < r:
            mid = l + (r - l) // 2
            # 后半段是无序, 最小值肯定在前半段
            if numbers[mid] < numbers[r]:
                r = mid
            # 后半段是有序的,但是不能确定最小值一定在后半段
            elif numbers[mid] > numbers[r]:
                l = mid + 1
            else:
                r -= 1
        
        return numbers[l]

12. 矩阵中的路径()

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        def dfs(i, j, k):
            if k >= len(word): return True
            if i < 0 or i >= len(board) or j < 0 or j >= len(board[0]) or board[i][j] != word[k]:
                return False
            if not board[i][j]: return False
            board[i][j] = ''

            up = dfs(i-1, j, k+1)
            down = dfs(i+1, j, k+1)
            left = dfs(i, j-1, k+1)
            right = dfs(i, j+1, k+1)
            board[i][j] = word[k]   # 取消操作
            return up or down or left or right
        m, n = len(board), len(board[0])
        visited = [[False] * n for _ in range(m)]
        for i in range(m):
            for j in range(n):
                if board[i][j] == word[0]:
                    res = dfs(i, j, 0)
                    if res: return True
        
        return False

14- I. 剪绳子

class Solution:
    def cuttingRope(self, n: int) -> int:
        # 拆成2,3就行了
        # 因为2只能拆成1+1,而1x1=1<2
        # 3只能拆成2+1或者1+1+1,乘积都小于3,而4就开始不一样了
        if n == 2: return 1
        if n == 3: return 2

        three_nums = n // 3
        left = n % 3

        if left == 1:
            three_nums -= 1
            left += 3
        elif left == 0:
            left = 1
        return 3 ** three_nums * left

14- II. 剪绳子 II

class Solution:
    def cuttingRope(self, n: int) -> int:
        # 本题不可以先拆再直接算,因为中间有些值可能过大,需要取模
        if n == 2: return 1
        if n == 3: return 2

        res = 1
        while n > 0:
            if 2 <= n <= 4:
                break
            n = n - 3
            res = int((res * 3) % (1e9+7))
        
        return int((res * n) % (1e9+7))

15. 二进制中1的个数

class Solution:
    def hammingWeight(self, n: int) -> int:
        res = 0
        while n:
            res += n & 1
            n = n >> 1
        
        return res

16. 数值的整数次方( 快速幂)

递归,但是递归会出现超出最大递归深度。

class Solution:
    def myPow(self, x: float, n: int) -> float:
        if n == 0: return 1
        if n > 0: return self.myPow(x, n-1) * x
        else: return 1 / (self.myPow(x, abs(n)-1) * x)
class Solution:
    def myPow(self, x: float, n: int) -> float:
        # 快速幂解法
        def quickMul(x, N):
            if N == 0: return 1
            y = quickMul(x, N // 2)
            return y * y if N % 2 == 0 else y * y * x
        
        return quickMul(x, n) if n > 0 else 1 / quickMul(x, abs(n))

17. 打印从1到最大的n位数

主要是考大数问题,不考虑大数问题的话就过于简单了。

class Solution:
    def printNumbers(self, n: int) -> List[int]:
        # 直接计算最大的n位数是多少
        n = int('9' * n)

        return list(range(1, n+1))

18. 删除链表的节点

class Solution:
    def deleteNode(self, head: ListNode, val: int) -> ListNode:
        dummy = ListNode(-1, head)
        pre = dummy
        cur = head

        while cur and cur.val != val:
            pre = cur
            cur = cur.next
        
        if cur:
            pre.next = cur.next
        else:
            return head
        
        return dummy.next

21. 调整数组顺序使奇数位于偶数前面

class Solution:
    def exchange(self, nums: List[int]) -> List[int]:
        l = 0
        n = len(nums)
        for r in range(n):
            if nums[r] % 2 == 1:
                nums[l], nums[r] = nums[r], nums[l]
                l += 1
        
        return nums

22. 链表中倒数第k个节点

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        slow, fast = head, head

        for _ in range(k):
            fast = fast.next
        
        while fast:
            slow = slow.next
            fast = fast.next
        
        return slow

24. 反转链表

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        pre = None
        cur = head

        while cur:
            tmp = cur.next
            cur.next = pre
            pre = cur
            cur = tmp
        
        return pre

25. 合并两个排序的链表

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy = ListNode(-1)
        cur = dummy

        while l1 and l2:
            if l1.val < l2.val:
                cur.next = ListNode(l1.val)
                l1 = l1.next
            else:
                cur.next = ListNode(l2.val)
                l2 = l2.next
            cur = cur.next
        
        if l1: cur.next = l1
        elif l2: cur.next = l2

        return dummy.next

26. 树的子结构(两个函数递归调用)

class Solution:
    def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
        def recur(A, B):
            if not B: return True
            if not A or A.val != B.val: return False
            
            left = recur(A.left, B.left)
            right = recur(A.right, B.right)
            return left and right

        return bool(A and B) and (recur(A, B) or self.isSubStructure(A.left, B) or self.isSubStructure(A.right, B))

27. 二叉树的镜像

class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        # 不断反转左右子树
        def recur(root):
            if not root: return
            root.left, root.right = root.right, root.left

            recur(root.left)
            recur(root.right)

            return root

        return recur(root)

28. 对称的二叉树

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        if not root: return True

        def recur(left, right):
            if not left and not right: return True
            if (left and not right) or (not left and right) or (left.val != right.val): return False
            # 如果是叶子节点,并且两个节点的值一样,才返回true
            if (left and not left.left and not left.right) and (right and right.left and right.right):
                if left.val == right.val: return True

            outer = recur(left.left, right.right)
            inner = recur(left.right, right.left)

            return outer and inner
        
        return recur(root.left, root.right)
class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        if not root: return True
        queue = [root.left, root.right]

        while queue:
            inner, outer = queue.pop(0), queue.pop(0)
            # 判断不对称
            if (inner and not outer) or (not inner and outer): return False
            if inner and outer and inner.val != outer.val: return False

            if inner and outer:
                queue.append(inner.left)
                queue.append(outer.right)
                queue.append(inner.right)
                queue.append(outer.left)
        
        return True

30. 包含min函数的栈

class MinStack:

    def __init__(self):
        """
        initialize your data structure here.
        """
        # 给每一个数加一个最小值的元素就行
        self.data = []
        
    def push(self, x: int) -> None:
        if self.data:
            top_num, min_num = self.data[-1]
            min_num = min(min_num, x)
            self.data.append((x, min_num))
        else:
            self.data.append((x, x))
    def pop(self) -> None:
        tmp, _ = self.data.pop()
        return tmp

    def top(self) -> int:
        tmp, _ = self.data[-1]
        return tmp

    def min(self) -> int:
        _, tmp = self.data[-1]
        return tmp

31. 栈的压入、弹出序列

用一个栈进行模拟,每次从pushed进行入栈,如果栈顶元素等于popped第一个元素,就不断对栈进行pop,最后看popped有没有走到头

class Solution:
    def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
        stack = []
        j = 0
        for num in pushed:
            stack.append(num)
            while stack and stack[-1] == popped[j]:
                stack.pop()
                j += 1
        
        if j == len(popped): return True
        return False

32 - I. 从上到下打印二叉树

class Solution:
    def levelOrder(self, root: TreeNode) -> List[int]:
        res = []
        if not root: return res
        queue = [root] 

        while queue:
            n = len(queue)
            for _ in range(n):
                node = queue.pop(0)
                res.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
        
        return res

32 - II. 从上到下打印二叉树 II

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        res = []
        if not root: return res
        queue = [root]

        while queue:
            n = len(queue)
            tmp = []
            for _ in range(n):
                node = queue.pop(0)
                tmp.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            res.append(tmp)
        
        return res

32 - III. 从上到下打印二叉树 III

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        res = []
        if not root: return res
        queue = [root]
        flag = True

        while queue:
            n = len(queue)
            tmp = []
            for _ in range(n):
                node = queue.pop(0)
                tmp.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            if flag: res.append(tmp)
            else: res.append(tmp[::-1])
            flag = not flag
        
        return res

33. 二叉搜索树的后序遍历序列()

后序遍历的结果为:[左子树|右子树|根节点]。根节点一定在最后,左子树一定比根节点小,右子树一定比根节点大。因此每次递归找到第一个比根节点大的值作为左右子树的分界点,然后按照左子树,右子树分开递归。

class Solution:
    def verifyPostorder(self, postorder: List[int]) -> bool:
        def recur(i, j):
            if i >= j: return True
            p = i
            while postorder[p] < postorder[j]: p += 1
            m = p
            while postorder[p] > postorder[j]: p += 1
            return p == j and recur(i, m-1) and recur(m, j-1)
        
        return recur(0, len(postorder) - 1)

34. 二叉树中和为某一值的路径( 回溯)

class Solution:
    def pathSum(self, root: TreeNode, target: int) -> List[List[int]]:
        paths, path = [], []
        def recur(root, target):
            if not root: return

            target -= root.val
            path.append(root.val)
            if target == 0 and not root.left and not root.right:
                paths.append(path[:])
            
            recur(root.left, target)
            recur(root.right, target)
            target += root.val
            path.pop()

        recur(root, target)
        return paths

35. 复杂链表的复制

class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        # 先把当前链表中的所有节点的值复制到新链表上,然后再复制指针
        if not head: return head
        dic = {}
        cur = head

        while cur:
            dic[cur] = Node(cur.val)
            cur = cur.next
        
        # 复制指针
        cur = head
        while cur:
            dic[cur].next = dic.get(cur.next)
            dic[cur].random = dic.get(cur.random)
            cur = cur.next
        
        return dic[head]

38. 字符串的排列(回溯)

class Solution:
    def permutation(self, s: str) -> List[str]:
        paths = []
        if not s: return paths

        path = ''
        used = [False] * len(s)
        def backtracking(s, path, used):
            if len(path) == len(s): 
                paths.append(path)
                return
            
            for i in range(len(s)):
                if not used[i]:
                    if i > 0 and s[i-1] == s[i] and not used[i-1]:
                        continue
                    used[i] = True
                    path += s[i]
                    backtracking(s, path, used)
                    path = path[:-1]
                    used[i] = False
        s = sorted(s)

        backtracking(s, path, used)
        return paths

39. 数组中出现次数超过一半的数字

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

用哈希表最好

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        dic = {}
        for num in nums:
            if num not in dic:
                dic[num] = 0
            else:
                dic[num] += 1
        
        for k, v in dic.items():
            if v >= len(nums) // 2:
                return k

40. 最小的k个数( 快排、堆)

class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        arr.sort()
        return arr[:k]
class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        nums = self.quickSort(arr, 0, len(arr) - 1)
        return nums[:k]

    def quickSort(self, nums, i, j):
        # 快排一定要记住
        # https://zhuanlan.zhihu.com/p/63227573
        if i > j: return

        pivot = nums[i]
        low = i
        high = j

        while i < j:
            while i < j and nums[j] > pivot:
                j -= 1
            nums[i] = nums[j]
            while i < j and nums[i] <= pivot:
                i += 1
            nums[j] = nums[i]
        nums[j] = pivot
        self.quickSort(nums, low, i-1)
        self.quickSort(nums, i+1, high)
        return nums

42. 连续子数组的最大和

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        n = len(nums)
        if n < 2: return sum(nums)
    
        dp = [0] * n
        dp[0] = nums[0]

        for i in range(1, len(nums)):
            dp[i] = max(dp[i-1] + nums[i], nums[i])
        
        return max(dp)

46. 把数字翻译成字符串()

class Solution:
    def translateNum(self, num: int) -> int:
        # 就是看怎么划分,可以使每一个数都处在0到25之间
        s = str(num)
        n = len(s)
        if n < 2: return 1

        dp = [0] * n
        dp[0] = 1
        if s[0] == '0':
            dp[1] = 1
        else:
            dp[1] = 2 if int(s[:2]) < 26 else 1
        
        for i in range(2, len(s)):
            if s[i-1] == '0':
                dp[i] = dp[i-1]
            else:
                dp[i] = dp[i-1] + dp[i-2] if int(s[i-1:i+1]) < 26 else dp[i-1]

        
        return dp[-1]

47. 礼物的最大价值

class Solution:
    def maxValue(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        if m == 1 or n == 1:
            res = 0
            for i in range(m):
                for j in range(n):
                    res += grid[i][j]
            return res

        dp = [[0] * n for _ in range(m)]
        
        dp[0][0] = grid[0][0]
        for i in range(1, n):
            dp[0][i] = dp[0][i-1] + grid[0][i]
        
        for i in range(1, m):
            dp[i][0] = dp[i-1][0] + grid[i][0]
        
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + grid[i][j]
        
        return dp[-1][-1]

48. 最长不含重复字符的子字符串

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        n = len(s)
        if n < 2: return n

        dic = {}
        max_length = -1
        i = -1
        for j, c in enumerate(s):
            if c in dic: 
                i = max(dic[c], i)
            dic[c] = j
            max_length = max(max_length, j-i)
        
        return max_length

50. 第一个只出现一次的字符

class Solution:
    def firstUniqChar(self, s: str) -> str:
        if not s: return ' '
        dic = {}

        for c in s:
            if c not in dic:
                dic[c] = 1
            else:
                dic[c] += 1
        
        for c in s:
            if dic[c] == 1:
                return c
        
        return ' '

52. 两个链表的第一个公共节点

class Solution:
    def getIntersectionNode(self, l1: ListNode, l2: ListNode) -> ListNode:
        p1, p2 = l1, l2
        while p1 != p2:
            p1 = p1.next if p1 else l2
            p2 = p2.next if p2 else l1
        
        return p1

插一道非剑指Offer:42. 接雨水

class Solution:
    def trap(self, height: List[int]) -> int:
        # 双指针多次遍历
        n = len(height)
        # 先找到位置i+1左边最高的柱子,虽然我们dp的时候求的是第i列左边,含第i列最大的柱子,但是实际上使用的时候是看i+1列
        maxLeft = [0] * n
        maxLeft[0] = height[0]
        for i in range(1, n):
            maxLeft[i] = max(maxLeft[i-1], height[i])
        # 再找到i-1列右边最高的柱子
        maxRight = [0] * n
        maxRight[-1] = height[-1]
        for i in range(n-2, -1, -1):
            maxRight[i] = max(maxRight[i+1], height[i])
        
        # 求和
        res = 0
        for i in range(1, n-1):
            tmp = min(maxLeft[i-1], maxRight[i+1]) - height[i]
            res += tmp if tmp > 0 else 0
            
        return res

II 100. 三角形中最小路径之和

小红书推荐算法面试题,不难,dp很容易做。

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        #    2
        #   3 4
        #  5 6 7
        # 8 9 10 11
        m = len(triangle)
        if m < 2: return triangle[0][0]

        dp = []
        for i in range(m):
            if i == 0:
                dp.append([triangle[0][0]])
            else:
                tmp = [0] * (i+1)
                for j in range(i+1):
                    if j == 0:
                        tmp[j] = dp[i-1][0] + triangle[i][0]
                    elif j == i:
                        tmp[j] = dp[i-1][-1] + triangle[i][-1]
                    else:
                        tmp[j] = min(dp[i-1][j-1], dp[i-1][j]) + triangle[i][j]
                dp.append(tmp[:])

        return min(dp[-1])

优化一下:

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        n = len(triangle)

        dp = [[0] * i for i in range(1, n+1)]
        dp[0][0] = triangle[0][0]

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

53 - I. 在排序数组中查找数字 I

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        dic = collections.Counter(nums)
        return dic[target]

很明显不是最优解,因为原始数组是有序的,因此肯定可以使用二分,二分找出左右边界,然后求差即可。

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        # 使用二分查到target出现的左边界和有边界
        # 之后左右边界之差加上1就行了
        if not nums: return 0
        leftBorder, rightBorder = self.searchLeft(nums, target), self.searchRight(nums, target)
        if leftBorder == -2 or rightBorder == -2: return 0
        if nums[leftBorder+1] != nums[rightBorder-1]: return 0
        return rightBorder - leftBorder - 1

    def searchRight(self, nums, target):
        l, r = 0, len(nums) - 1
        rightBorder = -2
        while l <= r:
            mid = l + (r - l) // 2
            if nums[mid] <= target:
                l = mid + 1
                rightBorder = l
            else:
                r = mid - 1
        return rightBorder

    def searchLeft(self, nums, target):
        l, r = 0, len(nums) - 1
        leftBorder = -2
        while l <= r:
            mid = l + (r - l) // 2
            if nums[mid] < target:
                l = mid + 1
            else:
                r = mid - 1
                leftBorder = r
        return leftBorder

此外,因为是排序数组,因此最直接的方法就是双指针,首先两个指针一起走,遇到taregt之后,左指针固定,右指针继续走,直到右指针指到不等于target的值。

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if len(nums) == 1: return 1 if nums[0] == target else 0
        l, r = 0, 0
        for i, num in enumerate(nums):
            if l == r and nums[r] != target:
                l += 1
                r += 1
            elif nums[r] == target:
                r += 1
            elif l != r and nums[r] != target:
                return r - l
        
        if r == len(nums): return r - l
        return 0

53 - II. 0~n-1中缺失的数字

笨方法,哈希表。

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        dic = collections.Counter(nums)
        for i in range(len(nums)+1):
            if i not in dic:
                return i

二分法,二分的关键是看当前位置的数等不等于下标,等于下标的话就说明缺失的值在右边,否则就在左边。

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        l, r = 0, len(nums) - 1
        while l <= r:
            m = l + (r - l) // 2
            if nums[m] == m: l = m + 1
            else: r = m - 1
        
        return l

54. 二叉搜索树的第k大节点

class Solution:
    def __init__(self):
        self.cnt = 0
        self.ans = 0

    def kthLargest(self, root: TreeNode, k: int) -> int:
        # 中序遍历是一个递增数列
        # 最暴力的方法就是先中序遍历,之后按下标找第k个元素即可
        # 最好的做法时一次性递归,维护一个计数器,记录是不是走到了第k个数
        
        def dfs(root):
            if not root: return 
            dfs(root.right)
            self.cnt += 1
            if self.cnt == k:
                self.ans = root.val
                return
            dfs(root.left)
        
        dfs(root)
        return self.ans

55 - I. 二叉树的深度

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        def dfs(root):
            if not root: return 0
            
            leftDepth = dfs(root.left)
            rightDepth = dfs(root.right)

            depth = max(leftDepth, rightDepth) + 1

            return depth
        
        return dfs(root)

55 - II. 平衡二叉树()

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        # 
        def recur(root):
            if not root: return 0
            left = recur(root.left)
            if left == -1: return -1
            right = recur(root.right)
            if right == -1: return -1
            return max(left, right) + 1 if abs(right - left) <= 1 else -1
        
        return recur(root) != -1

更易想到的解法:

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        if not root: return True
        left_depth = self.getDepth(root.left)
        right_depth = self.getDepth(root.right)
        return abs(right_depth - left_depth) <= 1 and self.isBalanced(root.left) and self.isBalanced(root.right)
    def getDepth(self, root):
        if not root: return 0
        return max(self.getDepth(root.left), self.getDepth(root.right)) + 1

56 - I. 数组中数字出现的次数()

class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        # x ^ x == 0
        # x ^ 0 = x
        n = 0
        for num in nums:
            n = n ^ num
        # 因为有两个只出现过一次的数,因此一次异或得到的结果只是x^y
        # 因为x,y不一样,所以x与y必然至少有一个不一样,因此找到其中一位
        m = 1
        while n & m == 0:
            m <<= 1
        
        # 找到m后,按照m可以将原数组分成两份,分别是与m进行&之后是0的,和是1的
        x, y = 0, 0
        for num in nums:
            if num & m: x ^= num
            else: y ^= num
        
        return x, y

56 - II. 数组中数字出现的次数 II

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        dic = collections.Counter(nums)
        for k, v in dic.items():
            if v == 1: return k

其他题解

57. 和为s的两个数字

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        dic = {}
        for i, num in enumerate(nums):
            if target - num in dic:
                return [num, target - num]
            else:
                dic[num] = i
        
        return [-1, -1]
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        l, r = 0, len(nums) - 1
        while l < r:
            s = nums[l] + nums[r]
            if s > target:
                r -= 1
            elif s < target:
                l += 1
            else:
                return [nums[l], nums[r]]
        
        return [-1, -1]

57 - II. 和为s的连续正数序列

class Solution:
    def findContinuousSequence(self, target: int) -> List[List[int]]:
        # 因为都是正数,所以在一个区间内去掉一个数和必然会减小,加上一个数和必然会增加
        l, r = 1, 2
        s = 3
        res = []

        while l < r:
            if s == target:
                res.append(list(range(l, r+1)))
            if s < target:
                r += 1
                s += r
            # 之所以在s==target的时候也继续移动左指针,是因为可能多移动几次,在移动一次右指针又满足条件了
            # 比如:[2,3,4]和[4,5]
            elif s >= target:
                s -= l
                l += 1
        
        return res

58 - I. 翻转单词顺序

class Solution:
    def reverseWords(self, s: str) -> str:
        # 删除前缀空格和后缀空格
        s = s.strip()
        words = s.split()
        return ' '.join(words[::-1])

58 - II. 左旋转字符串

class Solution:
    def reverseLeftWords(self, s: str, n: int) -> str:
        return s[n:] + s[:n] 

59 - I. 滑动窗口的最大值()

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        if not nums: return []
        deque = collections.deque()

        # 先构造一个初始的队列
        for i in range(k):
            while deque and deque[-1] < nums[i]: deque.pop()
            deque.append(nums[i])

        res = [deque[0]]
        for i in range(k, len(nums)):
            if nums[i-k] == deque[0]:
                deque.popleft()
            while deque and deque[-1] < nums[i]:deque.pop()
            deque.append(nums[i])
            res.append(deque[0])
    
        return res

62. 圆圈中最后剩下的数字

使用队列来模拟约瑟夫环,效率比较低,因此会超时。

class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        deque = collections.deque()
        for i in range(n):
            deque.append(i)
        
        while len(deque) > 1:
            for _ in range(m % n):
                tmp = deque.popleft()
                deque.append(tmp)
            deque.pop()
        
        return deque[0]

递归法:

class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        def dfs(n, m):
            if n == 1: return 0
            x = dfs(n-1, m)
            return (x + m) % n
        
        return dfs(n, m)

63. 股票的最大利润

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if not prices: return 0
        
        res = 0 
        min_price = prices[0]
        for price in prices:
            if price < min_price:
                min_price = price
            else:
                res = max(res, price - min_price)
        
        return res

64. 求1+2+…+n

class Solution:
    def sumNums(self, n: int) -> int:
        def recur(n, s):
            if n == 1: return s + 1
            return recur(n-1, s) + n
        
        return recur(n, 0)
class Solution:
    def sumNums(self, n: int) -> int:
            # 因为不能使用循环,因此只能使用递归来实现
            # 但是递归需要有终止条件,终止条件通常都是if语句
            # 所以,还需要解决if语句代替问题,在python中,当and前为false时,后面的运算会自动终止
            # 因此,可以使用and来代替
            return n and (n + self.sumNums(n-1))

66. 构建乘积数组

class Solution:
    def constructArr(self, a: List[int]) -> List[int]:
        n = len(a)
        if n < 2: return a

        dp_left, dp_right = [1] * n, [1] * n
        
        for i in range(1, n-1):
            dp_left[i] = dp_left[i-1] * a[i-1]
            dp_right[n-i-1] = dp_right[n-i] * a[n-i]
        
        dp_left[-1] = dp_left[-2] * a[-2]
        dp_right[0] = dp_right[1] * a[1]

        b = []
        for i in range(n):
            b.append(dp_left[i] * dp_right[i])
        
        return b

68 - I. 二叉搜索树的最近公共祖先

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if not root: return 
        if p.val <= root.val and q.val >= root.val:
            return root
        if p.val >= root.val and q.val <= root.val:
            return root
        
        if p.val < root.val and q.val < root.val:
            return self.lowestCommonAncestor(root.left, p, q)
        elif p.val > root.val and q.val > root.val:
            return self.lowestCommonAncestor(root.right, p, q)
        

68 - II. 二叉树的最近公共祖先()

只要 p , q p,q pq存在于当前子树上,返回值就不为空。

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if not root or root.val == p.val or root.val == q.val: return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if not left and not right: return  # 左右都为空,没有p,q
        if not left and right: return right  # pq走在右侧
        if not right and left: return left, # pq都在左侧
        return root # 左右都不为空,说明pq在异侧

59 - II. 队列的最大值

这题稍微有点问题,max操作不可能是 O ( 1 ) O(1) O(1)

class MaxQueue:

    def __init__(self):
        self.data = []
        self.deque = collections.deque()

    def max_value(self) -> int:
        if not self.deque:
            return -1
        return self.deque[0]

    def push_back(self, value: int) -> None:
        while self.deque and self.deque[-1] < value:
            self.deque.pop()
        self.data.append(value)
        self.deque.append(value)
            
    def pop_front(self) -> int:
        if not self.data:
            return -1
        ans = self.data.pop(0)
        if ans == self.deque[0]:
            self.deque.popleft()
        return ans

61. 扑克牌中的顺子

class Solution:
    def isStraight(self, nums: List[int]) -> bool:
        repeat = set()
        mi, ma = float('inf'), -float('inf')
        for num in nums:
            if num == 0: continue
            ma = max(ma, num)
            mi = min(mi, num)
            if num in repeat: return False
            repeat.add(num)

        return True if ma - mi < 5 else False

你可能感兴趣的:(Leetcode,链表,leetcode,算法)