【剑指offer】刷题记录(持续更新....)

目录

注意

面试题03. 数组中重复的数字

面试题04. 二维数组中的查找

面试题05. 替换空格

面试题06. 从尾到头打印链表

面试题07. 重建二叉树

面试题09. 用两个栈实现队列

面试题10- I. 斐波那契数列

面试题10- II. 青蛙跳台阶问题

面试题11. 旋转数组的最小数字

面试题12. 矩阵中的路径

面试题13. 机器人的运动范围

面试题14- I. 剪绳子

面试题14- II. 剪绳子 II

面试题15. 二进制中1的个数

面试题16. 数值的整数次方

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

面试题18. 删除链表的节点

面试题19. 正则表达式匹配

面试题20. 表示数值的字符串

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

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

面试题24. 反转链表

面试题25. 合并两个排序的链表

面试题26. 树的子结构

面试题27. 二叉树的镜像

面试题28. 对称的二叉树

面试题29. 顺时针打印矩阵

面试题30. 包含min函数的栈

面试题31. 栈的压入、弹出序列

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

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

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

面试题33. 二叉搜索树的后序遍历序列

面试题34. 二叉树中和为某一值的路径

面试题35. 复杂链表的复制

面试题36. 二叉搜索树与双向链表

面试题37. 序列化二叉树

面试题38. 字符串的排列

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

面试题40. 最小的k个数

面试题41. 数据流中的中位数

面试题42. 连续子数组的最大和

面试题43. 1~n整数中1出现的次数

面试题44. 数字序列中某一位的数字

面试题45. 把数组排成最小的数

面试题46. 把数字翻译成字符串

面试题47. 礼物的最大价值

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

面试题49. 丑数

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

面试题51. 数组中的逆序对

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

面试题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. 滑动窗口的最大值

面试题59 - II. 队列的最大值

面试题60. n个骰子的点数

面试题61. 扑克牌中的顺子

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

面试题63. 股票的最大利润

面试题64. 求1+2+…+n

面试题65. 不用加减乘除做加法

​面试题66. 构建乘积数组

面试题67. 把字符串转换成整数

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

面试题68 - II. 二叉树的最近公共祖先


注意

题源是在LeetCode平台上《剑指offer》。

使用的是Python3

面试题03. 数组中重复的数字

思路:

  • 在一个长度为 n 的数组 nums 里的所有数字都在【0~n-1】 的范围内。
  • 请找出数组中任意一个重复的数字。
  • 因为每个数字都在【0~n-1】中,利用哈希table就可以找到第一个重复的数字进行返回
class Solution:
    def findRepeatNumber(self, nums: List[int]) -> int:
        dic_num = {}
        for num in nums:
            dic_num[num] = dic_num.get(num,0) + 1
            if dic_num[num]>1:
                return num

面试题04. 二维数组中的查找

思路:

  • 从左至右递增↑,从上至下递增↑。
  • 则矩阵右上角是这一行最大的这一列最小的。同理左下角也是这一列最大的,这一行最小的。
  • 任取一角进行遍历
  • 注意n、m可取0,所以要考虑matrix为空的情况
class Solution:
    def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
        if len(matrix) == 0:
            return False
        rows, cols = len(matrix), len(matrix[0])
        i,j = 0, cols-1
        while 0 <= i < rows and 0 <= j < cols:
            if matrix[i][j] > target:
                j -= 1
            elif matrix[i][j] < target:
                i += 1
            elif matrix[i][j] == target:
                return True
        return False

面试题05. 替换空格

思路:

  • s的长度可能为0,要做判断
  • 可以使用split函数,以空格为界分成list,再用join以“%20”为间隔结合
  • 也可以直接使用replace函数
class Solution:
    def replaceSpace(self, s: str) -> str:
        if len(s) == 0:
            return ""
        s = s.split(" ")
        return '%20'.join(s)

面试题06. 从尾到头打印链表

思路:

  • 链表可能为空,需要处理
  • 因为要求返回数组,可以直接用个list装遍历的结果,最后返回从尾到头的结果
  • 利用栈先进后出的性质,也可以用栈装入,最后输出到结果list,一样也是从尾到头的结果
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

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

面试题07. 重建二叉树

思路:

  • 前序+中序构建二叉树
  • 节点个数可能为0,要处理
  • 建树的递归终止条件就是这个区间没有数了,即inl>inr
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if len(preorder) == 0 or len(inorder) == 0:
            return None
        return self.build_pre_in(0, 0, len(inorder)-1, preorder, inorder)
    
    def build_pre_in(self, prel, inl, inr, preorder, inorder):
        if inl>inr:
            return
        # 取根节点
        p = preorder[prel]
        # 找在inorder里的下标
        index = inorder.index(p)
        root = TreeNode(p)
        root.left = self.build_pre_in(prel+1, inl, index-1, preorder, inorder)
        root.right = self.build_pre_in(prel+(index-inl)+1, index+1, inr, preorder, inorder)
        return root

面试题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()
        else:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
            if self.stack2:
                return self.stack2.pop()
            else:
                return -1



# Your CQueue object will be instantiated and called as such:
# obj = CQueue()
# obj.appendTail(value)
# param_2 = obj.deleteHead()

面试题10- I. 斐波那契数列

思路:

  • 递归结构很明显,边界也给了,注意判断就可以。
  • 注意结果取模
  • 递归会有很多重复计算,会导致递归栈爆炸,就可以利用备忘录memo优化
class Solution:
    def fib(self, n: int) -> int:
        if n < 2:
            return n
        memo = [0] * (n+1)
        memo[0] = 0
        memo[1] = 1
        for i in range(2, n+1):
            memo[i] = memo[i-1]+memo[i-2]
        return memo[n]%1000000007

面试题10- II. 青蛙跳台阶问题

思路:

  • 斐波那契数列的变形
  • 注意结果取模
  • 设dp[i]:青蛙跳上i级台阶有dp[i]种跳法
  • 跳上i级台阶青蛙可以从i-1级台阶跳上,也可以从i-2级台阶跳上;
  • 将问题化为求dp[i]的子问题,求dp[i]时,我已知dp[i-1]和dp[i-2],则dp[i]=dp[i-1]+dp[i-2]
  • 形式其实又和斐波那契数列很像了,确定边界,dp[0]=1,dp[1]=1
  • 注意:python没有溢出判断,如果不注意取模,你调试可能也不会报溢出错误。
class Solution:
    def numWays(self, n: int) -> int:
        if n < 2:
            return 1
        dp = [0]*(n+1)
        dp[0] = 1
        dp[1] = 1
        for i in range(2, n+1):
            dp[i] = dp[i-1] + dp[i-2]
        return dp[n]%1000000007

面试题11. 旋转数组的最小数字

思路:

  • 可以使用二分法
  • 将整个待寻找数组[0....len-1]看做两部分[0....a]+[a+1....len-1],其中两部分都是递增数列,我们要寻找的数即下标a+1
  • 初始设置left=0,right=len-1,代表我们要寻找的区间[0...len-1],利用二分法每次寻找到mid。当mid<right时,说明mid之后的数都比mid大,那整个数组的最小值应该在[left,mid]中寻找。当mid>right时,说明目标应该在[mid+1,right]中寻找。当mid==right时,将right-1,因为目标绝对在[left,right-1]中。
class Solution:
    def minArray(self, numbers: List[int]) -> int:
        left,right = 0, len(numbers)-1
        while left < right:
            mid = (left+right)//2
            if numbers[mid] < numbers[right]:
                right = mid
            elif numbers[mid] > numbers[right]:
                left = mid + 1
            elif numbers[mid] == numbers[right]:
                right -= 1
        return numbers[left]

面试题12. 矩阵中的路径

思路:

  • 用dfs四个方向分别走
  • 路径走到头了,就可以返回true,否则中途遇到越界、不相等、或者已经访问过了就返回false
  • 起点不是(0,0),是你路径的起点,所以应该用for循环去寻找起点
  • 我本来用个for循环去走的,发现会超时。
class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        rows,cols = len(board), len(board[0])
        visited = [[0]*cols for _ in range(rows)]

        def dfs(x, y, k):
            if x<0 or x>=rows or y<0 or y>=cols or board[x][y]!=word[k] or visited[x][y]!=0:
                return False
            if k == len(word)-1:
                return True
            visited[x][y] = 1
            res = dfs(x+1,y,k+1) or dfs(x-1,y,k+1) or dfs(x,y+1,k+1) or dfs(x,y-1,k+1)
            visited[x][y] = 0
            return res

        for i in range(rows):
            for j in range(cols):
                if dfs(i, j, 0):return True
        return False

面试题13. 机器人的运动范围

思路:

  • 和上一道题类似,也是利用dfs
  • 注意添加一个判断是否超过k
class Solution:
    def movingCount(self, m: int, n: int, k: int) -> int:
        visited = [[0]*n for _ in range(m)]
        return self.dfs(0,0,m,n,k,visited)
    
    def valid_k(self, x, y, k):
        res = 0
        while x:
            res += x%10
            x //= 10
        while y:
            res += y%10
            y //= 10
        if res > k:
            return False
        return True

    def dfs(self, x, y, m, n, k, visited):
        moves = [(1,0),(-1,0),(0,1),(0,-1)]
        visited[x][y] = 1
        count = 1
        for move in moves:
            newx = x + move[0]
            newy = y + move[1]
            if newx<0 or newx>=m:
                continue
            if newy<0 or newy>=n:
                continue
            if visited[newx][newy]==1 or not self.valid_k(newx, newy, k):
                continue
            count += self.dfs(newx, newy, m,n,k,visited)
        return count

面试题14- I. 剪绳子

思路:

  • 设dp[i]:为长度为i的绳子的最大乘积
  • dp[i] = max(dp[i],max(j*(i-j), j*dp[i-j]) ,对于一个长度为i的绳子,我可以剪可以不剪,不剪就是dp[i],剪的话可以从j=(1...i-1)的里任一位置剪,剪了之后剩下的一段i-j又可以继续剪或者不剪,最后保留那个最大的乘积即可。
  • 找到边界n=2,dp[2]=1
class Solution:
    def cuttingRope(self, n: int) -> int:
        dp = [0] * (n+1)
        dp[0] = 0
        dp[1] = 1
        dp[2] = 1
        for i in range(3, n+1):
            for j in range(1, i):
                dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j]))
        return dp[n]

面试题14- II. 剪绳子 II

思路:

  • 就是比上一题多了一个取模,思路一致
class Solution:
    def cuttingRope(self, n: int) -> int:
        dp = [0] * (n+1)
        dp[2] = 1
        for i in range(3, n+1):
            for j in range(1, i):
                dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j]))
        return dp[n]%1000000007

面试题15. 二进制中1的个数

思路:

  • 每一位和1相与相加即可得到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 x == 0:
            return 0
        if n < 0:
            x, n = 1/x, -n
        res = 1
        while n:
            if n&1:res *= x
            x *= x
            n >>= 1
        return res

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

思路:

  • 利用for循环,n位数,即[1,10的n次方)
class Solution:
    def printNumbers(self, n: int) -> List[int]:
        num = 10**n
        res = []
        for i in range(1, num):
            res.append(i)
        return res

面试题18. 删除链表的节点

思路:

  • 链表题若是要对链表进行操作,可以考虑虚拟一个dummy头节点,使整个链表上的位置可以进行同样处理
  • 此题设置pre和p指针,一个指向当前节点的前一个节点,一个指向当前节点
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteNode(self, head: ListNode, val: int) -> ListNode:
        dummy = ListNode(-1)
        dummy.next = head
        pre,p = dummy, head
        while p:
            if p.val == val:
                pre.next = p.next
                p = p.next
            else:
                pre,p = p, p.next
        return dummy.next

面试题19. 正则表达式匹配

(先埋个坑在这)

面试题20. 表示数值的字符串

思路:

  • 对于字符串题,考虑其可能有多余空格的可能,或者为空的可能。
  • 判断字符串是否是合法数值,分情况讨论
  • 如果当前位置index是正负号,index=0或者index-1=e/E为合法,其余都不合法
  • 如果当前位置index是e/E,则之前(0,index-1)中含有数字没有出现过e/E为合法,其余都不合法
  • 如果当前位置index是.,则之前(0,index-1)中没有出现过.没有出现过e/E为合法,其余都不合法(注意.1合法)
  • 如果当前位置index是数字,则直接查看下一位
  • 最后返回是否出现过数字即可
class Solution:
    def isNumber(self, s: str) -> bool:
        s = s.strip()
        if s == "":
            return False
        met_dot = met_e = met_digit = False
        for i,ch in enumerate(s):
            if ch in ['+','-']:
                if i != 0 and (s[i-1] not in ['e','E']):
                    return False
            elif ch == '.':
                if met_dot or met_e:
                    return False
                met_dot = True
            elif ch in ['e', 'E']:
                if not met_digit or met_e:
                    return False
                met_e, met_digit = True,False
            elif ch.isdigit():
                met_digit = True
            else:
                return False
        return met_digit

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

思路:

  • 对于数组问题,一般都考双指针、二分法、滑动窗口
  • 若是双指针,在移动过程中一定要考虑好边界问题i<j,每一次移动过后,或者要进行交换操作都要判断
  • 另外对于判断奇偶数的方法可以采用num&1==1\num&1==0
class Solution:
    def exchange(self, nums: List[int]) -> List[int]:
        if len(nums) == 1:
            return nums
        i,j = 0, len(nums) - 1
        while i<j:
            while i<j and (nums[i]&1 == 1):
                i += 1
            while i<j and (nums[j]&1 == 0):
                j -= 1
            if i< j:
                nums[i], nums[j] = nums[j], nums[i]
        return nums

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

思路:

  • 建立一个虚节点,使操作计数更方便
  • 若head为空,或者k超过链表长度都应该返回None
  • 让fast指针先走k步,再用p节点从头节点和fast同步往后遍历,当fast到达最后一个节点时,p所指的节点即为答案
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        if not head:
            return None
        dummy = ListNode(-1)
        dummy.next = head
        fast = dummy
        while k and fast:
            fast = fast.next
            k -= 1
        if k != 0:
            return None
        p = head
        while fast.next:
            fast = fast.next
            p = p.next
        return p

面试题24. 反转链表

思路:

  • 翻转链表我比较喜欢用头插法
  • 记得判断一下链表为空的情况
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if not head:
            return None
        dummy = ListNode(-1)
        p = head
        while p:
            q = p.next # 防止断链
            p.next = dummy.next
            dummy.next = p
            p = q
        return dummy.next

面试题25. 合并两个排序的链表

思路:

  • 建立一个新链表,同时用两个指针分别指向l1,l2,依次遍历,较小的放到新链表上
  • 当某一个链表已经遍历完,剩下的数都应该直接接到链表之后
  • 记得返回的是新建立的链表的头结点
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        C = ListNode(-1)
        p1, p2, p3 = l1, l2, C
        while p1 and p2:
            if p1.val < p2.val:
                p3.next = p1
                p3, p1 = p3.next, p1.next
            else:
                p3.next = p2
                p3, p2 = p3.next, p2.next
        if p1:
            p3.next = p1
            p1 = p1.next
        if p2:
            p3.next, p2 = p2, p2.next
        return C.next 

面试题26. 树的子结构

思路:

  • 树的题一般可以考虑递归,因为树天生有递归的结构
  • 当子树递归到了空节点,说明别的节点已经检查完毕,返回true即可
  • 若主树为空,但子树不空,或者子树和主树对应节点数值不一样,返回false
  • 整个函数从当前节点递归,若没有,则去左子树找,若没有,则去右子树找
  • 这期间要保证主和子都不为空,否则返回false
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

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
            return recur(A.left, B.left) and recur(A.right, B.right)
        return recur(A,B) or self.isSubStructure(A.left,B) or self.isSubStructure(A.right, B) if A and B else False

面试题27. 二叉树的镜像

思路:

  • 由于这题是要在原本的树结构上更改,所以要注意处理
  • 利用先序遍历,若当前根节点的左右节点有一个不为空则就应该将它们位置互换
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        self.preorder(root)
        return root
    
    def preorder(self, root):
        if not root:
            return
        if root.left or root.right:
            root.left,root.right = root.right,root.left
        self.preorder(root.left)
        self.preorder(root.right)

面试题28. 对称的二叉树

思路:

  • 和26题思路有点相近,递归判断当前两个节点是否值相同,或者是否同为空
  • 再继续递归检查
  • 注意若树为空也返回True
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

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

面试题29. 顺时针打印矩阵

思路:

  • 这道题想清楚逻辑会好做一点,设定一个上下左右的边界
  • 每一次一行或一列遍历完,则那个边界就往里缩一点,每一次的遍历保持在边界里就可以
class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if len(matrix)==0 or len(matrix[0])==0:
            return []
        # 上下左右边界
        l,r,u,d = 0, len(matrix[0])-1, 0, len(matrix)-1
        res = []
        while True:
            for i in range(l,r+1):
                res.append(matrix[u][i])
            u += 1
            if u > d:
                break
            for i in range(u,d+1):
                res.append(matrix[i][r])
            r -= 1
            if l > r:
                break
            for i in range(r, l-1, -1):
                res.append(matrix[d][i])
            d -= 1
            if u > d:
                break
            for i in range(d, u-1, -1):
                res.append(matrix[i][l])
            l += 1
            if l > r:
                break
        return res

面试题30. 包含min函数的栈

思路:

  • 这里面的难点主要是返回栈里的min最小数
  • 维护一个存储最小数的栈,保证栈顶是最小的数,比栈顶小的插入到最小数栈。
class MinStack:

    def __init__(self):
        self.stack = []
        self.minstack = []

    def push(self, x: int) -> None:
        self.stack.append(x)
        if not self.minstack or x <= self.minstack[-1]:
            self.minstack.append(x)

    def pop(self) -> None:
        if self.stack.pop() == self.minstack[-1]:
            self.minstack.pop()

    def top(self) -> int:
        return self.stack[-1]

    def min(self) -> int:
        return self.minstack[-1]

面试题31. 栈的压入、弹出序列

思路:

  • 判断弹出序列是否合法,就直接模拟一个入栈出栈顺序即可
  • 若栈顶等于弹出序列的第一个数,则弹出
  • 最后检查栈里是否还有元素
class Solution:
    def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
        if not pushed or not popped:
            return True
        stack = []
        index = 0
        for i in range(len(pushed)):
            stack.append(pushed[i])
            while index<len(popped) and stack and popped[index]==stack[-1]:
                stack.pop()
                index += 1
        if stack:
            return False
        return True

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

思路:

  • 此题就是单纯的层序遍历
  • 利用队列即可,注意判断空节点
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        res = []
        nextNode = [root]
        while nextNode:
            x = nextNode.pop(0)
            res.append(x.val)
            if x.left:
                nextNode.append(x.left)
            if x.right:
                nextNode.append(x.right)
        return res

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

思路:

  • 在上一题的基础上,需要把属于同一层的输出至一个list里
  • 利用curres,curnode,nextnode,储存当前一层的节点,和下一层的节点
  • 注意空节点
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        res = []
        curres = []
        curNode = [root]
        nextNode = []
        while True:
            while curNode:
                x = curNode.pop(0)
                curres.append(x.val)
                if x.left:
                    nextNode.append(x.left)
                if x.right:
                    nextNode.append(x.right)
            res.append(curres)
            if not nextNode:
                break
            curNode = nextNode
            curres = []
            nextNode = []
        return res

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

思路:

  • 这道题再在上一题的基础上增加一个判断当前层的curres是否要逆序
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        res = []
        curres = []
        curNode = [root]
        nextNode = []
        isReverse = False
        while True:
            while curNode:
                x = curNode.pop(0)
                curres.append(x.val)
                if x.left:
                    nextNode.append(x.left)
                if x.right:
                    nextNode.append(x.right)
            if isReverse:
                curres = curres[::-1]
            isReverse = not isReverse
            res.append(curres)
            if not nextNode:
                break
            curNode = nextNode
            nextNode = []
            curres = []
        return res

面试题33. 二叉搜索树的后序遍历序列

思路:

  • 每当题目提到二叉搜索树,无非想两件事,一个是二叉搜索树的左节点<根,右节点>根,一个是中序遍历是递增序列
  • 该题给了一个后序遍历序列,让判断是否是二叉搜索树的,那我们就考虑上面的两个条件
  • 显然判断左右根的大小更容易
class Solution:
    def verifyPostorder(self, postorder: List[int]) -> bool:
        if not postorder:
            return True
        def recur(i, j):
            if i >= j:return True
            l = i
            while postorder[l] < postorder[j]:l += 1
            m = l
            while postorder[l] > postorder[j]: l += 1
            return l == j and recur(i, m-1) and recur(m, j-1)
        return recur(0, len(postorder)-1)

面试题34. 二叉树中和为某一值的路径

思路:

  • 利用dfs+回溯的方法遍历二叉树
  • 每当当前路径走到头,且其和为目标值,则把当前path加入res里,同时记得要弹出当前加入的值,方便其进入下一个不同的路径
  • 注意函数里的path,要用path[:],不然传的是空值
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
        if not root:
            return []
        res = []
        def dfs(root, path, cursum):
            if not root:
                return
            cursum += root.val
            path.append(root.val)
            if not root.left and not root.right and cursum==sum:
                res.append(path[:])
                path.pop()
                return
            dfs(root.left, path, cursum)
            dfs(root.right, path, cursum)
            path.pop()
        dfs(root, [], 0)
        return res

面试题35. 复杂链表的复制

思路:

  • 这道题主要考的是深拷贝的知识点,浅拷贝和深拷贝的差别在:浅拷贝是复制指向节点的指针,本质上还是共享同一块内存位置,深拷贝是开辟了另外一块空间。
  • 可以用Python的copy.deepcopy()函数
"""
# Definition for a Node.
class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = int(x)
        self.next = next
        self.random = random
"""
class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        if not head:
            return None
        visited = {}
        def dfs(head):
            if not head:
                return None
            if head in visited:
                return visited[head]
            copy = Node(head.val)
            visited[head] = copy
            copy.next = dfs(head.next)
            copy.random = dfs(head.random)
            return copy
        return dfs(head)
        

面试题36. 二叉搜索树与双向链表

思路:

  • 需要将二叉搜索树改造成为一个双向链表,且是循环双向链表,表尾节点的right指向表头节点,表头结点的left指向表尾节点
  • 这个链表肯定需要利用二叉搜索树的性质,中序遍历为递增数组
  • 同时我们需要记录表头结点
  • 利用全局变量self.pre,self.head记录当前节点的上一个节点,表头结点,当中序遍历完成,self.pre刚好指向表尾节点
  • 最后处理下使链表形成循环链表即可
"""
# Definition for a Node.
class Node:
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
"""
class Solution:
    def __init__(self):
        self.pre = None
        self.head = None

    def treeToDoublyList(self, root: 'Node') -> 'Node':
        if not root:
            return None
        def inorder(root):
            if not root:
                return
            inorder(root.left)
            if not self.pre:
                self.head = root
            else:
                root.left,self.pre.right = self.pre,root
            self.pre = root
            inorder(root.right)
        inorder(root)
        self.pre.right,self.head.left = self.head,self.pre
        return self.head

面试题37. 序列化二叉树

思路:

  • 题目要求我们将二叉树进行前序遍历序列化,再根据前序遍历的顺序进行反序列化
  • 难点主要在如何表示空节点上,只要处理了这个空节点就很好做了,利用‘$’代表空节点即可,反序列化的时候遇到这个符号,就返回None
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Codec:

    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        if not root:
            return []
        res = []
        def preorder(root):
            if not root:
                res.append('$')
            else:
                res.append(root.val)
                preorder(root.left)
                preorder(root.right)
        preorder(root)
        return res


    def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        if not data:
            return None
        x = data.pop(0)
        if x=='$':
            return None
        else:
            root = TreeNode(int(x))
            root.left = self.deserialize(data)
            root.right = self.deserialize(data)
        return root
        

# Your Codec object will be instantiated and called as such:
# codec = Codec()
# codec.deserialize(codec.serialize(root))

面试题38. 字符串的排列

思路:

  • 我的思路是用dfs+回溯,每条路都走一遍,用set存结果,防止重复
class Solution:
    def permutation(self, s: str) -> List[str]:
        if not s:
            return []
        n = len(s)
        visited = [0]*len(s)
        res = set()
        def dfs(cur, path):
            if cur >= n:
                return
            path.append(s[cur])
            visited[cur] = 1
            if len(path) == n:
                res.add(''.join(path))
            dic = set()
            for i in range(n):
                if visited[i] == 0:
                    dfs(i, path)
            path.pop()
            visited[cur] = 0
        for i in range(len(s)):
            dfs(i, [])
        return list(res)

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

思路:

  • 此题采用哈希表,当当前数字的出现次数超过数组长度一半,则直接返回该数字
class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        dic = {}
        n = len(nums)
        for num in nums:
            dic[num] = dic.get(num,0) + 1
            if dic[num] > n//2:
                return num

面试题40. 最小的k个数

思路:

  • 采用快排,每一轮快排,会固定一个位置,当固定的位置正好是k-1,则直接输出arr[:k]即可
 return self.quick_sort(arr, k)
    
    def quick_sort(self, arr, k):
        left = 0
        right = len(arr)-1
        while left<right:
            p = self.partition(left, right, arr)
            if p == k-1:
                break
            elif p > k-1:
                right = p-1
            elif p < k-1:
                left = p+1
        return arr[:k]
    
    def partition(self, i, j, arr):
        while True:
            while arr[i]<arr[j]:
                i += 1
            else:
                arr[i],arr[j] = arr[j],arr[i]
                if i>=j:
                    break
                j -= 1
            while arr[i]<arr[j]:
                j -= 1
            else:
                arr[i],arr[j] = arr[j],arr[i]
                if i>=j:
                    break
                i += 1
        return i

面试题41. 数据流中的中位数

思路:

  • 这道题单纯的在需要计算中位数时进行排序也可以,但那不是最优的方法
  • 最优的方法是维持两个优先队列(大顶堆,小顶堆),比中位数小的全部在大顶堆中,且堆顶是小于中位数的最大数。比中位数大的全部在小顶堆中,且堆顶是大于中位数的最小数,或者就是中位数本身。
  • 由于python中没有大顶堆,只能用乘以一个负数表示
class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.min_heap = []
        self.max_heap = []


    def addNum(self, num: int) -> None:
        if len(self.min_heap) == len(self.max_heap):
            heapq.heappush(self.min_heap, -heapq.heappushpop(self.max_heap, -num))
        else:
            heapq.heappush(self.max_heap, -heapq.heappushpop(self.min_heap, num))


    def findMedian(self) -> float:
        if len(self.min_heap) == len(self.max_heap):
            return (-self.max_heap[0] + self.min_heap[0])/2
        else:
            return self.min_heap[0]



# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()

面试题42. 连续子数组的最大和

思路:

  • 若当前连续子数组和小于0,则后面不管加什么都不会是最大和。则将其清0
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        res = 0
        max_res = nums[0]
        for num in nums:
            res += num
            max_res = max(max_res, res)
            if res<0:
                res = 0
        return max_res

面试题43. 1~n整数中1出现的次数

思路:

class Solution:
    def countDigitOne(self, n: int) -> int:
        res = 0
        i = 1
        while n // i:
            high = n//(i*10)
            cur = (n//i)%10
            low = n - (n//i)*i
            if cur == 0:
                res += high*i
            elif cur == 1:
                res += high*i + low + 1
            else:
                res += (high+1)*i
            i *= 10
        return res

面试题44. 数字序列中某一位的数字

思路:

  • 1-9:占9x1下标;10-99:占90x2个下标;100-999:占900x3个下标....以此类推
  • 根据给的n依次减掉下标占的数,得到目标是几位数(注意一位下标还有个0要处理)
  • 此时的n是digits位下标中的第n个,对digits整除得到是digits位中的第几个数,取余得到target中的第几位
class Solution:
    def findNthDigit(self, n: int) -> int:
        base = 9
        digits = 1
        if n > 9:
            n -= 1
        else:
            return n
        while n - digits*base > 0:
            n -= digits*base
            base *= 10
            digits += 1
        # print(digits)
        idx = n % digits
        number = 1
        for i in range(1, digits):
            number *= 10
        target = number + n//digits
        res = str(target)
        # print(res)
        return int(res[idx])

面试题45. 把数组排成最小的数

思路:

  • 通过写比较函数__lt__
class CMP(str):
    def __lt__(self, y):
        return self+y<y+self
class Solution:
    def minNumber(self, nums: List[int]) -> str:
        res = sorted(map(str,nums), key=CMP)
        # print(res)
        return ''.join(res)

面试题46. 把数字翻译成字符串

思路:

  • 这道题用动态规划,找到状态方程,dp[i]代表第i位共有几种组合方式
  • 转移方程dp[i]=dp[i-1]+dp[i-2](i位和i-1位组合在一起也可以表示一个字母),dp[i]=dp[i-1](i位和i-1位组合在一起不能表示一个字母)
  • 初始,dp[0]=dp[1]=1
class Solution:
    def translateNum(self, num: int) -> int:
        s = str(num)
        n = len(s)
        dp = [0]*(n+1)
        dp[0] = dp[1] = 1
        if n < 2:
            return dp[n]
        for i in range(2, n+1):
            if s[i-2]=='1' or (s[i-2]=='2' and s[i-1]<'6'):
                dp[i] = dp[i-1]+dp[i-2]
            else:
                dp[i] = dp[i-1]
        return dp[n]

面试题47. 礼物的最大价值

思路:

  • 这道题我一开始使用dfs像遍历完所有路径找到最大的但是会超时
  • 这道题可以使用动态规划,dp[i][j]代表当前(i,j)所能拿到的最大礼物价值
  • dp[i][j]=max(dp[i-1][j],dp[i][j-1])+grid[i][j],因为只能往下或者往右走,则往会看左边或者上面哪个的值更大就从那边走然后加上当前位置。
  • dp[0][0....cols-1],dp[0...rows-1][0]得到边界的初始值
class Solution:
    def maxValue(self, grid: List[List[int]]) -> int:
        rows,cols= len(grid),len(grid[0])
        dp = [[0]*cols for _ in range(rows)]
        dp[0][0] = grid[0][0]
        for i in range(1,cols):
            dp[0][i] = dp[0][i-1] + grid[0][i]
        for i in range(1,rows):
            dp[i][0] = dp[i-1][0] + grid[i][0]
        for i in range(1, rows):
            for j in range(1, cols):
                dp[i][j] = max(dp[i][j-1], dp[i-1][j]) + grid[i][j]
        return dp[rows-1][cols-1]

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

思路:

  • 利用滑动窗口,i记录目标字符串的头,j记录目标字符串的尾
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if len(s)==0:
            return 0
        i = 0
        max_len = 1
        for j in range(1, len(s)):
            while s[j] in s[i:j]:
                i += 1
            else:
                max_len = max(max_len, j-i+1)
        return max_len

面试题49. 丑数

思路:

  • 丑数只能由2,3,5为因子组成,可以设置三个指针p2,p3,p5分别代表当前因子所能乘到的地方
  • 维护一个dp列表,i表示第i个丑数,每轮用三个指针所指位置乘以自己的因子,得到的结果取最小,即为当前位置的丑数
class Solution:
    def nthUglyNumber(self, n: int) -> int:
        p2,p3,p5 = 1,1,1
        dp = [0]*(n+1)
        dp[1] = 1
        for i in range(2, n+1):
            dp[i] = min(dp[p2]*2, dp[p3]*3, dp[p5]*5)
            if dp[i] == dp[p2]*2:
                p2 += 1
            if dp[i] == dp[p3]*3:
                p3 += 1
            if dp[i] == dp[p5]*5:
                p5 += 1
        return dp[n]

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

思路:

  • 用哈希做,注意处理字符串为空和没有答案的情况
class Solution:
    def firstUniqChar(self, s: str) -> str:
        if not s:
            return " "
        dic = {}
        for ch in s:
            dic[ch] = dic.get(ch,0) + 1
        for k,v in dic.items():
            if v == 1:
                return k
        return " "

面试题51. 数组中的逆序对

(埋个坑)

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

思路:

# 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:
        if not headA or not headB:
            return None
        pa, pb = headA, headB
        while pa != pb:
            pa = pa.next if pa else headB
            pb = pb.next if pb else headA
        return pa
        

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

思路:

  • 注意这个题又是排序树组,第一反应可以考虑二分法
  • 这里用二分法寻找左右边界即可
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if target not in nums:
            return 0
        n = len(nums)
        i,j = 0, n-1
        # 找左边界
        while i <= j:
            mid = (i+j)//2
            if nums[mid] < target:
                i = mid+1
            elif nums[mid] >= target:
                j = mid-1
        l = j
        i, j = 0, n-1
        # 找右边界
        while i <= j:
            mid = (i+j)//2
            if nums[mid] <= target:
                i = mid+1
            elif nums[mid] > target:
                j = mid-1
        r = i
        # print(str(l)+" "+str(r))
        return r-l-1

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

思路:

  • 同样有序数组考虑二分法
  • 若nums[mid]=mid说明缺失的数字在[mid+1,r]中
  • 若nums[mid]!=mid说明缺失的数字在[l,mid-1]中
class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        l,r = 0, len(nums)-1
        while l <= r:
            mid = (l+r)//2
            if nums[mid] == mid:
                l = mid+1
            else:
                r = mid-1
        return l

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

思路:

  • 利用二叉搜索树的性质,中序遍历是个递增序列,即可知道第k大的节点
  • 也可以考虑右根左这样的遍历顺序,就可以得到递减序列。
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:
        res = []
        self.inorder(root, res)
        return res[-k]
    
    def inorder(self, root, res):
        if not root:
            return
        self.inorder(root.left, res)
        res.append(root.val)
        self.inorder(root.right, res)

面试题55 - I. 二叉树的深度

思路:

  • 这道题主要求解二叉树的深度,可以用dfs深度遍历,同时记录当前depth,用个全局变量max_depth来记录最大深度
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        self.max_depth = 0
        if not root:
            return self.max_depth
        def recur(root, depth):
            if not root:
                return
            self.max_depth = max(depth, self.max_depth)
            recur(root.left, depth+1)
            recur(root.right, depth+1)
        recur(root, 1)
        return self.max_depth

面试题55 - II. 平衡二叉树

思路:

  • 平衡二叉树可以运用递归来做
  • 写一个确定当前节点的高度的函数
  • 然后递归地判断当前节点的左右子节点的高度差,不满足平衡条件返回false
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        if not root:
            return True
        if abs(self.getHeight(root.left)-self.getHeight(root.right))>1:
            return False
        else:
            return self.isBalanced(root.left) and self.isBalanced(root.right)
    
    def getHeight(self, root):
        if not root:
            return 0
        return max(self.getHeight(root.left), self.getHeight(root.right))+1

面试题56 - I. 数组中数字出现的次数

思路:

  • 相同的数异或为0,不同的异或为1。0和任何数异或等于这个数本身。

  • 所以,数组里面所有数异或 = 目标两个数异或 。 由于这两个数不同,所以异或结果必然不为0。

  • 假设数组异或的二进制结果为10010,那么说明这两个数从右向左数第2位是不同的

  • 那么可以根据数组里面所有数的第二位为0或者1将数组划分为2个。这样做可以将目标数必然分散在不同的数组中,而且相同的数必然落在同一个数组中。

  • 这两个数组里面的数各自进行异或,得到的结果就是答案

class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        ret, index = 0, 0
        for n in nums:
            ret ^= n
        while ret & 1 == 0:
            index += 1
            ret >>= 1
        r1, r2 = 0, 0
        for n in nums:
            if (n >> index) & 1 == 0:
                r1 ^= n
            else:
                r2 ^= n
        return [r1, r2]

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

思路:

  • 当遇到目标数字只是一个的时候,可以直接用数学的方式解决
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        return (sum(set(nums))*3 - sum(nums))//2

面试题57. 和为s的两个数字

思路:

  • 又是一个有序的列表,考虑双指针i,j分别指向列表的头尾
  • 如果i,j指针相加的值等于target,则返回结果,若大于说明j的值过大,应该减小,若小于说明i的值过小,应该增大
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        i,j = 0, len(nums)-1
        while i<j:
            if nums[i] + nums[j] > target:
                j -= 1
            elif nums[i] + nums[j] < target:
                i += 1
            elif nums[i] + nums[j] == target:
                return [nums[i], nums[j]]
        return []

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

思路:

  • 输出答案的序列依然是从小到大,考虑两个指针。
  • target最大可由target//2+1组成,之后的连续子序列之和必然大于target
class Solution:
    def findContinuousSequence(self, target: int) -> List[List[int]]:
        n = target//2 + 1
        i, j = 1, 2
        res = []
        while j<=n:
            tmp = [x for x in range(i, j+1)]
            if sum(tmp)<target:
                j += 1
            elif sum(tmp)>target:
                i += 1
            elif sum(tmp)==target:
                res.append(tmp)
                j += 1
        return res
            

面试题58 - I. 翻转单词顺序

思路:

  • 利用strip函数去掉头尾的空格
  • 利用split函数根据空格将单词分隔开
  • 若列表里有空的,则略过
class Solution:
    def reverseWords(self, s: str) -> str:
        s = s.strip()
        if not s:
            return ""
        lis = s.split(" ")
        res = []
        for i in range(len(lis)):
            if lis[i] != '':
                res.append(lis[i])
        return ' '.join(res[::-1])

面试题58 - II. 左旋转字符串

思路:

  • 常用方法,(a,b)->(a^-1,b^-1)->(b,a)
  • 其中^-1代表取反的意思,可以自己写个reverse函数,也可以直接用python[::-1]实现(验证后面的方法更省时)
class Solution:
    def reverseLeftWords(self, s: str, n: int) -> str:
        if len(s) == 1:
            return s
        sa = s[:n]
        sb = s[n:]
        s = sa[::-1]+sb[::-1]
        return s[::-1]
    
    # def reverse(self, s):
    #     s = list(s)
    #     length = len(s)
    #     i,j=0,length-1
    #     while i<j:
    #         s[i], s[j] = s[j], s[i]
    #         i += 1
    #         j -= 1
    #     return ''.join(s)

面试题59 - I. 滑动窗口的最大值

思路:

  • 针对这道题,我们可以维护一个优先队列,始终保持队头是当前窗口里最大的值的下标
  • 之所以存储的是下标,是因为若队头的下标已经不在滑动窗口里的话,是需要弹出的
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        res = []
        deque = []
        for i,num in enumerate(nums):
            while deque and num > nums[deque[-1]]:
                deque.pop()
            deque.append(i)
            if i<k-1:
                continue
            while i-k>=deque[0]:
                deque.pop(0)
            index = deque[0]
            res.append(nums[index])
        return res

面试题59 - II. 队列的最大值

思路:

  • 这一题和上面一题类似,维持一个队列
class MaxQueue:

    def __init__(self):
        self.dequeue  = []
        self.queue = []

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


    def push_back(self, value: int) -> None:
        self.queue.append(value)
        while self.dequeue and value>self.dequeue[-1]:
            self.dequeue.pop()
        self.dequeue.append(value)

    def pop_front(self) -> int:
        res = self.queue[0] if self.queue else -1
        if self.queue:
            self.queue.pop(0)
        if res == self.dequeue[0]:
            self.dequeue.pop(0)
        return res



# Your MaxQueue object will be instantiated and called as such:
# obj = MaxQueue()
# param_1 = obj.max_value()
# obj.push_back(value)
# param_3 = obj.pop_front()

面试题60. n个骰子的点数

思路:

  • 这是一道模拟题,计算n个骰子掷出来的结果值出现的概率
  • n个骰子掷出来的结果肯定在(n~6*n)
  • 所有可能的结果为6^n,所以只需计算n个骰子掷出来的结果值出现的次数分别除以6^n即可
  • 利用dp[i][j]来表示i个骰子投出j出现的次数
  • 状态转移方程是dp[i][j]=dp[i][j]+dp[i-1][j-k],当j可以由j-k和k得到(其中j-k肯定不能小于i)
  • 边界,dp[1][1....6]=1
class Solution:
    def twoSum(self, n: int) -> List[float]:
        dp = [[0]*(6*n+1) for _ in range(n+1)]
        for i in range(1, 7):dp[1][i] = 1
        for i in range(2, n+1):
            for j in range(i, i*6+1):
                for k in range(1, 7):
                    if j-k < i-1:break
                    dp[i][j] += dp[i-1][j-k]
        return [x/6**n for x in dp[n][n:6*n+1]]


面试题61. 扑克牌中的顺子

思路:

  • 五张牌的顺子,最大值和最小值的差肯定是小于等于5的
  • 遇到0直接continue
  • 同时遇到重复数字肯定不是顺子,返回false
class Solution:
    def isStraight(self, nums: List[int]) -> bool:
        dic={
            'A':1,
            'J':11,
            'Q':12,
            'K':13
        }
        min_,max_ = 14, -1
        for index,num in enumerate(nums):
            if num == 0:
                continue
            if num in nums[:index]:
                return False
            if num in ['A','J','Q','K']:
                num = dic[num]
            min_ = min(min_, num)
            max_ = max(max_, num)
        if max_-min_+1>5:
            return False
        return True

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

思路:

  • 每轮删除的下标为(start+m-1+len)%len
  • 找到下标后pop出来,同时更新start
  • 直到圆圈中只有一个数了,就返回结果
class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        circle = [x for x in range(n)]
        start = 0
        while len(circle)!=1:
            index = (start+m-1+len(circle))%len(circle)
            circle.pop(index)
            start = index
        return circle[0]

面试题63. 股票的最大利润

思路:

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        dp = [[0,0] for _ in range(len(prices)+1)]
        dp[0][0]=0
        dp[0][1]=-float('INF')
        for i in range(1, len(prices)+1):
            dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i-1])
            dp[i][1] = max(dp[i-1][1], 0-prices[i-1])
        return dp[len(prices)][0]

面试题64. 求1+2+…+n

思路:

  • 利用递归相加,若当前数字为0,则返回0即可
  • [更新]题目要求不能使用if、for等关键词。则for->递归,if->逻辑与即可判断
class Solution:
    def sumNums(self, n: int) -> int:
        return n and (n + self.sumNums(n-1))

面试题65. 不用加减乘除做加法

思路:

  • 题目要求不能用加减乘除做加法,那整个思路只能往位运算想
  • n=a⊕b(非进位和:异或运算)
  • c=a&b<<1​(进位:与运算+左移一位)
class Solution:
    def add(self, a: int, b: int) -> int:
        while b:
            num = (a^b)&0xFFFFFFFF
            carry = ((a&b)<<1)&0xFFFFFFFF
            a = num
            b = carry
        return a if a <= 0x7FFFFFFF else ~(a^0xFFFFFFFF)

面试题66. 构建乘积数组

思路:

  • 将每一项的乘子写出来,可以划分成两个数组
  • A_L即A0...Ai-1的部分,A_R即Ai+1...An-1的部分
class Solution:
    def constructArr(self, a: List[int]) -> List[int]:
        n = len(a)
        if n==0:
            return []
        A_L = [0 for _ in range(n)]
        A_R = [0 for _ in range(n)]
        A_L[0] = 1
        A_R[n-1] = 1
        for i in range(1, n):
            A_L[i] = A_L[i-1]*a[i-1]
        for i in range(n-2, -1, -1):
            A_R[i] = A_R[i+1]*a[i+1]
        B = [0 for _ in range(n)]
        for i in range(n):
            B[i] = A_L[i]*A_R[i]
        return B

面试题67. 把字符串转换成整数

思路:

  • 处理好几种情况即可
  • 首先是开头含有空格删除掉,第一个非空字符不是数字或者正负号返回0 ,只有正负号也返回0 ,只获取第一节有效数据
  • 若是按超过了[-2^31,2^31-1]只输出最值即可,因为python中没有溢出,需要我们自己判断
class Solution:
    def strToInt(self, str: str) -> int:
        # 去除空格
        s = str.strip()
        if not s:
            return 0
        j = 0
        for i in range(len(s)):
            x = s[i]
            # print(x)
            if i == 0 and not ('0'<=x<='9') and x not in ['+','-']:
                return 0
            if x in ['+','-']:
                j = i+1
            else:
                j = i
            while j < len(s) and ('0'<=s[j]<='9'):
                j += 1
            else:
                if len(s[i:j]) and (s[i:j] in ['+', '-']):
                    return 0
                break
        num = int(s[i:j])
        INT_MIN = -2**31
        INT_MAX = 2**31-1
        if num < INT_MIN:
            return INT_MIN
        elif num > INT_MAX:
            return INT_MAX
        else:
            return num

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

思路:

  • 二叉搜索树的特点,左节点小于根节点,右节点大于根节点
  • 从根结点出发,若当前节点比pq都大,说明最近公共祖先应该在左子树中。若当前节点比pq都小,说明最近公共祖先应该在右子树中。
  • 最后直接返回答案即可
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

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

面试题68 - II. 二叉树的最近公共祖先

思路:

  • 没有了上一题的二叉搜索树的限定,那就利用二叉树最本质的特点,递归
  • 递归的在左子树、右子树中寻找最近公共祖先,若当前为空则返回,若当前节点就是寻找节点也直接返回。
  • 若这个祖先不是根节点,则其必定在左子树或者右子树中
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if not root or root==p or root==q:
            return root
        L = self.lowestCommonAncestor(root.left, p, q)
        R = self.lowestCommonAncestor(root.right, p, q)
        if not L:
            return R
        if not R:
            return L
        return root

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章