記錄刷題過程,方便自己後期回顧
題目來自《劍指offer》,在牛客上OC,思路大多來自劍指offer,偶爾來自自己的碎碎念,代碼自己瞎寫的,如果有更優的方法請告訴我,謝謝大佬們
語言:python2.7,我知道它有點過時,但是我現在好像只會這個,其他的都想不起來了。就這樣吧,下次一定用C++
寫之前先寫一些下面會用到的基礎知識:
a = float('inf'). # 正無窮
b = float('-inf') # 負無窮
二進制的位運算:與(&)、或(|)、異或(^)、左移(<<)、右移(>>)
1、賦值運算符函數
略
2、單例模式
略
3、 二維數組中的查找
在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序,請完成一個函數,輸入二維數組和一個整數,判斷數組中是否含有該整數
思路:從右上角開始,要查找元素等於當前元素返回True,要查找元素小於當前元素->向左,要查找元素大於當前元素->向下,直到行小於0或列大於最大列。或者從左下角開始
# -*- coding:utf-8 -*-
class Solution:
def Find(self, target, array):
if array and array[0]:
row = len(array)-1
col = len(array[0]) -1
r, c = 0, col
while r <= row and c >= 0:
if array[r][c] == target:
return True
elif array[r][c] < target:
r += 1
else:
c -= 1
return False
4、替換空格
把字符串中的每個空格替換成“%20”
思路:從後向前遍歷+替換。先遍歷一遍得到所有的空格數,替換後的長度=替換前的長度+空格數*2,雙指針old new,old指向原始字符串末尾,new指向新字符串末尾(其實是一個字符串)
這裏其實有個bug:python字符串不可變,所以只能先轉成列表再轉回來,但是這樣就佔用了額外的空間,時間複雜度是O(2n),既然已經佔用額外空間了,爲什麼不直接正着計算,遇到空格extends(['2', '0', '%'])就好了呀,時間複雜度是0(1n)
查了一下之前提交的代碼:s
=
s.replace(
' '
,
'%20'),我爲什麼這麼皮
# -*- coding:utf-8 -*-
class Solution:
def replaceSpace(self, s):
if not s:
return s
length, blank_num = 0, 0 # 字符串長度,空格個數
for i in s:
if i == ' ':
blank_num += 1
length += 1
old_index = length - 1
new_index = old_index + 2 * blank_num
s = list(s)
s.extend([' ']*blank_num*2) # 多一個空格,長度多2
while old_index >= 0:
if s[old_index] == ' ':
s[new_index] = '0'
new_index -= 1
s[new_index] = '2'
new_index -= 1
s[new_index] = '%'
else:
s[new_index] = s[old_index]
old_index -= 1
new_index -= 1
s = ''.join(s)
return s
5.01、鏈表末尾插入一個節點 (代碼以後補)
5.02、刪除鏈表中第一個含有某值的節點(代碼以後補)
5、從尾到頭打印鏈表
輸入一個鏈表的頭結點,從尾到頭反過來打印出每個節點的值
棧:from collection import dqueue 或者list.append() + list.reverse() 或者list.insert(0, ) 或者list.append() + list.pop()(憑印象寫的)
遞歸(MARK一下):
-*- coding:utf-8 -*-
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def printListFromTailToHead(self, listNode):
if listNode == None:
return []
return self.printListFromTailToHead(listNode.next)+[listNode.val]
常見的樹:二叉樹、二叉搜索樹、堆、紅黑樹
6、重建二叉樹
根據前序和中序遍歷結果,重建二叉樹(不含重複元素)
遞歸:
#-*- coding:utf-8 -*-
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
# 返回構造的TreeNode根節點
def reConstructBinaryTree(self, pre, tin):
# write code here
if not pre or not tin:
return None
root = TreeNode(pre[0])
root.left = self.reConstructBinaryTree(pre[1:tin.index(pre[0])+1], tin[:tin.index(pre[0])])
root.right = self.reConstructBinaryTree(pre[tin.index(pre[0])+1:], tin[tin.index(pre[0])+1:])
return root
7、兩個棧實現隊列
思路:兩個棧,一個模擬進隊列(進棧),一個模擬出隊列(出棧),直接進,出棧如果爲空,就把進棧的所有元素都給出棧
好像是我人生中第一場面試,面試官問了一道很難的算法題,我說了思路但是代碼沒寫出來,然後就被問了這道題~ 當時題量爲0,也還不知道劍指offer是什麼,生想出來的,後來才發現,那場面試的好多題都來自劍指offer,不知道面試官小哥哥是不是看上了我的聰明才智哈哈哈哈哈哈(呸呸呸 太不要臉了)
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.stack1 = []
self.stack2 = []
def push(self, node):
# write code here
self.stack1.append(node)
def pop(self):
# return xx
if not self.stack2:
while self.stack1:
self.stack2.append(self.stack1.pop())
return self.stack2.pop()
兩個隊列實現一個棧
初始時,隨便選一個隊列作爲插入隊列,然後一個向這個隊列插入元素,pop時,所所有元素都移到另一個隊列,彈出最後一個元素。再像另一個隊列插入,彈出時還是先轉移元素再刪除
總結:插入時,如果兩個stack都沒有元素,隨便插入一個,否則插入有元素的stack,彈出時,先轉移元素再刪除
8、旋轉數組的最小數字
把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。輸入一個非遞減排序的數組的一個旋轉,輸出旋轉數組的最小元素。例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。(NOTE:給出的所有元素都大於0,若數組大小爲0,請返回0。)
經典面試題目,旋轉數組相關的題目我被問到過兩次
思路:重點就是二分法+雙指針+定區間
二分法沒什麼問題
雙指針的意思是:旋轉數組可以理解爲兩個遞增子數組,最小值是第二個遞增子數組的第一個元素。兩個指針,初始化時一個指向隊首,一個指向隊尾,通過mid不斷更新兩個指針,最終第一個指針會指向第一個遞增子數組的最後一個元素,第二個指針會指向第二個遞增子數組的第一個元素,返回第二個指針對應的值。
定區間的意思是,正常情況下,left-mid和mid-right總有一個區間是單調增的,另一個區間又可以被分爲兩個子數組,去掉單調區間,選擇另一個區間,不斷循環
再考慮兩個特殊情況:數組旋轉=0 -> return l[0];l[left] = l[mid] = l[right] -> 遍歷
# -*- coding:utf-8 -*-
class Solution:
def minNumberInRotateArray(self, rotateArray):
length = len(rotateArray)
if length == 0:
return 0
if length == 1:
return rotateArray[0]
l,r = 0, length - 1
# 特例1: 左面元素小於右面元素 -> 沒有旋轉 -> 直接返回
if rotateArray[l] < rotateArray[r]:
return rotateArray[l]
# 進入二分循環
while l < r:
if r - l == 1:
return rotateArray[r]
m = (l + r) / 2
# 特例2
if rotateArray[l] == rotateArray[m] and rotateArray[l] == rotateArray[r]:
min_num = rotateArray[l]
for i in range(l+1, r+1):
min_num = min(rotateArray[i], min_num)
return min_num
# 左面不是單增區間
elif rotateArray[l] >= rotateArray[m]:
r = m
# 右面是單增區間
else:
l = m
只a了13% ??????很奇怪
9、佩波那契數列
n=0, f(n)=0; n=1, f(n)=1; else:f(n)=f(n-1)+f(n-2)
遞歸:本地跑是對的,線上超時?????
class Solution:
def Fibonacci(self, n):
# write code here
if n == 0:
return 0
if n == 1:
return 1
return self.Fibonacci(n-1) + self.Fibonacci(n-2)
s = Solution()
print s.Fibonacci(10)
動態規劃:
class Solution:
def Fibonacci(self, n):
a ,b = 0, 1
for i in range(n):
a, b = b, a+b
return a
跳臺階:一隻青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法(先後次序不同算不同的結果)
f(n) = f(n-1) + f(n-2),本級臺階來自於前一個臺階跳一級和前兩個臺階跳兩級。
# -*- coding:utf-8 -*-
class Solution:
def jumpFloor(self, number):
a, b = 1, 2
for i in range(1,number):
a, b = b, a+b
return a
變態跳臺階:一隻青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。
矩形覆蓋:我們可以用2*1的小矩形橫着或者豎着去覆蓋更大的矩形。請問用n個2*1的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?
10、二進制中1的個數
輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼錶示。
n&(n-1) 可以把該整數最右面的一個1變爲0
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1(self, n):
count = 0
if n < 0:
n = n & 0xffffffff
while n:
n = n & (n-1)
count += 1
return count
11、數值的整數次方
給定一個double類型的浮點數base和int類型的整數exponent。求base的exponent次方。
思路:遞歸。exponent爲奇數時 exponent爲偶數時
同時考慮e<0,b=0 的特殊情況(考慮b=0是因爲,e<0時要對base取倒數)
class Solution():
def Power(self, base, exponent):
# 指數小於0且底數=0時
if abs(base-0.0)<1e-7 and exponent<0:
return False
if exponent < 0:
result = 1.0/self.power_with_exp(base,exponent*(-1))
else:
result = self.power_with_exp(base, exponent)
return result
def power_with_exp(self, base, exponent):
if exponent == 0:
return 1
if exponent == 1:
return base
result = self.power_with_exp(base, exponent>>1)
result *= result
if (exponent&1):
result *= base
return result
12、打印1到最大的n位數
如:輸入3,輸出1,2,3,。。。。999
思路:主要考慮用列表代替數字,因爲存在大數溢出問題。但是這題python又是個bug,因爲python int可以無限大 唉。。。。。
模擬加法+打印數字+判斷是否到最大值跳出循環
遞歸:
(代碼以後補)
13、在O(1)時間刪除鏈表節點
思路:要刪除的節點i,下一個節點是j i.val = j.val, i.next = j.next 然後刪掉j
要考慮兩個特殊情況:要刪除的節點是尾節點;鏈表只有一個節點
14、調整數組順序使奇數位於偶數前面
思路:雙指針,第一個指針從前向後掃描直到遇到偶數,第二個指針從後向前掃描直到遇到奇數,交換元素位置
保證重排後奇數和奇數,偶數和偶數之間的相對位置不變
思路:這個是牛客上的,沒想到什麼簡單的思路,遍歷兩遍又用了一個輔助數組
# -*- coding:utf-8 -*-
class Solution:
def reOrderArray(self, array):
ans = []
for i in array:
if i%2:
ans.append(i)
for i in array:
if not i%2:
ans.append(i)
return ans
15、鏈表中倒數第k個節點
思路:兩個指針,初始時都放在頭節點的位置,第一個指針向前走k-1步,這樣兩個指針就差了k-1個節點,然後兩個指針同時向後移動,當第一個指針到最後一個節點時,第二個指針到倒數第k個節點
class Solution:
def FindKthToTail(self, head, k):
if not head or k <= 0:
return None
fore = head
behind = head
for i in range(k-1):
if not fore.next:
return None
fore = fore.next
while fore.next:
fore = fore.next
behind = behind.next
return behind
16、反轉鏈表
爲了保證反轉後的鏈表不斷裂,需要三個指針:當前節點,當前節點的前一個節點,當前節點的下一個節點
# -*- coding:utf-8 -*-
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def ReverseList(self, pHead):
reversed_head = None
cur = pHead
pre = None
# post = pHead.next
while cur:
post = cur.next
if not post:
reversed_head = cur
cur.next = pre
pre = cur
cur = post
return reversed_head
17、合併兩個排序的鏈表
# -*- coding:utf-8 -*-
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
# 返回合併後列表
def Merge(self, pHead1, pHead2):
# write code here
if not pHead1:
return pHead2
if not pHead2:
return pHead1
new_head = ListNode(0) # 我當時是怎麼想起來這麼機智的做法的!!!!
p = new_head
while pHead1 and pHead2:
if pHead1.val <= pHead2.val:
p.next = pHead1
p = p.next
pHead1 = pHead1.next
else:
p.next = pHead2
p = p.next
pHead2 = pHead2.next
if pHead1:
p.next = pHead1
if pHead2:
p.next = pHead2
return new_head.next
18、樹的子結構
輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)
思路:遍歷+遞歸。先在樹A中查找和樹B根節點相同的值, 然後遞歸地判斷A中該節點的子樹和B根節點的子樹是否相同。遞歸終止條件是遇到不同的值,或者到達了A或B的葉子節點
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def HasSubtree(self, pRoot1, pRoot2):
# write code here
if not pRoot1 or not pRoot2:
return False
return self.IsSubtree(pRoot1, pRoot2) or self.HasSubtree(pRoot1.left, pRoot2) or self.HasSubtree(pRoot1.right, pRoot2)
def IsSubtree(self, pRoot1, pRoot2):
if not pRoot2:
return True
if not pRoot1 or pRoot1.val != pRoot2.val:
return False
return self.IsSubtree(pRoot1.left, pRoot2.left) and self.IsSubtree(pRoot1.right, pRoot2.right)
19、二叉樹的鏡像
操作給定的二叉樹,將其變換爲源二叉樹的鏡像
思路:前序遍歷二叉樹,對於遍歷到的每個節點,交換他們的左右子樹
# -*- coding:utf-8 -*-
class Solution:
# 返回鏡像樹的根節點
def Mirror(self, root):
if not root:
return
if not root.left and not root.right:
return
temp_node = root.left
root.left = root.right
root.right = temp_node
self.Mirror(root.left)
self.Mirror(root.right)
或
class Solution:
# 返回鏡像樹的根節點
def Mirror(self, root):
if root:
temp_node = root.left
root.left = root.right
root.right = temp_node
self.Mirror(root.left)
self.Mirror(root.right)
20、順時針打印矩陣
輸入一個矩陣,按照從外向裏以順時針的順序依次打印出每一個數字
思路:按圈打印,第一圈從(1, 1)開始,第二圈從(2, 2)開始。。。。一共需要打印 (min(行,列)+1)/2圈(4行打印兩圈,5行打印三圈)。對於每一圈:從左到右、從上到下、從右到左、從下到上依次打印。最後一圈存在特殊情況:一個元素,一行,一列,兩行,兩列等->最後一圈存在只需要一步、兩步、三步的情況,所以進行每步打印時需要判斷。
# -*- coding:utf-8 -*-
class Solution(object):
# matrix類型爲二維列表,需要返回列表
def printMatrix(self, matrix):
# write code here
ans = []
row = len(matrix)
if matrix[0]:
col = len(matrix[0])
iter = min((row+1)/2,(col+1)/2)
for i in range(iter):
#print i
self.print_circle(matrix,i,row-2*i, col-2*i, ans) #(矩陣,左上角座標,要打印的行數,要打印的列數)
return ans
def print_circle(self, matrix, start, row, col, ans):
end_x = row + start - 1
end_y = col + start - 1
#print end_x, end_y
for i in range(start, end_y+1):
ans.append(matrix[start][i])
if end_x > start:
for i in range(start+1, end_x+1):
ans.append(matrix[i][end_y])
if end_y > start and end_x > start:
for i in range(end_y-1,start-1,-1):
ans.append(matrix[end_x][i])
if end_x - start > 1 and end_y >start:
for i in range(end_x-1,start,-1):
ans.append(matrix[i][start])
return ans
21、包含min函數的棧
定義棧的數據結構,請在該類型中實現一個能夠得到棧中所含最小元素的min函數,push pop min的時間複雜度都爲O(1)。
注意:保證測試中不會當棧爲空的時候,對棧調用pop()或者min()或者top()方法。
思路:輔助棧。每push一個元素,輔助棧都會push min(當前元素,輔助棧棧頂元素)
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.stack_data = [] # 棧
self.stack_min = [] # 輔助棧
def push(self, node):
self.stack_data.append(node)
if self.stack_min == [] or node < self.stack_min[-1]:
self.stack_min.append(node)
else:
self.stack_min.append(self.stack_min[-1])
def pop(self):
if self.stack_data:
self.stack_min.pop()
self.stack_data.pop()
def top(self):
return self.stack_data[-1]
def min(self):
return self.stack_min[-1]
22、棧的壓入、彈出序列
輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否可能爲該棧的彈出順序。假設壓入棧的所有數字均不相等。
思路:從彈出序列的角度判斷。對於彈出序列的每個元素,如果和壓入棧當前棧頂元素相同,則彈出,繼續判斷彈出棧下一個元素和當前棧頂元素。。。。。。不相同時,壓入元素,直到和彈出序列元素相同。當棧頂元素和彈出序列元素不同且所有元素都入棧時,return false。看代碼吧還是。。。。
class Solution(object):
def IsPopOrder(self, pushV, popV):
length_push = len(pushV)
length_pop = len(popV)
if length_push == 0 or length_push != length_pop:
return False
stack = []
index_pop = 0
for i in pushV:
stack.append(i)
while stack and index_pop < length_pop and stack[-1] == popV[index_pop]:
index_pop += 1
stack.pop()
if stack:
return False
return True
23、從上往下打印二叉樹
思路:層序遍歷
class Solution:
def PrintFromTopToBottom(self, root):
l = []
if not root:
return l
queue = []
queue.append(root)
while queue:
l.append(queue[0].val)
if queue[0].left:
queue.append(queue[0].left)
if queue[0].right:
queue.append(queue[0].right)
del queue[0]
return l
24、二叉搜索樹的後序遍歷序列
輸入一個整數數組,判斷該數組是不是某二叉搜索樹的後序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的數組的任意兩個數字都互不相同。
思路:後序遍歷:左右根。根據二叉搜索樹的性質可知:根節點左子樹的所有節點都比根節點小,右子樹的所有節點都比根節點大。遞歸判斷
class Solution(object):
def VerifySquenceOfBST(self, sequence):
if not sequence:
return False
# 判斷根節點左右子樹的位置,i之前的都是左子樹,i及之後的都是右子樹
for i in range(len(sequence)):
if sequence[i] > sequence[-1]:
break
for j in range(i,len(sequence)-1):
if sequence[j] < sequence[-1]:
return False
left = True
right = True
if i>0:
left = self.VerifySquenceOfBST(sequence[:i])
if i<len(sequence)-1:
print sequence[i:len(sequence)-1]
right = self.VerifySquenceOfBST(sequence[i:len(sequence)-1])
return left and right
25、二叉樹中和爲某一值的路徑
輸入一顆二叉樹的根節點和一個整數,打印出二叉樹中結點值的和爲輸入整數的所有路徑。路徑定義爲從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。
思路:前序遍歷和遞歸,遞歸過程中用一個列表保存路徑
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
# 返回二維列表,內部每個列表表示找到的路徑
def FindPath(self, root, expectNumber):
if not root:
return []
if not root.left and not root.right and root.val == expectNumber:
return [[root.val]]
res = []
left = self.FindPath(root.left, expectNumber-root.val)
right = self.FindPath(root.right, expectNumber-root.val)
for i in left+right:
res.append([root.val]+i)
return res
26、複雜鏈表的複製
輸入一個複雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針random指向一個隨機節點),請對此鏈表進行深拷貝,並返回拷貝後的頭結點。
思路:step1:複製val和next,對於原始鏈表中的每個節點N,都複製一份N'並鏈接在N的後面。
step2:複製random指針,N->S 那麼 N.next.random -> S.next
step3:拆成兩個鏈表
# -*- coding:utf-8 -*-
class RandomListNode:
def __init__(self, x):
self.label = x
self.next = None
self.random = None
class Solution:
def Clone(self, pHead):
if not pHead:
return None
# step1
cur = pHead
while cur:
tmp = RandomListNode(cur.label)
tmp.next = cur.next
cur.next = tmp
cur = tmp.next
# step2
cur = pHead
while cur:
tmp = cur.next
if cur.random:
tmp.random = cur.random.next
cur = tmp.next
# step3
cur = pHead
res = pHead.next
while cur.next:
tmp = cur.next
cur.next = tmp.next
cur = tmp
return res
27、二叉搜索樹與雙向鏈表
輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。
思路:中序遍歷,左根右。先遞歸地處理左子樹,然後把左子樹和根節點連起來,然後遞歸地處理右子樹,把根節點和右子樹連接起來。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
# 這個應該可以再優化一下 連接根節點和左右子樹那裏複雜了,但是我簡單的沒跑通
class Solution:
def Convert(self, pRootOfTree):
if not pRootOfTree:
return pRootOfTree
if not pRootOfTree.left and not pRootOfTree.right:
return pRootOfTree
# 處理左子樹
self.Convert(pRootOfTree.left)
left=pRootOfTree.left
# 連接根與左子樹最大結點
if left:
while(left.right):
left=left.right
pRootOfTree.left,left.right=left,pRootOfTree
# 處理右子樹
self.Convert(pRootOfTree.right)
right=pRootOfTree.right
# 連接根與右子樹最小結點
if right:
while(right.left):
right=right.left
pRootOfTree.right,right.left=right,pRootOfTree
while(pRootOfTree.left):
pRootOfTree=pRootOfTree.left
return pRootOfTree
28、字符串的排列
輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串abc,則打印出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba
思路:遞歸。
# -*- coding:utf-8 -*-
class Solution:
def Permutation(self, ss):
# write code here
if len(ss) == 0:
return []
res = []
#flag = [1] * len(ss)
temp = ''
res = self.dfs(ss, temp, res)
return sorted(set(res))
def dfs(self, ss, temp, res):
if len(ss) == 1:
temp += ss
res.append(temp)
return res
else:
for i in range(len(ss)):
self.dfs(ss[:i]+ss[i+1:], temp+ss[i], res)
return res