第2章 面試基礎知識
2.2 編程語言
面試題2 使用Python實現單例模式
單例模式(Singleton Pattern)是一種常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在。當你希望在整個系統中,某個類只能出現一個實例時,單例對象就能派上用場。
單例模式的要點有三個;一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。
其實,Python 的模塊就是天然的單例模式。因爲模塊在第一次導入時,會生成 .pyc 文件,當第二次導入時,就會直接加載 .pyc 文件,而不會再次執行模塊代碼。因此,我們只需把相關的函數和數據定義在一個模塊中,就可以獲得一個單例對象了。
詳參https://www.cnblogs.com/metianzing/p/7719901.html
2.3 數據結構
面試題3 二維數組中的查找
題目:二維數組中,每行從左到右遞增,每列從上到下遞增,給出一個數,判斷它是否在數組中
思路:從左下角或者右上角開始比較
def find_integer(matrix, num):
"""
:param matrix: [[]]
:param num: int
:return: bool
"""
if not matrix:
return False
rows, cols = len(matrix), len(matrix[0])
row, col = rows - 1, 0
while row >= 0 and col <= cols - 1:
if matrix[row][col] == num:
return True
elif matrix[row][col] > num:
row -= 1
else:
col += 1
return False
面試題4 替換空格
題目:把字符串中的空格替換成'20%'
#法一:直接使用Python字符串的內置函數
' a b '.replace(' ', '20%')
#法二:使用正則表達式
import re
ret = re.compile(' ')
ret.sub('20%', ' a b ')
面試題5 從尾到頭打印單鏈表
#方法1:使用棧,可以使用列表模擬
def print_links(links):
stack = []
while links:
stack.append(links.val)
links = links.next
while stack:
print stack.pop()
#方法2:直接遞歸
def print_link_recursion(links):
if links:
print_link_recursion(links.next)
print links.val
面試題6 重建二叉樹
要求:用前序和中序遍歷結果構建二叉樹,遍歷結果中不包含重複值
思路:前序的第一個元素是根結點的值,在中序中找到該值,中序中該值的左邊的元素是根結點的左子樹,右邊是右子樹,然後遞歸的處理左邊和右邊
def construct_tree(preorder=None, inorder=None):
"""
structure binary tree
"""
if not preorder or not inorder:
return None
index = inorder.index(preorder[0])
left = inorder[0:index]
right = inorder[index+1:]
root = TreeNode(preorder[0])
root.left = construct_tree(preorder[1:1+len(left)], left)
root.right = construct_tree(preorder[-len(right):], right)
return root
面試題7 用兩個棧實現隊列
要求:用兩個棧實現隊列,分別實現入隊和出隊操作 思路:一個棧負責入隊,另一個負責出隊,出棧爲空則從入棧中導入到出棧中
class MyQueue(object):
def __init__(self):
self.stack = []
self.stack2 = []
def push(self, val):
self.stack.append(val)
def pop(self):
if self.stack2:
return self.stack2.pop()
while self.stack:
self.stack2.append(self.stack.pop())
return self.stack2.pop() if self.stack2 else u'Empty Queue'
2.4 算法和數據操作
面試題8 旋轉數組的最小數字
要求:把遞增數組的前面部分數字移到隊尾,求數組中的最小值,例如[3,4,5,6,1,2]
思路:使用二分法,但要考慮[1, 0, 0, 1]這種數據,只能順序查找(數值降低後的第一個值?!)
https://www.cnblogs.com/General-up/archive/2016/04/20/5413162.html
def min(data):
length=len(data)
key=data[0]
for i in xrange(1,length):
if data[i]<key:
return data[i]
return key
def find_min(nums):
if not nums:
return False
length = len(nums)
left, right = 0, length - 1
while nums[left] >= nums[right]:
if right - left == 1:
return nums[right]
mid = (left + right) / 2
if nums[left] == nums[mid] == nums[right]:
return min(nums)
if nums[left] <= nums[mid]:
left = mid
if nums[right] >= nums[mid]:
right = mid
return nums[0]
面試題9 斐波那契數列
思路:用生成器
def fib(num):
a, b = 0, 1
for i in xrange(num):
yield b
a, b = b, a + b
面試題10 二進制中1的個數
要求:求一個整數的二進制表示中,1的個數
思路:二進制表示中,最後的那個1被減去後,低位都變爲0,高位不變,按位與就可以去掉這個1
def num_of_1(n):
ret = 0
while n:
ret += 1
n = n & n-1
return ret
第3章 高質量代碼
3.3 代碼的完整性
面試題11 數值的整數次方
要求:求一個數的整數次方
思路:需要考慮次方是正數、負數和0,基數是0
1.指數爲負,底數是零;
2.指數底數都是零;
3.返回正常零和返回錯誤零的區別;
4.底數爲正,指數爲負如何處理;
5.考慮底數次方大的話,想效率問題;
6.處理double數值相等問題(浮點數相等不能直接用==);
def power(base, exponent):
if equal_zero(base) and exponent < 0:
raise ZeroDivisionError
ret = power_value(base, abs(exponent))
if exponent < 0:
return 1.0 / ret
else:
return ret
def equal_zero(num):
if abs(num - 0.0) < 0.0000001:
return True
def power_value(base, exponent):
if exponent == 0:
return 1
if exponent == 1:
return base
ret = power_value(base, exponent >> 1)
ret *= ret
if exponent & 1 == 1:
ret *= base
return ret
面試題12 打印1到最大的n位數
要求:輸入n,打印出從1到最大的n位數
思路:Python2.7對大整數可以自動轉換爲long,Python3中int等價於long,不需要考慮大整數溢出問題
def print_max_n(n):
for i in xrange(10 ** n):
print i
面試題13 O(1)時間刪除鏈表結點
要求:O(1)時間刪除鏈表結點
思路:如果有後續結點,後續結點的值前移,刪除後續結點,如果沒有,只能順序查找了
def delete_node(link, node):
if node == link: # 只有一個結點
del node
if node.next is None: # node是尾結點
while link:
if link.next == node:
link.next = None
link = link.next
else:
node.val = node.next.val
node.next = node.next.next
面試題14 調整數組順序使奇數位於偶數前面
思路:使用兩個指針,前後各一個,爲了更好的擴展性,可以把判斷奇偶部分抽取出來
def reorder(nums, fun):
left, right = 0, len(nums) - 1
while left < right:
while not func(nums[left]):
left += 1
while func(nums[right]):
right -= 1
if left < right:
nums[left], nums[right] = nums[right], nums[left]
def is_even(num):
return (num & 1) == 0
if __name__ == '__main__':
tests = [2, 3]
reorder(tests, is_even)
print tests
3.4 代碼的魯棒性
面試題15 鏈表中倒數第k個結點
要求:求單鏈表中的倒數第k個結點
思路:使用快慢指針,快的先走k-1步,需要考慮空鏈表以及k爲0
def last_kth(link, k):
if not link or k <= 0:
return None
move = link
while move and k-1 >= 0:
move = move.next
k -= 1
while move:
move = move.next
link = link.next
if k == 0:
return link.val
return None
面試題16 反轉鏈表
要求:反轉鏈表
思路:需要考慮空鏈表,只有一個結點的鏈表
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
prev = None
cur = head
while cur:
temp = cur.next
cur.next = prev
prev = cur
cur = temp
return prev
面試題17 合併兩個排序的鏈表
要求:合併兩個排序的鏈表
def mergeTwoLists(self, l1, l2):
init = out = ListNode(0)
while l1 and l2:
if l1.val < l2.val:
out.next = l1
l1 = l1.next
else:
out.next = l2
l2 = l2.next
out = out.next
out.next = l1 or l2
return init.next
面試題18 樹的子結構
要求:判斷一棵二叉樹是不是另一個的子結構
思路:使用遞歸
def sub_tree(tree1, tree2):
if tree1 and tree2:
if tree1.val == tree2.val:
return sub_tree(tree1.left, tree2.left) and sub_tree(tree1.right, tree2.right)
else:
return sub_tree(tree1.left, tree2) or sub_tree(tree1.right, tree2)
if not tree1 and tree2:
return False
return True
第4章 解決面試題思路
4.2 畫圖讓抽象問題形象化
面試題19 二叉樹的鏡像
#遞歸
def Mirror(self, root):
if root:
root.left, root.right = self.Mirror(root.right), self.Mirror(root.left)
return root
else:
return None
# stack DFS 從樹的右子樹進行不停的深入
def invertTree(self, root):
stack = [root]
while stack:
node = stack.pop()
if node:
node.left, node.right = node.right, node.left
stack.append(node.left)
stack.append(node.right)
return root
# queue BFS 整個遍歷過程是從左到右一層一層的遍歷的
from collections import deque
def mirror_bfs(root):
queue = deque([root])
while queue:
node = queue.popleft()
if node:
node.left, node.right = node.right, node.left
queue.append(node.left)
queue.append(node.right)
return root
面試題20 順時針打印矩陣
def spiralOrder(self, matrix):
return matrix and list(matrix.pop(0)) + spiralOrder(list(zip(*matrix))[::-1])
4.3 舉例讓抽象問題具體化
面試題21 包含min函數的棧
class solution:
def __init__(self):
"""
initialize your data structure here.
push 3 5 2 -1
min 3 3 2 -1
[] 0 2 -1-3 push-pre_min
"""
self.min = 0
self.stack = []
def push(self, x):
if not self.stack:
self.stack.append(0)
self.min = x
else:
self.stack.append(x - self.min)
if x < self.min:
self.min = x
def pop(self):
x = self.stack.pop()
if x < 0:
# p_min = push - x = self.min - x
self.min = self.min - x
def top(self):
# not self.stack.pop
x = self.stack[-1]
if x > 0:
return x + self.min
else:
return self.min
def getMin(self):
return self.min
面試題22 棧的壓入彈出序列
要求:判斷給定的兩個序列中,後者是不是前者的彈出序列,給定棧不包含相同值
class solution:
def IsPopOrder(self, pushV, popV):
# stack中存入pushV中取出的數據
stack=[]
while popV:
# 如果第一個元素相等,直接都彈出,根本不用壓入stack
if pushV and popV[0]==pushV[0]:
popV.pop(0)
pushV.pop(0)
#如果stack的最後一個元素與popV中第一個元素相等,將兩個元素都彈出
elif stack and stack[-1]==popV[0]:
stack.pop()
popV.pop(0)
# 如果pushV中有數據,壓入stack
elif pushV:
stack.append(pushV.pop(0))
# 上面情況都不滿足,直接返回false。
else:
return False
return True
面試題23 從上往下打印二叉樹
廣度優先搜索,按層次遍歷
from collections import deque
def print_btree(root):
if not root:
return None
queue = deque([root])
ret = []
while queue:
node = queue.popleft()
ret.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return ret
面試題24 二叉樹的後序遍歷序列
要求:判斷給定的整數數組是不是二叉搜索樹的後序遍歷序列
def is_post_order(order):
length = len(order)
if length:
root = order[-1]
left = 0
while order[left] < root:
left += 1
right = left
while right < length - 1:
if order[right] < root:
return False
right += 1
left_ret = True if left == 0 else is_post_order(order[:left])
right_ret = True if left == right else is_post_order(order[left:right])
return left_ret and right_ret
return False
面試題25 二叉樹中和爲某一值的路徑
輸入一棵二叉樹和一個值,求從根結點到葉結點的和等於該值的路徑
#遞歸 86.28%
def pathSum2(self, root, sum):
if not root:return []
if not root.left and not root.right and sum == root.val:
return [[root.val]]
tmp = self.pathSum(root.left, sum-root.val) + self.pathSum(root.right, sum-root.val)
return [[root.val]+i for i in tmp]
#BFS + Queue 86.28%
def pathSum(self, root, sum):
if not root:return []
result = []
queue = collections.deque([(root, sum, [])])
while queue:
curr, sum, path = queue.popleft()
if not curr.left and not curr.right and curr.val == sum:
result.append(path + [curr.val])
if curr.left:
queue.append((curr.left, sum - curr.val, path + [curr.val]))
if curr.right:
queue.append((curr.right, sum - curr.val, path + [curr.val]))
return result
4.4 分解讓複雜問題簡單化
面試題26 複雜鏈表的複製
def __init__(self, x):
self.label = x
self.next = None
self.random = None
def copyRandomList(self, head):
"""
:type head: RandomListNode
:rtype: RandomListNode
"""
cur = head
dummy = RandomListNode(0)
while cur:
copy = RandomListNode(cur.label)
copy.next = cur.next
cur.next = copy
cur = copy.next
cur = head
while cur:
if cur.random:
cur.next.random = cur.random.next
cur = cur.next.next
cur, copy_cur = head, dummy
while cur:
copy_cur.next = cur.next
cur.next = cur.next.next
copy_cur, cur = copy_cur.next, cur.next
return dummy.next
面試題27 二叉搜索樹與雙向鏈表
#https://blog.csdn.net/u010005281/article/details/79657259
class Solution:
def __init__(self):
self.listHead = None
self.listTail = None
def Convert(self, pRootOfTree):
if pRootOfTree==None:
return
self.Convert(pRootOfTree.left)
if self.listHead==None:
self.listHead = pRootOfTree
self.listTail = pRootOfTree
else:
self.listTail.right = pRootOfTree
pRootOfTree.left = self.listTail
self.listTail = pRootOfTree
self.Convert(pRootOfTree.right)
return self.listHead
面試題28 字符串的排列
def permute(self, nums):
perms = [[]]
for n in nums:
new_perm = []
for perm in perms:
#print ("==")
for i in range(len(perm) + 1):
new_perm.append(perm[:i] + [n] + perm[i:])
#print (new_perm)
perms = new_perm
return perms
全排列題目彙總
http://blog.csdn.net/menghan1224/article/details/52269064
第5章 優化時間和空間效率
5.2 時間效率
面試題29 數組中出現次數超過一半的數字
def majorityElement(self, nums):
return sorted(nums)[len(nums)//2]
#找出所有超過1/3的數字
#Boyer-Moore Majority Vote
def majorityElement(self, nums):
if not nums:return []
count1, count2, candidate1, candidate2 = 0, 0, 0, 1
for n in nums:
if n == candidate1:
count1 += 1
elif n == candidate2:
count2 += 1
elif count1 == 0:
candidate1, count1 = n, 1
elif count2 == 0:
candidate2, count2 = n, 1
else:
count1, count2 = count1 - 1, count2 - 1
return [n for n in (candidate1, candidate2)
if nums.count(n) > len(nums) // 3]
面試題30 最小的k個數
import heapq
def get_least_k_nums(nums, k):
# 數組比較小的時候可以直接使用
return heapq.nsmallest(k, nums)
面試題31 連續子數組的最大和
#Maximum Subarray
def maxSubArray(self, nums):
if max(nums) < 0:
return max(nums)
global_max, local_max = 0, 0
for x in nums:
local_max = max(0, local_max + x)
global_max = max(global_max, local_max)
return global_max
面試題32 從1到n整數中1出現的次數
Number of Digit One
● 若weight爲0,則1出現次數爲round*base
● 若weight爲1,則1出現次數爲round*base+former+1
● 若weight大於1,則1出現次數爲rount*base+base
● http://blog.csdn.net/yi_afly/article/details/52012593
def countDigitOne(self, n):
ones, m = 0, 1
while m <= n:
ones += (n//m + 8) // 10 * m + (n//m % 10 == 1) * (n%m + 1)
m *= 10
return ones
面試題33 把數組排成最小的數
AC by python2, no cmp in python3
#AC by python2
def largestNumber(self, nums):
nums = map(str,nums)
#least number
#nums.sort(cmp=lambda x, y: cmp(x+y, y+x))
#largest number
nums.sort(cmp=lambda x, y: cmp(y+x, x+y))
return ''.join(nums).lstrip('0') or '0'
#no cmp method in python3
class LargerNumKey(str):
def __lt__(x, y):
return x+y > y+x
class Solution:
# @param {integer[]} nums
# @return {string}
def largestNumber(self, nums):
largest_num = ''.join(sorted(map(str, nums), key=LargerNumKey))
return '0' if largest_num[0] == '0' else largest_num
class Solution:
# @param {integer[]} nums
# @return {string}
def largestNumber(self, nums):
strarrs = sorted([str(x) for x in nums], reverse=True, key=lambda x: x * 100)
return ''.join(strarrs) if strarrs[0] != '0' else '0'
5.3 時間效率與空間效率的平衡
面試題34 醜數
class Solution:
ugly = sorted(2**a * 3**b * 5**c
for a in range(32) for b in range(20) for c in range(14))
def nthUglyNumber(self, n):
return self.ugly[n-1]
#(1) *1x2, 2x2, *2x2, 3x2, *3x2, *4x2, 5x2...
#(2) 1x3, *1x3, 2x3, 2x3, *2x3, 3x3, *3x3...
#(3) 1x5, 1x5, 1x5, *1x5, 2x5, 2x5, 2x5...
def nthUglyNumber(self, n):
ugly = [1]
i2, i3, i5 = 0, 0, 0
while n > 1:
u2, u3, u5 = 2 * ugly[i2], 3 * ugly[i3], 5 * ugly[i5]
umin = min(u2, u3, u5)
if umin == u2:
i2 += 1
if umin == u3:
i3 += 1
if umin == u5:
i5 += 1
ugly.append(umin)
n -= 1
return ugly[-1]
面試題35 第一個只出現一次的字符
def first_not_repeating_char(string):
if not string:
return -1
count = {}
loc = {}
for k, s in enumerate(string):
count[s] = count[s] + 1 if count.get(s) else 1
loc[s] = loc[s] if loc.get(s) else k
ret = float('inf')
for k in loc.keys():
if count.get(k) == 1 and loc[k] < ret:
ret = loc[k]
return ret
面試題36 數組中的逆序對
思路:歸併排序,時間複雜度o(nlogn)
import copy
class Inverse:
def InversePairs(self, array):
if not array:
return 0
arrCopy = copy.deepcopy(array)
return self.InverseRecur(array, arrCopy, 0, len(array)-1)
def InverseRecur(self, array, arrCopy, start, end):
if start == end:
return 0
mid = (start + end) // 2
left = self.InverseRecur(array, arrCopy, start, mid)
right = self.InverseRecur(array, arrCopy, mid+1, end)
count = 0
i = mid
j = end
locCopy = end
while i>=start and j > mid:
if array[i] > array[j]:
count += j - mid
arrCopy[locCopy] = array[i]
locCopy -= 1
i -= 1
else:
arrCopy[locCopy] = array[i]
locCopy -= 1
i -= 1
while i >= start:
arrCopy[locCopy] = array[i]
locCopy -= 1
i -= 1
while j > mid:
arrCopy[locCopy] = array[j]
locCopy -= 1
j -= 1
s = start
while s <= end:
array[s] = arrCopy[s]
s += 1
return left + right + count
面試題37 兩個鏈表的第一個公共結點
def getIntersectionNode(self, headA, headB):
if headA and headB:
A, B = headA, headB
while A!=B:
A = A.next if A else headB
B = B.next if B else headA
return A
第6章 面試能力
6.3 知識遷移能力
面試題38 數字在排序數組中出現的次數
def binarysearch(nums,k):
start,end=0,len(nums)-1
if end < 0:
return 0
while start <= end:
mid=(end - start)/2 +start
if nums[mid] == k:
#print(mid)
return mid
elif nums[mid] < k:
start = mid + 1
else:
end = mid -1
return 0
def timesofnumber(nums,k):
length = len(nums)
if length <=0: return 0
right = left = binarysearch(nums,k)
while left > 0 and nums[left-1] == k:
left = binarysearch(nums[:left],k)
while right < length -1 and nums[right+1] == k:
right += binarysearch(nums[right+1:],k)+1
if right == 0 and left ==0 and nums[0] != k:
return 0
return right-left+1
面試題39 二叉樹的深度
def maxDepth(self, root):
"""
遞歸
if not root:
return 0
return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1
"""
#層次遍歷
if not root:
return 0
estack = [root]
depth = 0
while estack:
depth+=1
new_root = []
for item in estack:
if item.left:
new_root.append(item.left)
if item.right:
new_root.append(item.right)
# minDepth start
if not item.left and not item.right:
return depth
# minDepth finish
estack = new_root
return depth
面試題40 數組中只出現一次的數字
#其它值都出現兩次
def singleNumber(self, nums):
x = 0
for a in nums:
x ^= a
return x
#其它值都出現三次
def singleNumber(self, nums):
a = set(nums);
a = sum(a) * 3 - sum(nums);
a = a/2;
return a;
面試題41 和爲s的兩個數字VS和爲s的連續正數序列
輸入一個遞增排序的數組和一個數字s,在數組中查找兩個數,使其和爲s
def sum_to_s(nums, s):
head, end = 0, len(nums) - 1
while head < end:
if nums[head] + nums[end] == s:
return [nums[head], nums[end]]
elif nums[head] + nums[end] > s:
end -= 1
else:
head += 1
return None
面試題42 翻轉單詞順序與左旋轉字符串
def reverseWords(self, s):
return ' '.join(s.split()[::-1])
def rotate_string(s, n):
if not s:
return ''
n %= len
return s[n:] + s[:n]