劍指offer刷題合集
阿新 • • 發佈:2021-10-05
參考大神https://blog.csdn.net/zjulyx1993/article/details/108327108
1.劍指 Offer 03. 陣列中重複的數字(陣列)
1 """ 找出陣列中重複的數字。 2 3 在一個長度為 n 的陣列 nums 裡的所有數字都在 0~n-1 的範圍內。陣列中某些數字是重複的,但不知道有幾個數字重複了,也不知道每個數字重複了幾次。請找出陣列中任意一個重複的數字。 4 2 <= n <= 100000 5 6 示例 1: 7 8 輸入: 9 [2, 3, 1, 0, 2, 5, 3] 10 輸出:2 或 3 11 12 """ 13 14#集合法,依次將列表中的數放入集合,若已在集合中重複,則輸出,時間複雜度O(n),空間複雜度O(n) 15 def findRepeatNumber1(nums): 16 d = set() 17 for i in nums: 18 if i not in d: 19 d.add(i) 20 else: 21 return i 22 23 #注意數字範圍在0~n-1之間, 這說明每個數都可以放到等同於其自身值的下標中,時間複雜度O(n),空間複雜度降到O(1) 24 def findRepeatNumber2(nums):25 for i in range(len(nums)): 26 27 # for迴圈中每遍歷一次位置i,會一直交換至nums[i]==i 28 while i != nums[i]: 29 # 當前下標i和值nums[i]不相等, 進入/繼續內層迴圈,如果相等則說明此位置符合要求 30 j = nums[i] 31 if nums[i] == nums[j]: 32 # 前提條件: i!=j, 所以nums[i]和nums[j]是陣列的兩個數字, 它們值相等, 即為重複數字33 return nums[i] 34 # 交換兩個值, 使得nums[j] == j, 這樣可以繼續迴圈判斷i和新的nums[i] 35 nums[i], nums[j] = nums[j], nums[i] 36 37 return -1 #如果給定的陣列沒有重複值的話,返回-1
2.劍指 Offer 04. 二維陣列中的查詢(陣列)
1 """ 在一個 n * m 的二維陣列中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個高效的函式,輸入這樣的一個二維陣列和一個整數,判斷陣列中是否含有該整數。 2 3 示例: 4 5 現有矩陣 matrix 如下: 6 [ 7 [1, 4, 7, 11, 15], 8 [2, 5, 8, 12, 19], 9 [3, 6, 9, 16, 22], 10 [10, 13, 14, 17, 24], 11 [18, 21, 23, 26, 30] 12 ] 13 給定 target=5,返回true。 給定target=20,返回false。 14 15 限制: 16 0 <= n <= 1000 17 0 <= m <= 1000 18 """ 19 #初始化座標為右上角, 然後判斷當前點與 target 的關係, 大於的話列號減 1, 小於的話行號+1, 直到找到等於 target 的點, 或者超出矩陣範圍 20 #時間複雜度O(R+C),空間複雜度O(1) 21 def findNumberIn2DArray(matrix, target): 22 if not matrix: 23 return False 24 row, col = len(matrix), len(matrix[0]) 25 r, c = 0, col-1 26 while r<row and c>-1: 27 if target < matrix[r][c]: 28 c -= 1 29 elif target > matrix[r][c]: 30 r += 1 31 else: 32 return True 33 return False
3.劍指 Offer 05. 替換空格(字串)
1 """ 請實現一個函式,把字串 s 中的每個空格替換成"%20"。 2 3 示例 1: 4 5 輸入:s = "We are happy." 6 輸出:"We%20are%20happy." 7 8 限制: 0 <= s 的長度 <= 10000 9 """ 10 11 def replaceSpace(s): 12 # return s.replace(' ', '%20') 13 return '%20'.join(s.split())
4.劍指 Offer 06. 從尾到頭列印連結串列(連結串列)
1 """ 輸入一個連結串列的頭節點,從尾到頭反過來返回每個節點的值(用陣列返回)。 2 3 示例 1: 4 5 輸入:head = [1,3,2] 6 輸出:[2,3,1] 7 8 限制:0 <= 連結串列長度 <= 10000 9 class Solution: 10 def reversePrint(self, head: ListNode) -> List[int]: 11 """ 12 13 # Definition for singly-linked list. 14 class ListNode: 15 def __init__(self, x): 16 self.val = x 17 self.next = None 18 19 #先正向儲存到陣列,再翻轉返回 20 def reversePrint1(head: ListNode): 21 lst = [] 22 while head: 23 lst.append(head.val) 24 head = head.next 25 return lst[::-1] 26 27 #使用遞迴 28 def reversePrint2(head: ListNode): 29 res = [] 30 31 def add(x: ListNode): 32 if not x: #遞迴出口 33 return 34 add(x.next) 35 res.append(x.val) 36 37 add(head) 38 return res 39 40 #不能翻轉,不能使用遞迴,只能用棧迭代 41 def reversePrint3(head: ListNode): 42 stack = [] 43 while head: 44 stack.append(head.val) #正向壓入棧中 45 head = head.next 46 47 res = [] 48 while stack: 49 res.append(stack.pop().val) #再一個個彈出 50 return res
5.劍指 Offer 07. 重建二叉樹(樹)
1 """ 輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。 2 3 例如,給出 4 前序遍歷 preorder =[3,9,20,15,7] 5 中序遍歷 inorder = [9,3,15,20,7] 6 返回如下的二叉樹: 7 8 3 9 / \ 10 9 20 11 / \ 12 15 7 13 14 class Solution: 15 def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode: 16 """ 17 18 # Definition for a binary tree node. 19 class TreeNode: 20 def __init__(self, x): 21 self.val = x 22 self.left = None 23 self.right = None 24 25 26 #遞迴法 27 def buildTree1(preorder, inorder): 28 if not preorder: 29 return None 30 31 valueToInorderIndex = {} #使用一個value=>inorder index的字典加速運算, 為了使得查詢中序下標的操作變為O(1), 不需要掃描整個中序陣列 32 n = len(preorder) 33 34 for i in range(n): 35 valueToInorderIndex[inorder[i]] = i 36 37 def build(pb, pe, ib, ie): #(pb, pe)是當前前序遍歷的起點和終點 (ib, ie)是當前中序遍歷的起點和終點 38 if pb > pe: 39 return None #遞迴出口 40 41 root = TreeNode(preorder[pb]) #前序遍歷的當前第一個元素即為當前的根節點 42 if pb == pe: 43 return root 44 45 im = valueToInorderIndex[root.val] #根節點對應的中序遍歷的下標,以此作為分界點 46 pm = pb + im - ib #根節點對應的前序遍歷的下標(對應部分的前序和中序長度應該相等, 即im-ib=pm-pb,由此得出) 47 48 #前序[pb ]pm[ pe] 49 #中序[ib ]im[ ie] 50 root.left = build(pb + 1, pm, ib, im-1) #左子樹部分,前序(pb+1, pm),中序(ib, im-1) 51 root.right = build(pm + 1, pe, im + 1, ie) #右子樹部分,前序(pm+1, pe),後序(im+1, ie) 52 53 return root 54 return build(0, n-1, 0, n-1) 55 56 #迭代法 57 def buildTree2(preorder, inorder): 58 if not preorder: 59 return None 60 61 root = TreeNode(preorder[0]) 62 stack = [root] 63 64 ii = 0 #ii表示當前的中序下標 65 66 for pi in range(1, len(preorder)): 67 prenode = stack[-1] #記錄上一個棧頂節點,一定不為空 68 curnode = TreeNode(preorder[pi]) #當前節點curnode位於上一節點的左子樹或者右子樹 69 70 if prenode.val != inorder[ii]: #上一個節點不是當前中序節點, 意味著現在還沒到上一個節點的右邊部分, 所以當前節點位於左子樹, 71 prenode.left = curnode 72 else: #上一節點就是當前中序節點,意味著當前節點在右子樹上 73 while stack and ii < len(inorder) and stack[-1].val == inorder[ii]: 74 prenode = stack.pop() #找最上層一個(也即倒置前序序列最後一個)與當前中序節點相同的節點 75 ii += 1 76 prenode.right = curnode #那個節點的右兒子就是當前節點 77 78 stack.append(curnode) #將當前節點加入stack中, 作為下次迴圈的上一個節點 79 80 return root
6.劍指 Offer 09. 用兩個棧實現佇列(棧/佇列)
1 """ 用兩個棧實現一個佇列。佇列的宣告如下,請實現它的兩個函式 appendTail 和 deleteHead ,分別完成在佇列尾部插入整數和在佇列頭部刪除整數的功能。(若佇列中沒有元素,deleteHead操作返回 -1 2 3 示例 1: 4 輸入: 5 ["CQueue","appendTail","deleteHead","deleteHead"] 6 [[],[3],[],[]] 7 輸出:[null,null,3,-1] 8 9 示例 2: 10 輸入: 11 ["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"] 12 [[],[],[5],[2],[],[]] 13 輸出:[null,-1,null,null,5,2] 14 15 提示:1 <= values <= 10000,最多會對appendTail、deleteHead 進行10000次呼叫 16 """ 17 # 兩個佇列stack1和stack2,佇列尾部插入資料,即資料壓入stack1,佇列頭部刪除整數,判斷三種情況 18 # 1.第二個棧不為空,則從第二個棧中彈出資料 2.第一個棧不為空,則從第一個棧中把元素依次彈出並壓入第二個棧中 3.第一個棧為空,即刪除失敗 19 class CQueue: 20 def __init__(self): 21 self.stack1 = [] 22 self.stack2 = [] 23 24 def appendTail(self, value: int): #在佇列尾部插入整數 25 self.stack1.append(value) 26 27 def deleteHead(self): #在佇列頭部刪除整數 28 if self.stack2: #對應情況1 29 return self.stack2.pop() 30 if not self.stack1: #對應情況3 31 return -1 32 33 while self.stack1: #對應情況2 34 x = self.stack1.pop() 35 self.stack2.append(x) 36 return self.stack2.pop() 37 38 39 # Your CQueue object will be instantiated and called as such: 40 # obj = CQueue() 41 # obj.appendTail(value) 42 # param_2 = obj.deleteHead()
7.劍指 Offer 10- I. 斐波那契數列(動態規劃)
1 """ 寫一個函式,輸入 n ,求斐波那契(Fibonacci)數列的第 n 項(即 F(N))。斐波那契數列的定義如下: 2 F(0) = 0, F(1)= 1 3 F(N) = F(N - 1) + F(N - 2), 其中 N > 1. 4 斐波那契數列由 0 和 1 開始,之後的斐波那契數就是由之前的兩數相加而得出。 5 6 答案需要取模 1e9+7(1000000007),如計算初始結果為:1000000008,請返回 1。 7 """ 8 9 def fib(n): 10 if n < 2: 11 return n 12 13 x, y, z = 0, 1, 1 14 while n-2: 15 x, y = y, z 16 z = x + y 17 n = n - 1 18 return z % 1000000007 19 20 #用陣列更清晰 21 def fib2(n): 22 if n < 2: 23 return n 24 25 dp=[0]*(n+1) 26 dp[1]=1 27 for k in range(2, n+1): 28 dp[k] = dp[k-1] + dp[k-2] 29 return dp[-1]%1000000007
8.劍指 Offer 10- II. 青蛙跳臺階問題(動態規劃)
本質和上一題一樣
9.劍指 Offer 11. 旋轉陣列的最小數字(二分查詢)
1 """ 把一個數組最開始的若干個元素搬到陣列的末尾,我們稱之為陣列的旋轉。 2 輸入一個遞增排序的陣列的一個旋轉,輸出旋轉陣列的最小元素。 3 例如,陣列[3,4,5,1,2] 為 [1,2,3,4,5] 的一個旋轉,該陣列的最小值為1。 4 5 輸入:[3,4,5,1,2] 6 輸出:1 7 8 輸入:[2,2,2,0,1] 9 輸出:0 10 """ 11 #題目的意思是陣列有兩段遞增序列,找分段點,如果只有一段遞增序列,那就是開頭 12 #二分查詢 13 def minArray(numbers): 14 n = len(numbers) 15 i, j = 0, n-1 16 while i < j: 17 mid = (i + j) // 2 18 if numbers[mid] < numbers[j]: #如果中間值小於末尾, 那麼一定說明該數字之後(後半段)有序. 19 j = mid 20 elif numbers[mid] > numbers[j]: #如果中間值大於末尾, 那麼毫無疑問後半段無序. 21 i = mid + 1 22 else: #如果中間值等於末尾, 那就不好判斷是前半段無序還是後半段無序,退化為逐個遍歷 23 j -= 1 24 return numbers[i]
類似的還有面試題
面試題 10.03. 搜尋旋轉陣列
1 """ 搜尋旋轉陣列。給定一個排序後的陣列,包含n個整數,但這個陣列已被旋轉過很多次了,次數不詳。 2 請編寫程式碼找出陣列中的某個元素,假設陣列元素原先是按升序排列的。若有多個相同元素,返回索引值最小的一個 3 4 輸入: arr = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14], target = 5 5 輸出: 8(元素5在該陣列中的索引) 6 """ 7 8 #理解一下:旋轉一次和旋轉多次沒有任何區別, 最終還是隻有一個旋轉點, 以及不多於 2 個的有序區間 9 def search(arr, target): 10 left, right = 0, len(arr) - 1 11 while left <= right: 12 13 mid = (left + right) // 2 14 if arr[left] == target: 15 return left 16 elif arr[mid] == target: #mid和right值等於target時,未必是索引值最小的,還要繼續遍歷 17 right = mid 18 elif arr[right] == target: 19 left = mid + 1 20 elif arr[mid] < arr[right]: #後半段有序 21 if arr[mid] < target < arr[right]: #target在後半段裡 22 left = mid + 1 23 else: #target在前半段裡 24 right = mid - 1 25 elif arr[mid] > arr[right]: #前半段有序 26 if arr[left] < target < arr[mid]: #target在前半段裡 27 right = mid - 1 28 else: #target在後半段裡 29 left = mid + 1 30 else: #前後段誰有序不清楚,逐一遍歷 31 right -= 1 32 return -1