目錄
注意
題源是在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. 數值的整數次方
思路:
- 我一開始就是用循環一次次的乘,但最後超出了時間限制
- 看了題解,這個寫的很好(https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/solution/mian-shi-ti-16-shu-zhi-de-zheng-shu-ci-fang-kuai-s/)
- 利用二分快速冪
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出現的次數
思路:
- 這題只能靠總結規律來做,我每次都會忘記。參考了這個題解(https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/solution/wo-de-jian-dan-jie-fa-by-ai-bian-cheng-de-zhou-n-2/)
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. 兩個鏈表的第一個公共節點
思路:
- 尋找兩個鏈表的公共節點,利用雙指針法(https://leetcode-cn.com/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/solution/shuang-zhi-zhen-fa-lang-man-xiang-yu-by-ml-zimingm/)可以參考這篇有圖畫出來更方便理解
# 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. 股票的最大利潤
思路:
- 經典的股票買賣問題,具體操作可以看https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/yi-ge-fang-fa-tuan-mie-6-dao-gu-piao-wen-ti-by-l-3/
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