【劍指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

 

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