劍指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]

 

發佈了26 篇原創文章 · 獲贊 12 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章