剑指Offer -- Python版

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

猜你喜欢

转载自blog.csdn.net/ZJL0105/article/details/81090678