來(lái)源leetcode熱題HOT100中46,98,105,114,207,337,394
46.全排列
- 題目描述
- 給定一個(gè) 沒(méi)有重復(fù) 數(shù)字的序列,返回其所有可能的全排列。
- 示例
輸入: [1,2,3]
輸出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
- 題解:
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
# 深度搜索主程序
def dfs(nums,size,depth,path,used,res):
if depth == size:
res.append(path[:])
return
for i in range(size):
if not used[I]:
used[i] = True
path.append(nums[I])
dfs(nums,size,depth+1,path,used,res)
used[i] = False
path.pop()
'''
depth:表示當(dāng)前遞歸到第幾層
為了區(qū)分不同階段,用兩個(gè)狀態(tài)變量記錄:
(1)path:已經(jīng)選了哪些數(shù),到葉節(jié)點(diǎn)時(shí),這些數(shù)就構(gòu)成一個(gè)全排列
(2)布爾數(shù)組used,初始化為false表示都沒(méi)有被選擇
'''
size = len(nums)
if len(nums) == 0:
return []
used = [False for _ in range(size)]
res = []
dfs(nums,size,0,[],used,res)
return res
98.驗(yàn)證二叉搜索樹
- 題目描述
- 給定一個(gè)二叉樹,判斷其是否是一個(gè)有效的二叉搜索樹。
假設(shè)一個(gè)二叉搜索樹具有如下特征:
節(jié)點(diǎn)的左子樹只包含小于當(dāng)前節(jié)點(diǎn)的數(shù)。
節(jié)點(diǎn)的右子樹只包含大于當(dāng)前節(jié)點(diǎn)的數(shù)。
所有左子樹和右子樹自身必須也是二叉搜索樹。
- 給定一個(gè)二叉樹,判斷其是否是一個(gè)有效的二叉搜索樹。
- 示例
示例 1:
輸入:
2
/ \
1 3
輸出: true
示例 2:
輸入:
5
/ \
1 4
/ \
3 6
輸出: false
解釋: 輸入為: [5,1,4,null,null,3,6]。
根節(jié)點(diǎn)的值為 5 ,但是其右子節(jié)點(diǎn)值為 4 。
- 題解
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
def dfs(node, min_val, max_val):
if not node: # 邊界條件,如果node為空肯定是二叉搜索樹
return True
# 如果當(dāng)前節(jié)點(diǎn)超出上下界范圍,肯定不是
if not min_val < node.val < max_val:
return False
# 走到下面這步說(shuō)明已經(jīng)滿足了如題所述的二叉搜索樹的前兩個(gè)條件
# 那么只需要遞歸判斷當(dāng)前節(jié)點(diǎn)的左右子樹是否同時(shí)是二叉搜索樹即可
return dfs(node.left, min_val, node.val) and dfs(node.right, node.val, max_val)
# 對(duì)于根節(jié)點(diǎn),它的上下限為無(wú)窮大和無(wú)窮小
return dfs(root, float('-inf'), float('inf'))
105.從前序和中序遍歷序列構(gòu)造二叉樹
- 題目描述
- 根據(jù)一棵樹的前序遍歷與中序遍歷構(gòu)造二叉樹。
- 示例
前序遍歷 preorder = [3,9,20,15,7]
中序遍歷 inorder = [9,3,15,20,7]
返回如下的二叉樹:
3
/ \
9 20
/ \
15 7
- 題解
# 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 not preorder or not inorder: # 遞歸終止條件
return
root = TreeNode(preorder[0]) # 先序?yàn)椤案笥摇?,所以根?jù)preorder可以確定root
idx = inorder.index(preorder[0]) # 中序?yàn)椤白蟾摇保鶕?jù)root可以劃分出左右子樹
# 下面遞歸對(duì)root的左右子樹求解即可
root.left = self.buildTree(preorder[1:1 + idx], inorder[:idx])
root.right = self.buildTree(preorder[1 + idx:], inorder[idx + 1:])
return root
114.二叉樹展開為鏈表
-
題目描述:
- 給定一個(gè)二叉樹,將它展開為一個(gè)單鏈表。
示例:
例如,給定二叉樹
1
/ \
2 5
/ \ \
3 4 6
將其展開為:
1
\
2
\
3
\
4
\
5
\
6
- 題解:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def flatten(self, root: TreeNode) -> None:
"""
Do not return anything, modify root in-place instead.
"""
while root:
if root.left: #左子樹存在的話才進(jìn)行操作
sub_left = root.left
while sub_left.right: #左子樹的右子樹找到最深
sub_left = sub_left.right
sub_left.right = root.right #將root的右子樹掛到左子樹的右子樹的最深
root.right = root.left #將root的左子樹掛到右子樹
root.left = None #將root左子樹清空
root = root.right #繼續(xù)下一個(gè)節(jié)點(diǎn)的操作
207.課程表
- 題目描述:
- 你這個(gè)學(xué)期必須選修 numCourse 門課程,記為 0 到 numCourse-1 。
在選修某些課程之前需要一些先修課程。 例如,想要學(xué)習(xí)課程 0 ,你需要先完成課程 1 ,我們用一個(gè)匹配來(lái)表示他們:[0,1]
給定課程總量以及它們的先決條件,請(qǐng)你判斷是否可能完成所有課程的學(xué)習(xí)?
- 你這個(gè)學(xué)期必須選修 numCourse 門課程,記為 0 到 numCourse-1 。
- 示例
示例 1:
輸入: 2, [[1,0]]
輸出: true
解釋: 總共有 2 門課程。學(xué)習(xí)課程 1 之前,你需要完成課程 0。所以這是可能的。
示例 2:
輸入: 2, [[1,0],[0,1]]
輸出: false
解釋: 總共有 2 門課程。學(xué)習(xí)課程 1 之前,你需要先完成?課程 0;并且學(xué)習(xí)課程 0 之前,你還應(yīng)先完成課程 1。這是不可能的
- 題解:
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
'''
有向無(wú)圈圖DAG,如果構(gòu)成環(huán)路是不成立的
通過(guò)拓?fù)渑判蚺袛嗍欠袷荄AG:
對(duì)DAG對(duì)頂點(diǎn)進(jìn)行排序,使得每一條有向邊(u,v),均符合y比v先出現(xiàn)
DFS解法
借助一個(gè)列表flags用于判斷每個(gè)節(jié)點(diǎn)的狀態(tài):
未被訪問(wèn),i==0
已被其他節(jié)點(diǎn)啟動(dòng)的DFS訪問(wèn),i==-1
已被當(dāng)前節(jié)點(diǎn)啟動(dòng)的DFS訪問(wèn),i==1
對(duì)numCourses個(gè)節(jié)點(diǎn)依次執(zhí)行DFS,判斷是否有環(huán):
當(dāng)flags[i]==-1,被其他節(jié)點(diǎn)訪問(wèn)過(guò),返回True
當(dāng)flags[i]==1,已在本輪搜索中訪問(wèn)過(guò),即有環(huán),返回False
將當(dāng)前節(jié)點(diǎn)i對(duì)應(yīng)flags[i]置為1,表示本輪訪問(wèn)過(guò)
遞歸訪問(wèn)當(dāng)前節(jié)點(diǎn)i的所有鄰接節(jié)點(diǎn)j,發(fā)現(xiàn)環(huán)返回False
當(dāng)前節(jié)點(diǎn)的所有鄰接節(jié)點(diǎn)也被遍歷,則當(dāng)前節(jié)點(diǎn)置為-1,返回True
如果整個(gè)圖DFS結(jié)束都沒(méi)發(fā)現(xiàn)環(huán),返回True
'''
def dfs(i,adjacency,flags):
#終止條件
if flags[i] == -1:
return True
if flags[i] == 1:
return False
#當(dāng)前點(diǎn)置為1
flags[i] = 1
#對(duì)當(dāng)前節(jié)點(diǎn)對(duì)所有鄰接點(diǎn)進(jìn)行dfs
for j in adjacency[I]:
if not dfs(j,adjacency,flags):
return False
#沒(méi)有環(huán)則當(dāng)前節(jié)點(diǎn)置為-1,下次不需要再進(jìn)行dfs了,返回True
flags[i] = -1
return True
#
adjacency = [[] for _ in range(numCourses)]
flags = [0 for _ in range(numCourses)]
for cur,pre in prerequisites:
adjacency[pre].append(cur)
#對(duì)所有節(jié)點(diǎn)進(jìn)行dfs
for i in range(numCourses):
if not dfs(i,adjacency,flags):
return False
return True
'''
BFS解法:
統(tǒng)計(jì)課程安排圖中每個(gè)節(jié)點(diǎn)的入度,生成入度表indegrees
借助一個(gè)隊(duì)列queue,將所有入度為0的節(jié)點(diǎn)入隊(duì)
當(dāng)queue不為空,依次隊(duì)首節(jié)點(diǎn)出隊(duì),在課程安排圖中刪除此節(jié)點(diǎn)pre
并不是真正從鄰接表刪除,而是此節(jié)點(diǎn)對(duì)應(yīng)所有鄰接節(jié)點(diǎn)cur-1
當(dāng)入度-1后鄰接節(jié)點(diǎn)cur的入度為0,說(shuō)明cur的前驅(qū)節(jié)點(diǎn)已經(jīng)被刪除,此時(shí)cur入隊(duì)
在每次pre出隊(duì)時(shí),執(zhí)行numCourses--
若課程安排圖是DAG,則所有節(jié)點(diǎn)都出隊(duì)入隊(duì)過(guò)
如圖中存在環(huán),則一定有節(jié)點(diǎn)的入度始終不為0
拓?fù)渑判虺鲫?duì)次數(shù)等于課程個(gè)數(shù),返回numCourses==0可以判斷是否成功
'''
#入度表
indegrees = [0 for _ in range(numCourses)]
#課程的依賴
adjacency = [[] for _ in range(numCourses)]
queue = collections.deque()
# 入度表記錄所有節(jié)點(diǎn)的入度,
for cur,pre in prerequisites:
indegrees[cur] += 1
adjacency[pre].append(cur)
#入度不為0的加入隊(duì)列
for i in range(len(indegrees)):
if not indegrees[I]:
queue.append(i)
#BFS
while queue:
#取出隊(duì)首數(shù)據(jù)
pre = queue.popleft()
numCourses -= 1
#遍歷隊(duì)首的所有鄰接元素
for cur in adjacency[pre]:
indegrees[cur] -= 1
#如果鄰接元素入度-1后還不為0,則將其加入隊(duì)列
if not indegrees[cur]:
queue.append(cur)
return numCourses == 0
337.打家劫舍三
-
題目描述
- 在上次打劫完一條街道之后和一圈房屋后,小偷又發(fā)現(xiàn)了一個(gè)新的可行竊的地區(qū)。這個(gè)地區(qū)只有一個(gè)入口,我們稱之為“根”。 除了“根”之外,每棟房子有且只有一個(gè)“父“房子與之相連。一番偵察之后,聰明的小偷意識(shí)到“這個(gè)地方的所有房屋的排列類似于一棵二叉樹”。 如果兩個(gè)直接相連的房子在同一天晚上被打劫,房屋將自動(dòng)報(bào)警。
計(jì)算在不觸動(dòng)警報(bào)的情況下,小偷一晚能夠盜取的最高金額。
- 在上次打劫完一條街道之后和一圈房屋后,小偷又發(fā)現(xiàn)了一個(gè)新的可行竊的地區(qū)。這個(gè)地區(qū)只有一個(gè)入口,我們稱之為“根”。 除了“根”之外,每棟房子有且只有一個(gè)“父“房子與之相連。一番偵察之后,聰明的小偷意識(shí)到“這個(gè)地方的所有房屋的排列類似于一棵二叉樹”。 如果兩個(gè)直接相連的房子在同一天晚上被打劫,房屋將自動(dòng)報(bào)警。
-
示例
image.png 題解
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
'''
每一個(gè)節(jié)點(diǎn)有偷與不偷兩種選擇:
每一個(gè)節(jié)點(diǎn)如果偷的話值為:
左側(cè)子節(jié)點(diǎn)不偷的值 + 右側(cè)子節(jié)點(diǎn)不偷的值 + 該節(jié)點(diǎn)的值
每一個(gè)節(jié)點(diǎn)如果不偷的話值為:
左側(cè)子節(jié)點(diǎn)的最大值 + 右側(cè)子節(jié)點(diǎn)的最大值
'''
def rob(self,root):
# a為一個(gè)二維數(shù)組,為root的[偷值,不偷值]
a = self.helper(root)
# 返回兩個(gè)值的最大值
return max(a[0],a[1])
#參數(shù)為root節(jié)點(diǎn),返回二維數(shù)組,root的[偷值,不偷值]
def helper(self,root):
if not root:
return [0,0]
left = self.helper(root.left)
right = self.helper(root.right)
# root的偷值
robValue = left[1] + right[1] +root.val
# root的不偷值
skipValue = max(left[0],left[1]) + max(right[0],right[1])
return [robValue,skipValue]
394.字符串解碼
-
題目描述
- 給定一個(gè)經(jīng)過(guò)編碼的字符串,返回它解碼后的字符串。
編碼規(guī)則為: k[encoded_string],表示其中方括號(hào)內(nèi)部的 encoded_string 正好重復(fù) k 次。注意 k 保證為正整數(shù)。
你可以認(rèn)為輸入字符串總是有效的;輸入字符串中沒(méi)有額外的空格,且輸入的方括號(hào)總是符合格式要求的。
此外,你可以認(rèn)為原始數(shù)據(jù)不包含數(shù)字,所有的數(shù)字只表示重復(fù)的次數(shù) k ,例如不會(huì)出現(xiàn)像 3a 或 2[4] 的輸入。
- 給定一個(gè)經(jīng)過(guò)編碼的字符串,返回它解碼后的字符串。
-
示例
image.png -
題解
- 題解來(lái)源于Krahets
class Solution:
def decodeString(self, s: str) -> str:
'''
輔助棧法
構(gòu)建輔助棧stack,遍歷字符串s中的每個(gè)字符c:
當(dāng)c為數(shù)字時(shí),將數(shù)字字符轉(zhuǎn)換為數(shù)字multi
當(dāng)c為字母時(shí),在res結(jié)尾添加c
當(dāng)c為[時(shí),將當(dāng)前multi和res入棧,并分別置空
記錄此[前當(dāng)臨時(shí)結(jié)果res入棧,用于發(fā)現(xiàn)對(duì)應(yīng)]后的拼接操作
記錄[前的multi入棧,用于發(fā)現(xiàn)對(duì)應(yīng)]后,獲取multi*[]字符串
當(dāng)c為]時(shí),stack出棧,拼接字符串res = last_res + cur_multi*res
last_res是上個(gè)[到當(dāng)前[的字符串
cur_multi是當(dāng)前[到]內(nèi)字符串的重復(fù)次數(shù)
'''
stack = []
res = ''
multi = 0
for c in s:
if c == '[':
stack.append([multi,res])
res,multi = '',0
elif c == ']':
cur_multi,last_res = stack.pop()
res = last_res + cur_multi * res
elif '0' <= c <= '9':
#處理數(shù)字不是個(gè)位數(shù)的情況
multi = multi*10 + int(c)
else:
res += c
return res

