《劍指 Offer》(第 2 版) 題解(Python 語言實現)第 01-10 題

序號 題目 難易程度 思路 備注
3 數組中重復的數字 1、桶排序;2、二分法。 多寫幾遍
LeetCode 第 287 題:尋找重復數。
4 二維數組的查找 從右上角或者左下角開始找。
6 從尾到頭打印鏈表 重點理解:在回溯的時候打印。或者用棧。
7 重建二叉樹 遞歸,算好偏移量,拿具體例子分析不會出錯。
LeetCode 第 105 題、LeetCode 第 106 題。
8 二叉樹的下一個結點 關鍵在于討論是否有右子樹。
9 用兩個棧實現隊列 重要 得用兩個棧,一定是 stack2 空,才做“翻轉”。 多寫幾遍
用隊列實現棧 重要 用一個隊列就可以了。
10 斐波拉契數列數列 ==思考如何與快速冪扯上關系。==
基礎斐波拉契數列 滾動數組,a , b = a + b , a
變態跳臺階 1、動態規劃;2、歸納出通項公式。
==矩陣求法== 重要
11 ==旋轉數組中的最小數字== 重要 1、二分法;2、分治。
12 矩陣中的路徑 重要 floodfill,理解狀態重置,先占位置,如果不符合要求,要把狀態重置。
LeetCode 第 79 題:單詞搜索。
13 機器人的運動范圍 重要 BFS,只要分析不是,就可以 mark 為 True。
14 剪繩子 重要 1、動態規劃;2、貪心算法。 要會畫樹形圖。
LeetCode 第 343 題:整數分割。
15 二進制中 1 的個數 重要 位運算。n & (n - 1) 把最低位的 1 變成 0
Python 中寫法有點不大一樣:預處理:n = n & 0xFFFFFFFF
16 數值的整數次方 特別重要 快速冪,“遞歸”和“循環”都要會做。
LeetCode 第 50 題。“循環”的代碼記住就可以了。
17 打印從 1 到最大的 n 位數
18 刪除鏈表中的結點 注意兩個結點非空一起判斷。
LeetCode 第 82 題:保留 1 個
LeetCode 第 83 題:相同不保留
19 正則表達式匹配 困難 動態規劃。
20 表示數值的字符串 困難
21 調整數組使得奇數位于偶數之前 兩路快排,l > r 才停止,掌握更通用的寫法。
22 鏈表中倒數第 k 個結點 快慢指針。
23 鏈表中環的入口結點
24 反轉鏈表
25 合并排序的鏈表
26 樹的子結構
27 翻轉二叉樹
28 判斷一棵二叉樹是否對稱 使用雙端隊列。
29 順時針打印矩陣
30 包含 min 函數的棧 設置輔助棧,只有元素比當前所有都小的時候,才 push。
31 棧的壓入、彈出序列
32 不分行從上往下打印二叉樹
分行從上往下打印二叉樹
之字形打印二叉樹
33 二叉搜索樹的后序遍歷序列 易錯 遞歸。
34 二叉樹中和為某一值的路徑 重要 回溯,注意狀態要重置。
35 復雜鏈表的復制
36 二叉搜索樹與雙向鏈表 重點 1、遞歸返回 tuple;2、分治。
37 序列化二叉樹 易錯 前序遍歷,定義好分隔符和空結點表示。
38 字符串的排列 重要 掌握如何去掉重復。
39 數組中超過一半的數字
40 最小的 K 個數 快排,嚴格小于的才放到前面,堆。
41 數據流中的中位數 堆。
42 連續子數組的最大和 重要 狀態:以某個數結尾的。最后要拉通求一遍。
43 從 1 到 n 整數中 1 出現的次數 困難
44 數字序列中某一位的數字 困難
45 把數組排成最小的數
46 把數字翻譯成字符串
LeetCode 第 91 題:解碼方法。
47 禮物的最大價值
48 最長不重復字符串的子字符串 1、滑動窗口;2、動態規劃;3、隔板法。
LeetCode 第 3 題:最長不重復字符串
49 丑數
50 字符串中第一個只出現一次的字符
字符流中第一個只出現一次的字符
51 逆序對
52 兩個鏈表的第一個公共結點 記住寫法。
53 數字在排序數組中出現的次數 二分法。
54 二叉搜索樹的第 k 大結點
55 二叉樹的深度
平衡二叉樹
56 數組中只出現一次的兩個數字
0 到 n-1 中缺失的數字
數組中數值和下標相等的元素
數組中唯一只出現一次的數字
57 和為 S 的兩個數字
和為 S 的連續正數序列
58 翻轉單詞順序列
左旋轉字符串
59 滑動窗口最大值 重要
60 n 個骰子的點數
61 撲克牌順子
62 圓圈中最后剩下的數
63 股票的最大利潤 有一些擴展問題。
64 求 1 + 2 + 3 + ... + n
65 不用加減乘除做加法
66 構建乘積數組
67 把字符串轉換成整數
68 樹中兩個節點的最近公共祖先

第 3 題:數組中重復的數字(桶排序,抽屜原理)

傳送門:AcWing:數組中重復的數字

給定一個長度為 n 的整數數組 nums,數組中所有的數字都在 0~n?1 的范圍內。

數組中某些數字是重復的,但不知道有幾個數字重復了,也不知道每個數字重復了幾次。

請找出數組中任意一個重復的數字。

注意:如果某些數字不在 0~n?1 的范圍內,或數組中不包含重復數字,則返回 -1

樣例:

給定 nums = [2, 3, 5, 4, 3, 2, 6, 7]

返回 23

思路1:最容易想到用哈希表判重。在 n 不超過 32 的時候,使用位運算可以實現 O(1) 空間復雜度判重。

思路2:排序以后,再遍歷一遍就知道哪個重復了。

思路3:“抽屜原理”。這道題實際上是要求我們使用桶排序的思想(一個蘿卜一個坑),找出重復的數字。

Python 代碼:這個解法會修改原始數組

class Solution(object):
    def duplicateInArray(self, nums):
        """
        :type nums: List[int]
        :rtype int
        """

        size = len(nums)
        if size < 2:
            return -1
        
        # 先統一檢查數字是不是越界了
        for i in range(size):
            if nums[i] < 0 or nums[i] > size - 1:
                return -1

        for i in range(size):

            # nums[i] 應該在 i 的位置上
            while i != nums[i]:
                # 發現要交換的那個數和自己一樣,就可以返回了
                if nums[i] == nums[nums[i]]:
                    return nums[i]
                self.__swap(nums, i, nums[i])
        return -1

    def __swap(self, nums, index1, index2):
        if index1 == index2:
            return
        temp = nums[index1]
        nums[index1] = nums[index2]
        nums[index2] = temp

思路4:下面的問題可以不修改數組找出重復的數字,即使用“二分法”。

LeetCode 第 287 題:尋找重復數

傳送門:287. 尋找重復數

給定一個包含 n + 1 個整數的數組 nums,其數字都在 1 到 n 之間(包括 1 和 n),可知至少存在一個重復的整數。假設只有一個重復的整數,找出這個重復的數。

示例 1:

輸入: [1,3,4,2,2]
輸出: 2

示例 2:

輸入: [3,1,3,4,2]
輸出: 3

說明:

  1. 不能更改原數組(假設數組是只讀的)。
  2. 只能使用額外的 O(1) 的空間。
  3. 時間復雜度小于 O(n^2)
  4. 數組中只有一個重復的數字,但它可能不止重復出現一次。

思路:分治法,用二分去做,是對“數”做二分,而不是對“索引”做二分。

Python 代碼:使用了二分法的模板,要定位的“數”根據題意在 1n 之間

class Solution:

    def findDuplicate(self, nums):
        """
        【不修改數組找出重復的數字】
        給定一個包含 n + 1 個整數的數組 nums,
        其數字都在 1 到 n 之間(包括 1 和 n),
        可知至少存在一個重復的整數。
        假設只有一個重復的整數,找出這個重復的數。
        :type nums: List[int]
        :rtype: int
        """
        left = 1
        right = len(nums) - 1
        while left < right:
            # 取中點有兩種方式,偏左和偏右
            mid = left + (right - left + 1) // 2  # 4
            count = 0
            for num in nums:
                if num < mid:
                    count += 1
            if count < mid:
                # 比 4 小的個數,最多就只能是 3
                # 所以重復的肯定不是 [1,2,3],不能排除 4
                # 因為左邊不變,所以取中點的時候,就要偏右
                left = mid
            else:
                # 比 4 小的個數,達到 4 或者更多
                # 重復的就落在 [1,2,3]
                right = mid - 1
        # 跳出循環肯定是因為 start = end
        return left

參考資料:《劍指 Offer》(第 2 版)第 3 題:數組中重復的數字

第 4 題:二維數組中的查找

同 LeetCode 第 240 題,LeetCode 傳送門:搜索二維矩陣 II,AcWing:二維數組中的查找,牛客網傳送門:二維數組中的查找

在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。

請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

樣例:

輸入數組:

[
     [1,2,8,9],
     [2,4,9,12],
     [4,7,10,13],
     [6,8,11,15]
]

如果輸入查找數值為 7,則返回 true,

如果輸入查找數值為 5 ,則返回 false。

分析:有點像 LeetCode 上島嶼的問題,特別之處:從右上角開始找,或者從左下角開始找,為什么不能選左上或者右下開始,因為不能縮小查找范圍。首先選取數組中右上角的數字。如果該數字等于要查找的數字,查找過程結束;如果該數字大于要查找的數組,剔除這個數字所在的列;如果該數字小于要查找的數字,剔除這個數字所在的行。也就是說如果要查找的數字不在數組的右上角,則每一次都在數組的查找范圍中剔除一行或者一列,這樣每一步都可以縮小查找的范圍,直到找到要查找的數字,或者查找范圍為空。
[圖片上傳失敗...(image-54ad66-1549813200679)]

Python 代碼:從右上角開始找,一個一個地找。小了向下面走,大了向左邊走

class Solution(object):

    def searchArray(self, array, target):
        rows = len(array)
        if rows == 0:
            return False

        cols = len(array[0])
        if rows > 0 and cols > 0:
            row = 0
            col = cols - 1
            # 注意:在橫縱坐標都有意義的時候,才可以搜索,因此用 and
            while row < rows and col >= 0:
                if target == array[row][col]:
                    return True
                elif target < array[row][col]:
                    # [4, 5, 6, 12, 13] 找 7
                    col -= 1
                else:
                    # [7]
                    # [8]
                    # [12] 找 9
                    row += 1
        # 全部走完都找不到,就說明沒有
        return False

說明:其實不管是每行還是每列,都是有序數組,所以可以使用二分法。我寫了個二分法,只是作為練習。但是二分法不能保證一次寫對,所以不建議在面試的時候寫。

Python 代碼:(了解即可)

# 4、二維數組中的查找

class Solution(object):

    # 二分法查找規律
    # 1、從右到左,找第 1 個小于或者等于 target 的數
    # 2、從上到下,找第 1 個大于或者等于 target 的數

    def searchArray(self, array, target):
        """
        :type array: List[List[int]]
        :type target: int
        :rtype: bool
        """

        rows = len(array)
        if rows == 0:
            return False
        cols = len(array[0])

        col = cols - 1
        row = 0

        while row < rows and col >= 0:

            # print('row', row, 'col', col, array[row][0])
            # 1、從右到左,找第 1 個小于或者等于 target 的數
            if col == 0 and array[row][0] > target:
                return False
            l = 0
            r = col
            while l < r:
                mid = l + (r - l + 1) // 2
                if array[row][mid] <= target:
                    l = mid
                else:
                    assert array[row][mid] > target
                    r = mid - 1
            col = l

            # 2、從上到下,找第 1 個大于或者等于 target 的數
            if row == rows - 1 and array[rows - 1][col] < target:
                return False

            l = row
            r = rows - 1
            while l < r:
                mid = l + (r - l) // 2
                if array[mid][col] >= target:
                    r = mid
                else:
                    assert array[mid][col] < target
                    l = mid + 1
            row = l

            if array[row][col] == target:
                return True

        return False


if __name__ == '__main__':
    array = [[1, 2, 8, 9],
             [2, 4, 9, 12],
             [4, 7, 10, 13],
             [6, 8, 11, 15]]
    target = 16
    solution = Solution()
    result = solution.searchArray(array, target)
    print(result)

LeetCode 第 74 題:搜索二維矩陣

傳送門:搜索二維矩陣

編寫一個高效的算法來判斷 m x n 矩陣中,是否存在一個目標值。該矩陣具有如下特性:

  • 每行中的整數從左到右按升序排列。
  • 每行的第一個整數大于前一行的最后一個整數。

示例 1:

輸入:
matrix = [
[1,   3,  5,  7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 3
輸出: true

示例 2:

輸入:
matrix = [
[1,   3,  5,  7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 13
輸出: false

Python 代碼1:“標準的”二分法

class Solution(object):
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        m = len(matrix)
        if m == 0:
            return False
        n = len(matrix[0])
        if n == 0:
            return False
        left = 0
        # 這里一定要記得減 1
        right = m * n - 1
        while left <= right:
            mid = left + (right - left) // 2
            # 定位到矩陣中
            num = matrix[mid // n][mid % n]
            if num == target:
                return True
            elif num < target:
                left = mid + 1
            else:
                right = mid - 1
        return False

Python 代碼2:“神奇的”二分法模板

class Solution(object):
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        m = len(matrix)
        if m == 0:
            return False
        n = len(matrix[0])
        # [[]] 針對這種情況,要特判
        if n == 0:
            return False

        l = 0
        r = m * n - 1

        while l < r:
            mid = l + (r - l) // 2
            if matrix[mid // n][mid % n] < target:
                l = mid + 1
            else:
                r = mid
        # 這個模板在退出循環的時候 l == r 成立,但是有可能存在不滿足條件的時候
        # 所以要單獨判斷
        return matrix[l // n][l % n] == target

第 6 題:從尾到頭打印鏈表

傳送門:AcWing:從尾到頭打印鏈表

輸入一個鏈表的頭結點,按照 從尾到頭 的順序返回節點的值。

返回的結果用數組存儲。

樣例:

輸入:[2, 3, 5]
返回:[5, 3, 2]

思路1:首先應該想到,使用棧作為輔助。

Python 代碼1:Python 中的列表有可以在指定位置插入元素,我們就每次在索引 0 處插入元素好了

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution(object):

    def printListReversingly(self, head):
        """
        :type head: ListNode
        :rtype: List[int]
        """
        p = head
        stack = []
        while p:
            stack.append(p.val)
            p = p.next
        return stack[::-1]   

Python 代碼2:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution(object):

    def printListReversingly(self, head):
        """
        :type head: ListNode
        :rtype: List[int]
        """
        p = head
        stack = []
        while p:
            stack.insert(0, p.val)
            p = p.next
        return stack

思路2:使用遞歸,關鍵在于遞歸函數的編寫,特別注意:在回溯的時候,添加當前結點的值到結果集中。

Python 代碼:

class Solution(object):

    def printListReversingly(self, head):
        """
        :type head: ListNode
        :rtype: List[int]
        """
        res = []
        self.helper(res, head)
        return res

    def helper(self, res, listnode):
        if listnode is None:
            return
        # 應該先判斷下一個結點是否為空,如果不為空,則遞歸調用,在回溯的時候,才添加到結果中
        if listnode.next:
            self.helper(res, listnode.next)
        # 這一步特別關鍵:回溯時添加
        res.append(listnode.val)

思考下面這個寫法為什么是錯的。

image-20190109212447435

拿具體的測試用例就可以很容易想明白,不能使用 if else 語句。

image-20190209135518985

第 7 題:重建二叉樹(遞歸)

同 LeetCode 第 105 題,傳送門:從前序與中序遍歷序列構造二叉樹

傳送門:AcWing:重建二叉樹

輸入一棵二叉樹前序遍歷和中序遍歷的結果,請重建該二叉樹。

注意:

  • 二叉樹中每個節點的值都互不相同;
  • 輸入的前序遍歷和中序遍歷一定合法;

樣例:

給定:
前序遍歷是:[3, 9, 20, 15, 7]
中序遍歷是:[9, 3, 15, 20, 7]

返回:[3, 9, 20, null, null, 15, 7, null, null, null, null]
返回的二叉樹如下所示:

     3
    / \
  9  20
      / \
    15  7

思路:遞歸重建。二叉樹的 DFS 有如下三種遍歷方式:

  • 前序遍歷:先訪問根結點,再訪問左子結點,最后訪問右子結點。
  • 中序遍歷:先訪問左子結點,再訪問根結點,最后訪問右子結點。
  • 后序遍歷:先訪問左子結點,再訪問右子結點,最后訪問根結點。

本題為前序遍歷和中序遍歷,最少需要兩種遍歷方式,才能重建二叉樹。

關鍵:前序遍歷數組的第 1 個數(索引為 0)的數一定是二叉樹的根結點,于是可以在中序遍歷中找這個根結點的索引,然后把“前序遍歷數組”和“中序遍歷數組”分為兩個部分,就分別對應二叉樹的左子樹和右子樹,分別遞歸完成就可以了。

image-20190120134854260

注意:1、編寫遞歸方法的時候,先寫特殊情況;

2、索引是多少不好判斷的時候,干脆就用一個具體的例子,就比如我上面畫的這個圖,把具體的數換成我們使用的變量,這樣思考的難度會降低,而且還不容易出錯。

Python 代碼:

class TreeNode(object):
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None


class Solution(object):
    def buildTree(self, preorder, inorder):
        """
        返回構造的 TreeNode 根結點
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        # 在編碼過程中,一定要保證 len(pre) == len(tin),否則邏輯一定不正確
        if len(preorder) == 0:
            return None
        if len(preorder) == 1:
            # 這里要返回結點,而不是返回具體的數
            return TreeNode(preorder[0])
        root = TreeNode(preorder[0])
        # 直接得到在中序遍歷中的位置,下面算好偏移量就好了,如果容易算錯,記得拿具體例子
        pos = inorder.index(preorder[0])
        root.left = self.buildTree(preorder[1:pos + 1], inorder[:pos])
        root.right = self.buildTree(preorder[pos + 1:], inorder[pos + 1:])
        return root

類似問題:LeetCode 第 106 題。

LeetCode 第 106 題:從中序與后序遍歷序列構造二叉樹

傳送門:106. 從中序與后序遍歷序列構造二叉樹

根據一棵樹的中序遍歷與后序遍歷構造二叉樹。

注意:
你可以假設樹中沒有重復的元素。

例如,給出

中序遍歷 inorder = [9,3,15,20,7]
后序遍歷 postorder = [9,15,7,20,3]

返回如下的二叉樹:

    3
   / \
  9  20
      / \
     15  7

思路:二叉樹的問題,在紙上寫寫畫畫更形象。

Python 代碼:

class TreeNode(object):
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None


class Solution:
    def buildTree(self, inorder, postorder):
        """
        :type inorder: List[int]
        :type postorder: List[int]
        :rtype: TreeNode
        """

        assert len(inorder) == len(postorder)

        if len(inorder) == 0:
            return None
        if len(inorder) == 1:
            # 這里要返回結點,而不是返回具體的數
            return TreeNode(inorder[0])
        
        # 最后一個結點是根結點
        root = TreeNode(postorder[-1])

        pos = inorder.index(postorder[-1])

        root.left = self.buildTree(inorder[:pos], postorder[:pos])
        root.right = self.buildTree(inorder[pos + 1:], postorder[pos:-1])
        return root


# 用于驗證的方法
def validate(node):
    if node is None:
        return
    validate(node.left)
    print(node.val, end=' ')
    validate(node.right)


if __name__ == '__main__':
    inorder = [9, 3, 15, 20, 7]
    postorder = [9, 15, 7, 20, 3]
    solution = Solution()
    root = solution.buildTree(inorder, postorder)
    validate(root)

第 8 題:二叉樹的下一個結點

傳送門:AcWing:二叉樹的下一個結點

給定一棵二叉樹的其中一個節點,請找出中序遍歷序列的下一個節點。

注意:

  • 如果給定的節點是中序遍歷序列的最后一個,則返回空節點;
  • 二叉樹一定不為空,且給定的節點一定不是空節點;

樣例:

假定二叉樹是:[2, 1, 3, null, null, null, null], 給出的是值等于 2 的節點。

則應返回值等于3的節點。

解釋:該二叉樹的結構如下,2 的后繼節點是 3 。

     2
   / \
1   3

思路:用《算導》中提出的方法,畫圖分析,把要分類討論的情況分析清楚,編碼就很容易了。這道題的關鍵在于:看是否有右子樹

image-20190107113109744

畫個清楚的圖幫助理解:

image-20190126034007896

Python 代碼:

class Solution(object):
    def inorderSuccessor(self, q):
        """
        :type q: TreeNode
        :rtype: TreeNode
        """

        if q is None:
            return None

        # 分類討論1:如果這個結點有右子樹,返回這個右子樹的最小者
        if q.right:
            node = q.right
            while node.left:
                node = node.left
            return node
        # 分類討論2:如果這個結點沒有右子樹,向上追溯,追到父親結點的左結點是自己
        while q.father:
            if q.father.left == q:
                return q.father
            q = q.father
        return None

第 9-1 題:用兩個棧實現隊列

傳送門:AcWing:用兩個棧實現隊列

請用棧實現一個隊列,支持如下四種操作:

  • push(x) – 將元素x插到隊尾;
  • pop() – 將隊首的元素彈出,并返回該元素;
  • peek() – 返回隊首元素;
  • empty() – 返回隊列是否為空;

注意:

  • 你只能使用棧的標準操作:push to toppeek/pop from top, sizeis empty
  • 如果你選擇的編程語言沒有棧的標準庫,你可以使用list或者deque等模擬棧的操作;
  • 輸入數據保證合法,例如,在隊列為空時,不會進行pop或者peek等操作;

樣例

MyQueue queue = new MyQueue();

queue.push(1);
queue.push(2);
queue.peek();  // returns 1
queue.pop();   // returns 1
queue.empty(); // returns false

注意:下面這個邏輯是錯的,應該是只要 stack2 是空的,才把 stack1 的元素全部搬到 stack2,這里要小心。

    def __shift(self):
        if self.stack1:
            while self.stack1:
                self.stack2.append(self.stack1.pop())

Python 代碼:

class MyQueue(object):

    def __init__(self):
        """
        Initialize your data structure here.
        """

        self.stack1 = []
        self.stack2 = []

    def push(self, x):
        """
        Push element x to the back of queue.
        :type x: int
        :rtype: void
        """
        self.stack1.append(x)

    def __shift(self):
        if len(self.stack2) == 0:
            while self.stack1:
                self.stack2.append(self.stack1.pop())

    def pop(self):
        """
        Removes the element from in front of queue and returns that element.
        :rtype: int
        """
        self.__shift()
        return self.stack2.pop()

    def peek(self):
        """
        Get the front element.
        :rtype: int
        """
        self.__shift()
        return self.stack2[-1]

    def empty(self):
        """
        Returns whether the queue is empty.
        :rtype: bool
        """
        return len(self.stack1) == 0 and len(self.stack2) == 0

# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()

第 9-2 題:用隊列實現棧

同 LeetCode 第 225 題。

傳送門:225. 用隊列實現棧

使用隊列實現棧的下列操作:

  • push(x) -- 元素 x 入棧
  • pop() -- 移除棧頂元素
  • top() -- 獲取棧頂元素
  • empty() -- 返回棧是否為空

注意:

  • 你只能使用隊列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 這些操作是合法的。
  • 你所使用的語言也許不支持隊列。 你可以使用 list 或者 deque(雙端隊列)來模擬一個隊列 , 只要是標準的隊列操作即可。
  • 你可以假設所有操作都是有效的(例如, 對一個空的棧不會調用 pop 或者 top 操作)。

Python 代碼:

class MyStack:

    def __init__(self):
        """
        Initialize your data structure here.
        """

        self.queue = []

    def push(self, x):
        """
        Push element x onto stack.
        :type x: int
        :rtype: void
        """
        self.queue.append(x)
        # 將隊列中前面已經逆序的元素放在 x 元素后面,使得整體逆序
        for _ in range(len(self.queue) - 1):
            ele = self.queue.pop(0)
            self.queue.append(ele)

    def pop(self):
        """
        Removes the element on top of the stack and returns that element.
        :rtype: int
        """
        if self.queue:
            return self.queue.pop(0)

    def top(self):
        """
        Get the top element.
        :rtype: int
        """
        if self.queue:
            return self.queue[0]

    def empty(self):
        """
        Returns whether the stack is empty.
        :rtype: bool
        """
        return len(self.queue) == 0

# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()

第 10 題:跳臺階(斐波拉契數列、滾動變量)

傳送門:AcWing:跳臺階

輸入一個整數 n ,求斐波那契數列的第 n 項。

假定從 0 開始,第 0 項為 0 。(n \le 39)

樣例:

輸入整數 n=5

返回 5

思路:這題的數據范圍很小,我們直接模擬即可。當數據范圍很大時,就需要采用其他方式了,可以參考求解斐波那契數列的若干方法

時間復雜度:總共需要計算 n 次,所以時間復雜度是 O(n)

Python 代碼1:用兩個變量滾動式往后計算,a 表示第 n?1 項,b 表示第 n 項。則令 c=a+b 表示第 n+1 項,然后讓 ab 順次往后移一位。

class Solution(object):
    def Fibonacci(self, n):
        """
        :type n: int
        :rtype: int
        """

        if n == 0:
            return 0
        if n == 1:
            return 1
        a = 0
        b = 1

        while n:
            c = a + b
            # “滾動變量”:接下來重新定義 a 和 b
            a = b
            b = c
            n -= 1
        return a

Python 代碼2:Python 語法糖,了解即可

class Solution(object):
    def Fibonacci(self, n):
        """
        :type n: int
        :rtype: int
        """

        if n == 0:
            return 0
        if n == 1:
            return 1
        a = 0
        b = 1

        while n:
            a , b = a + b , a
            n -= 1
        return a

Python 代碼3:

class Solution:
    def climbStairs(self, n):
        """
        :type n: int
        :rtype: int
        """
        a = 0
        b = 1
        
        while n:
            a , b = b , a + b 
            n -= 1
        return b

參考資料:面試官問你斐波那契數列的時候不要高興得太早。書上斐波拉契數列數列空間更省的寫法,P76。

第 10-2 題:變態跳臺階

傳送門:牛客網:變態跳臺階

一只青蛙一次可以跳上 1 級臺階,也可以跳上 2 級,……,它也可以跳上 n 級。求該青蛙跳上一個 n 級的臺階總共有多少種跳法。

Python 代碼:因為青蛙一次可以跳任意臺階,我們就讓它跳 n 階,所以初始值設置為 1

class Solution:
    def jumpFloorII(self, number):
        if number == 0:
            return 1
        if number == 1:
            return 1
        dp = [1 for _ in range(number + 1)]
        dp[0] = 0
        dp[1] = 1
        for i in range(2, number + 1):
            for j in range(1, i):
                dp[i] += dp[j]
        return dp[number]

思路2:

n=1 時,結果為 1

n=2 時,結果為 2

n=3 時,結果為 4

……

以此類推,使用數學歸納法不難發現,跳法 f(n)=2^{n-1}

Python 代碼:

class Solution:
    def jumpFloorII(self, number):
        # write code here
        if number <= 2:
            return number
        total = 1
        for _ in range(1, number):
            total *= 2
        return total

第 10-3 題:斐波拉契數列矩陣求法

參考資料:求解斐波那契數列的若干方法

(本節完)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379