文章目錄
1、二維數組中的查找
在一個二維數組中(每個一維數組的長度相同),每一行都按照從左到右遞增的順序排序;每一列都是按照從上到下遞增的順序排序。請完成一個函數,輸入這樣一個二維數組,判斷數組中是否含有該整數。
1.1 思路
順序排序,查找。典型的二分遞歸思路:
具體思路如下:
0- 先判斷是否在數組的數據分佈範圍內
1- 判斷是在下半行還是中間行的下半段,縮小數組
判斷是在上半行還是中間行的上半段,縮小數組
2- 當僅僅剩餘一個值時,判斷兩者是否相等
1.2 解題
# 二分遞歸
# 例子:
# arr = [[1, 2, 3, 4], [5, 6, 7, 8]
# , [9, 10, 11, 12],[15, 16, 17, 18]
# , [20, 22, 25, 27], [29, 30, 40, 41 ]]
# n_in = 28
# find_int(arr, n_in)
import copy
def find_int(arr_in, n_in):
arr = copy.deepcopy(arr_in)
m = len(arr) - 1
n = len(arr[0]) - 1
# 0. 不在查找的範圍
if (n_in < arr[0][0]) or (n_in > arr[m][n]):
return False
else:
# 對奇數偶數一同處理
m_mid = m // 2 + m % 2
n_mid = n // 2 + n % 2
# 1. 一般情況 在後面行
if (n_in > arr[m_mid][n_mid]) and (n_in > arr[m_mid][n]):
arr = arr[m_mid:]
print('now array is:', arr)
return find_int(arr, n_in)
# 2. 在中間行後半段
elif (n_in >= arr[m_mid][n_mid]) and (n_in <= arr[m_mid][n]):
arr = [arr[m_mid][n_mid:]]
print('now array is:', arr)
if (len(arr[0]) == 1):
return arr[0] == n_in
else:
return find_int(arr, n_in)
# 3. 一般情況 在前面行
elif (n_in < arr[m_mid][n_mid]) and (n_in < arr[m_mid][0]):
arr = arr[:m_mid]
print('now array is:', arr)
return find_int(arr, n_in)
# 4. 在中間行前半段
elif (n_in <= arr[m_mid][n_mid]) and (n_in >= arr[m_mid][0]):
arr = [arr[m_mid][:n_mid]]
print('now array is:', arr)
if (len(arr[0]) == 1):
return arr[0] == n_in
else:
return find_int(arr, n_in)
arr = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [15, 16, 17, 18]
, [20, 22, 25, 27], [29, 30, 40, 41]]
n_in = 26
find_int(arr, n_in)
"""
>>> find_int(arr, n_in)
now array is: [[15, 16, 17, 18], [20, 22, 25, 27],
[29, 30, 40, 41]]
now array is: [[25, 27]]
now array is: [[25]]
False
"""
2、替換空格
請實現一個函數,將一個字符串中的每個空格替換成“%20”。例如,當字符串爲We Are Happy.則經過替換之後的字符串爲We%20Are%20Happy。
2.1 思路
替換問題,顯然需要遍歷,當需要遍歷的時候就可以雙指針同時遍歷來加速:
具體思路如下:
0- 雙向同時遍歷,正向正常空格替換 %20,反向空格替換爲 02%
1- 合併前半 和 後半(逆序)字符
2- 考慮字段長度奇偶問題
2.2 解題
# 雙向遍歷
def replaceSpace(s):
s_l = len(s) - 1
s_s, s_e = 0, s_l
s_outs, s_oute = '', ''
while s_s <= s_e:
# 考慮字段長度奇偶問題
if s_s == s_e:
s_outs = s_outs + '%20' if s[s_s] == ' ' else s_outs + s[s_s]
else:
s_outs = s_outs + '%20' if s[s_s] == ' ' else s_outs + s[s_s]
s_oute = s_oute + '02%' if s[s_e] == ' ' else s_oute + s[s_e]
s_s += 1
s_e -= 1
return s_outs + s_oute[::-1]
s = 'We are happy '
replaceSpace(s)
"""
>>> replaceSpace(s)
'We%20are%20happy%20'
"""
3、從尾到頭打印鏈表
輸入一個鏈表,按鏈表值從尾到頭的順序返回一個ArrayList。
3.1 思路
讀取指針值,添加到list, 最後逆方向返回
3.2 解題
def printListFromTailToHead(self, listNode):
if not listNode:
return []
else:
p = listNode
stack = []
while p:
stack.append(p.val)
p = p.next
return stack[::-1]
4、重建二叉樹
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。
假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。
例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}
和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。
設L、D、R分別表示遍歷左子樹、訪問根結點和遍歷右子樹, 則對一棵二叉樹的遍歷有三種情況:DLR(稱爲先根次序遍歷),LDR(稱爲中根次序遍歷),LRD (稱爲後根次序遍歷)。
4.1 思路
二叉樹,遞歸思路:
0- 從DRL中找到 節點值
1- 從LDR中找到 節點的位置
2- 然後拆分成左右進行遞歸
4.2 解題
# 0 由於python 中無樹結構,需要自己構建
class Binary_Tree():
def __init__(self, value=None, left=None, right=None):
self.value = value
self.left = left
self.right = right
# 1 按照思路重建二叉樹
def reConstrauctBtree(dlr, ldr):
if dlr == []:
return None
ldr_dc = dict(zip(ldr, range(len(ldr))))
n = ldr_dc[dlr[0]]
root = Binary_Tree(dlr[0])
print('node:', dlr[0], 'left :', dlr[1:n+1], 'right :', dlr[n+1:])
root.left = reConstrauctBtree(dlr[1:n+1], ldr[:n])
root.right = reConstrauctBtree(dlr[n+1:], ldr[n+1:])
return root
dlr = [1, 2, 4, 7, 3, 5, 6, 8]
ldr = [4, 7, 2, 1, 5, 3, 8, 6]
r = reConstrauctBtree(dlr, ldr)
# 2 前項遍歷查看
def pretree(root):
print(root.value)
if root.left:
pretree(root.left)
if root.right:
pretree(root.right)
pretree(r)
>>> r = reConstrauctBtree(dlr, ldr)
node: 1 left : [2, 4, 7] right : [3, 5, 6, 8]
node: 2 left : [4, 7] right : []
node: 4 left : [] right : [7]
node: 7 left : [] right : []
node: 3 left : [5] right : [6, 8]
node: 5 left : [] right : []
node: 6 left : [8] right : []
node: 8 left : [] right : []
>>> pretree(r)
1
2
4
7
3
5
6
8
5、用兩個棧實現一個隊列
用兩個棧來實現一個隊列,完成隊列的Push和Pop操作。 隊列中的元素爲int類型
跳過
6、旋轉數組中的最小數字
把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。 輸入一個非減排序的數組的一個旋轉,輸出旋轉數組的最小元素。 例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。 NOTE:給出的所有元素都大於0,若數組大小爲0,請返回0。
本意爲:一個遞增數組,將大的數幫到前面,求改數組的最小值
6.1 思路
本質是順序數組,首先考慮二分遞歸:
0- 求數組的中位數,左中位數,右中位數
1- 分4種情況比較三個中位數
2- 取小段再遞歸
3- 直到左中位數等於右中位數
6.2 解題
import copy
def minNumberInRotateArray(rotateArray: list) -> int:
"""
二分遞歸
"""
r_ = copy.deepcopy(rotateArray)
if r_ == []:
return 0
n = len(r_) - 1
mid = n // 2 + n % 2
left_m = mid // 2 + mid % 2
right_m = (n - mid) // 2 + (n - mid) % 2 + mid
print('left mid:', r_[left_m], 'mid:', r_[mid], 'right mid:'
, r_[right_m]) #, 'r_', r_)
if (left_m == right_m):
return r_[left_m]
# 0- l < m < r
elif (r_[mid] <= r_[right_m]) and (r_[mid] >= r_[left_m]):
return minNumberInRotateArray(r_[:mid+1])
# 1- l > m > r
elif (r_[mid] >= r_[right_m]) and (r_[mid] <= r_[left_m]):
return minNumberInRotateArray(r_[mid:])
# 2- l > m < r
elif (r_[mid] <= r_[right_m]) and (r_[mid] <= r_[left_m]):
return minNumberInRotateArray(r_[left_m:right_m+1])
# 3- l < m > r , l > r
elif (r_[mid] >= r_[right_m]) and (r_[mid] >= r_[left_m]) and (r_[left_m] >= r_[right_m]):
return minNumberInRotateArray(r_[mid:])
# 4- l < m > r , l < r
elif (r_[mid] >= r_[right_m]) and (r_[mid] >= r_[left_m]) and (r_[left_m] <= r_[right_m]):
return minNumberInRotateArray(r_[:mid+1])
minNumberInRotateArray([6, 7, 8, 9, 1, 2, 3, 4, 5])
簡單二分
def smpl_minNumberInRotateArray(rotateArray: list) -> int:
"""
簡單二分
"""
if rotateArray == []:
return 0
l = 0
r = len(rotateArray) - 1
while l < r:
mid = (l + r)//2
print(l, r)
if rotateArray[mid] > rotateArray[r]:
l = mid + 1
else:
r = mid
return rotateArray[l]
7、斐波那契數列
要求輸入一個整數n,請你輸出斐波那契數列的第n項(從0開始,第0項爲0),n<=39
7.1 思路
遞歸:
Fib(n) = Fib(n-1) + Fib(n-2)]
7.2 解題
from datetime import datetime
def Fib(n):
if n <= 1:
return n
return Fib(n-1) + Fib(n-2)
a = datetime.now()
Fib(35)
b = datetime.now() - a
print('Fib(35) cost:',b.total_seconds())
"""
>>> a = datetime.now()
>>> Fib(35)
9227465
>>> b = datetime.now() - a
>>> print('Fib(35) cost:',b.total_seconds())
Fib(35) cost: 4.406252
"""
7.3 優化思路
遞歸會存在重複計算的情況:
Fib(n) = Fib(n-1) + Fib(n-2)]
將fib的計算結果全部記錄到memo字典中,減少不必要的計算
- 快速fib,減少重複計算
def Fib_q(n, memo):
if n not in memo: # 減少了重疊部分的計算
memo[n] = Fib_q(n-1, memo) + Fib_q(n-2, memo)
return memo[n]
a = datetime.now()
Fib_q(35, {0:0, 1:1})
b = datetime.now() - a
print('Fib_q(35) cost:', b.total_seconds())
"""
>>> a = datetime.now()
>>> Fib_q(35, {0:0, 1:1})
9227465
>>> b = datetime.now() - a
>>> print('Fib_q(35) cost:', b.total_seconds())
Fib_q(35) cost: 0.119007
"""
Fib_q增加內存佔有,降低了時間複雜度。
7.4 優化思路2
參考: https://zhuanlan.zhihu.com/p/75864673
只需定義兩個整型變量,b表示後面的一個數字,a表示前面的數字即可。每次進行的變換是: a,b = b,a+b
def Fibonacci(n):
if n <= 0:
return 0
a = b = 1
for i in range(2,n):
a,b = b,a+b
return b
a = datetime.now()
Fibonacci(35)
b = datetime.now() - a
print('Fib_q(35) cost:', b.total_seconds())
""" 測試後該方法 運行較快 也較穩定(<0.3s)
>>> Fibonacci(35)
9227465
>>> b = datetime.now() - a
>>> print('Fibonacci(35) cost:', b.total_seconds())
Fibonacci(35) cost: 0.225013
"""
優化1,雖然也減少了循環次數,但是
Fib_q(n-1, memo) + Fib_q(n-2, memo)
,仍然是對其的兩次調用。而優化2,無論怎了都是O(n)的時間複雜度
8、跳臺階
一隻青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法(先後次序不同算不同的結果)。
8.1 思路
枚舉+排列組合:
1- 以2爲步數,遍歷 1步和兩步的組合
2- 對每個組合進行排列組合
3- 輸出最後所有排列組合的情況總和
8.2 解題
from toolz import reduce
def jumpFloor(n):
x_start = 2 - n % 2
way_ls = []
for x in range(x_start, n+1, 2):
x_times = x
y_times = (n - x) // 2
all_tp = reduce(lambda x, y: x * y, range(y_times + 1, x_times + y_times + 1)) / \
reduce(lambda x, y: x * y, range(1, x_times + 1))
way_ls.append(all_tp)
print(f'x_times:{x_times}, y_times:{y_times}, ways:{all_tp}')
return sum(way_ls)
jumpFloor(23)
"""
>>> jumpFloor(23)
x_times:1, y_times:11, ways:12.0
x_times:3, y_times:10, ways:286.0
x_times:5, y_times:9, ways:2002.0
x_times:7, y_times:8, ways:6435.0
x_times:9, y_times:7, ways:11440.0
x_times:11, y_times:6, ways:12376.0
x_times:13, y_times:5, ways:8568.0
x_times:15, y_times:4, ways:3876.0
x_times:17, y_times:3, ways:1140.0
x_times:19, y_times:2, ways:210.0
x_times:21, y_times:1, ways:22.0
x_times:23, y_times:0, ways:1.0
46368.0
"""
8.3 優化解題
- 思路
典型的動態規劃問題
對於到達第n階臺階來說,有兩種辦法
一種是從第n-1階,爬一個臺階
一種是從第n-2階,爬兩個臺階。
也就是說,到第n臺階是, 到達n-1臺階的方法加上到達n-2臺階的方法總和
def jumpFloor(number):
if number <= 2:
return number if number >= 0 else 0
result = [1, 2] # 1 的時候result[0], 2 的時候result[1]
for i in range(2, number):
result.append(result[i-1] + result[i-2])
return result[-1]
jumpFloor(23)
9、變態跳臺階
一隻青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。
9.1 思路
- 思路
對於到達第n階臺階來說,有n種辦法
一種是從第n-1階,上來
一種是從第n-2階,上來
一種是從第n-3階,上來
…
一種是從第1階,上來
9.2 解題
def jumpFloorII(n):
if n <= 2:
return n
res = [1, 2]
for i in range(2, n):
res.append(res[-1] * 2)
return res[-1]
"""
>>> jumpFloorII(4)
8
"""
9.3 優化解題
對F(n) = 2 * F(n-1)
化簡 :
def jumpFloorII(n):
return 2 ** (n-1)
10、矩陣覆蓋
我們可以用 2*1
的小矩形橫着或者豎着去覆蓋更大的矩形。請問用n個2*1
的小矩形無重疊地覆蓋一個2*n
的大矩形,總共有多少種方法?
10.1 思路
- 思路
動態規劃:
到了n,那麼上一步就有兩種情況
1-橫着, F(n-1)的時候加上一塊
2-豎着, F(n-2)的時候加上兩塊
還有一種特殊情況矩陣的形狀爲1*2n
, 不論n是多少,僅有一種方法
- 一般情況:
- 特殊情況:
10.2 解題
def rectCover(n):
if n <= 0:
return 0
if n == 1:
return 1
if n == 2:
return 2 + 1
res = [1, 2]
for i in range(2, n):
res.append(res[i-1] + res[i-2])
# 加上特殊情況
return res[-1] + 1
"""
>>> rectCover(3)
4
"""
11、二進制中1的個數
輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼錶示。例如,9表示1001,因此輸入9,輸出2。
11.1 思路
- 思路
0- 如果整數不等於0,那麼該整數的二進制表示中至少有1位是1
1- 情況1:(xxxxx1)
如果最後位是1,那個減去1就可計數一次
再判斷減去1之後的數有多少個1,即做一次位運算x & (x - 1)
的數2- 情況2:
(xxx100)
最後位是0,而第m位爲1,該數減去1,結果是(xxx011)
那麼也可計數一次,再判斷之後的數有多少個1,即做一次位運算x & (x - 1)
的數3- 結合兩種情況,可以和自己做幾次
x & (x - 1)
即有多少個1
11.2 解題
def getnumb1(n):
cnt = 0
while n:
n = n & (n - 1)
cnt += 1
return cnt
"""
>>> getnumb1(10000)
5
>>> len(bin(10000).split('1')) - 1
5
"""
12、數值的整數次方
給定一個double類型的浮點數base和int類型的整數exponent。求base的exponent次方
12.1 思路
- 思路
0- 循環乘
12.2 解題
def Power(x: float, n: int) -> float:
if x == 0:
return 0
if n == 0:
return 1
out = 1
while n:
out = out * x if n > 0 else out / x
n = n - 1 if n > 0 else n + 1
return out
"""
>>> Power(2.1, 8)
378.22859361000013
>>> 2.1 ** 8
378.22859361000013
"""
13、調整數組順序使奇數位於偶數前面
輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得所有的奇數位於數組的前半部分,所有的偶數位於數組的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變。
13.1 思路
- 思路
0- 全部遍歷
13.2 解題
def reOrderArray(lst):
odd_lst = []
even_lst = []
for i in lst:
if i % 2 == 1:
odd_lst.append(i)
else:
even_lst.append(i)
return odd_lst + even_lst
- 用lambda
def reOrderArray(lst):
return sorted(lst, key = lambda x: x%2, reverse=True)
14、鏈表中的倒數第K個節點
輸入一個鏈表,輸出該鏈表中倒數第k個結點
14.1 思路
- 思路
0- 由於不知道鏈表的總長度,所以需要一把K長的尺子做衡量
1- 當尺子後端到達鏈表末尾,那邊尺子的前端就是倒數K節點
14.2 解題
def findKtoTail(l, k):
# 如果鏈表爲空
if not l or k <= 0:
return None
st, ed = l, l
lon_t = 0
while (lon_t < k) and (ed):
ed = ed.next
lon_t += 1
# 尺子不夠長時
if lon_t < k:
return None
# 尺子準備就緒, 同步後移
while ed:
ed = ed.next
st = st.next
return st