1. 程式人生 > 其它 >劍指offer刷題合集

劍指offer刷題合集

參考大神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