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

10.劍指 Offer 12. 矩陣中的路徑(回溯法)

 1 """ 給定一個 m x n 二維字符網格 board 和一個字符串單詞 word 。如果 word 存在於網格中,返回 true ;否則,返回 false 。
 2 單詞必須按照字母順序,通過相鄰的單元格內的字母構成,其中“相鄰”單元格是那些水平相鄰或垂直相鄰的單元格。同一個單元格內的字母不允許被重復使用。
 3 
 4 輸入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
 5 輸出:true
 6 
 7 輸入:board = [["a","b"],["c","d"]], word = "abcd"
 8 輸出:false
 9 
10 """
11 
12 #直接修改board記錄每個點是否被訪問過
13 def exist(board, word):               #題干強調了同一個單元格內的字母不允許被重復使用
14   
15     def dfs(i, j, k):
16         if not 0<= i < len(board) or not 0<= j < len(board[0]) or board[i][j] != word[k]:
17             return False
18         if k == len(word) - 1:
19             return True
20         board[i][j] = ' '
21         res = dfs(i+1, j, k+1) or dfs(i-1, j, k+1) or dfs(i, j+1, k+1) or dfs(i, j-1, k+1)
22         board[i][j] = word[i][j]
23         return res
24     
25     for i in range(len(board)):
26         for j in range(len(board[0])):
27             if dfs(i, j, 0):
28                 return True
29     return False       
30 
31 #使用集合visited(C++中使用矩陣)記錄每個點是否被訪問過
32 def exist2(board, word):   
33          
34     def dfs(i, j, k, visited):
35         if not 0<= i < len(board) or not 0<= j < len(board[0]) or board[i][j] != word[k] or (i,j) in visited:
36             return False
37         if k == len(word) - 1:
38             return True
39         visited.add((i,j))
40         res = dfs(i-1, j, k+1, visited) or dfs(i+1, j, k+1, visited) or dfs(i, j-1, k+1, visited) or dfs(i, j+1, k+1, visited)
41         visited.remove((i, j))
42         return res
43 
44     for i in range(len(board)):
45         for j in range(len(board[0])):
46             visited = set()
47             if dfs(i, j, 0, visited):
48                 return True
49     return False

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM