第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]= 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]