劍指offer--Leetcode版


面試題03. 數組中重復的數字

在一個長度為 n 的數組 nums 里的所有數字都在 0~n-1 的范圍內。數組中某些數字是重復的,但不知道有幾個數字重復了,也不知道每個數字重復了幾次。請找出數組中任意一個重復的數字。

思路:1. 利用字典,時間復雜度O(n),空間復雜度O(n)

class Solution:
    def findRepeatNumber(self, nums: List[int]) -> int:
        dic={}
        for i in nums:
            if i not in dic:
                dic[i]=1
            else:
                return i
        return False

思路:2. 數組哈希法,遍歷數組,把序列[2,3,1,0,2,5,3]修改成一個下標和下標對應值是相同的數組[0,1,2,3,2,5,3] (nums[nums[i]] = nums[i]),尋找當前位(i, 4)的值(nums[i], 2)和當前位的值(nums[i], 2)作為下標的值(nums[nums[i]], 2)相等,時間復雜度O(n),空間復雜度O(1)

class Solution:
    def findRepeatNumber(self, nums: List[int]) -> int:
        n = len(nums)
        for i in range(n):
            while nums[i] != i:
                if nums[i] == nums[nums[i]]:
                    return nums[i]
                nums[nums[i]], nums[i] = nums[i], nums[nums[i]]
        return False

面試題04. 二維數組中的查找

在一個 n * m 的二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

思路:利用二分查找,左下角的元素是這一行中最小的元素,同時又是這一列中最大的元素,比較左下角元素和目標。

  時間復雜度O(n),空間復雜度O(1)

class Solution:
    def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
        x = len(matrix)
        if x == 0:
            return False
        y = len(matrix[0])
        if y == 0:
            return False
        i = x-1
        j = 0
        while i >= 0 and j < y:
            if matrix[i][j] == target:
                return True
            elif matrix[i][j] < target:
                j += 1
            else:
                i -= 1
        return False

面試題05. 替換空格

請實現一個函數,把字符串 s 中的每個空格替換成"%20"。

思路:利用字符串內置函數,str.replace(), str.split(), "".join()

  時間復雜度O(n),空間復雜度O(1)

class Solution:
    def replaceSpace(self, s: str) -> str:
        return "%20".join(s.split(" "))

面試題06. 從尾到頭打印鏈表

輸入一個鏈表,按鏈表值從尾到頭的順序返回一個ArrayList。

思路:利用棧,遍歷鏈表,先入后出

  時間復雜度O(n),空間復雜度O(n)

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

class Solution:
    def reversePrint(self, head: ListNode) -> List[int]:
        ArrayList = []
        while head:
            ArrayList.append(head.val)
            head = head.next
        return ArrayList[::-1]

 面試題07. 重建二叉樹

輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重復的數字。

思路:利用遞歸(深度優先搜索)。前序遍歷序列中,第一個數字總是樹的根結點的值。在中序遍歷序列中,根結點的值在序列的中間,左子樹的結點的值位於根結點的值的左邊,而右子樹的結點的值位於根結點的值的右邊。

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

  時間復雜度O(n),空間復雜度O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if len(preorder)==0:
            return None
        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

面試題09. 用兩個棧實現隊列

用兩個棧實現一個隊列。隊列的聲明如下,請實現它的兩個函數 appendTail 和 deleteHead ,分別完成在隊列尾部插入整數和在隊列頭部刪除整數的功能。(若隊列中沒有元素,deleteHead 操作返回 -1 )

思路:利用兩個“先進后出”的棧實現一個“先進先出”的隊列,將隊列元素a, b, c 壓入stack1 [a, b, c],彈出壓入stack2 [c, b, a],stack2中的棧頂元素是最先進入隊列的元素。

  時間復雜度O(n),空間復雜度O(n)

class CQueue:

    def __init__(self):
        self.stack1 = []
        self.stack2 = []

    def appendTail(self, value: int) -> None:
        self.stack1.append(value)

    def deleteHead(self) -> int:
        if self.stack2:
            return  self.stack2.pop()
        else:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
            return self.stack2.pop()if self.stack2 else - 1


# Your CQueue object will be instantiated and called as such:
# obj = CQueue()
# obj.appendTail(value)
# param_2 = obj.deleteHead()

 面試題10- I. 斐波那契數列

寫一個函數,輸入 n ,求斐波那契(Fibonacci)數列的第 n 項。斐波那契數列的定義如下:

F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契數列由 0 和 1 開始,之后的斐波那契數就是由之前的兩數相加而得出。

答案需要取模 1e9+7(1000000007),如計算初始結果為:1000000008,請返回 1。

思路:利用動態規划,狀態轉移方程為f(n)=f(n-1)+f(n-2)

  時間復雜度O(n),空間復雜度O(n)

class Solution:
    def fib(self, n: int) -> int:
        if n <= 1:
            return n
        f = [0,1]
        for i in range(2,n+1):
            f.append(f[i-1]+f[i-2])
        return f[n]%1000000007

面試題10- II. 青蛙跳台階問題

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

答案需要取模 1e9+7(1000000007),如計算初始結果為:1000000008,請返回 1。

思路:利用動態規划,狀態轉移方程為f(n)=f(n-1)+f(n-2)

  時間復雜度O(n),空間復雜度O(n)

class Solution:
    def numWays(self, n: int) -> int:
        f = [1,1,2]
        for i in range(3,n+1):
            f.append(f[i-1]+f[i-2])
        return f[n]%1000000007

面試題11. 旋轉數組的最小數字

把一個數組最開始的若干個元素搬到數組的末尾,我們稱之為數組的旋轉。輸入一個遞增排序的數組的一個旋轉,輸出旋轉數組的最小元素。例如,數組 [3,4,5,1,2] 為 [1,2,3,4,5] 的一個旋轉,該數組的最小值為1。

思路:利用二分查找,比較numbers[mid] 和 numbers[right],分三種情況,numbers[mid] > numbers[right],最小數字一定在mid的右邊;numbers[mid] == numbers[right],此時最小數字不好判斷在mid左邊還是右邊;numbers[mid] < numbers[right],最小數字一定就是mid或者在mid的左邊。

  時間復雜度O(log(n)),空間復雜度O(1)

class Solution:
    def minArray(self, numbers: List[int]) -> int:
        if len(numbers) == 0:
            return 0
        left = 0
        right = len(numbers) - 1
        while left < right:
            mid = (right + left) // 2
            if numbers[mid] > numbers[right]:
                left = mid + 1
            elif numbers[mid] == numbers[right]:
                right -= 1
            else:
                right = mid
        return numbers[left]

面試題12. 矩陣中的路徑

請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。路徑可以從矩陣中的任意一格開始,每一步可以在矩陣中向左、右、上、下移動一格。如果一條路徑經過了矩陣的某一格,那么該路徑不能再次進入該格子。

思路:利用深度優先搜索是對圖中的點依次遍歷來找到第一個字符的位置,再利用回溯法,從第一個字符的位置開始按約束條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並滿足約束,就退回一步重新遍歷。本題約束條件是上下左右四個字符中需要存在與字符串中的下一個字符相同。

  時間復雜度O(n2),空間復雜度O(n)

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        if not word: return True
        if not board: return False
        for i in range(len(board)):
            for j in range(len(board[0])):
                if self.dfs(board, i, j, word):
                    return True
        return False
    
    def dfs(self, board, i, j, word):
        if not word:
            return True
        if i >= len(board) or i < 0 or j >= len(board[0]) or j < 0 or board[i][j] != word[0]: #匹配失敗跳出遞歸
            return False
        tmp = board[i][j]
        board[i][j] = '.'
        if self.dfs(board, i+1, j, word[1:]) or self.dfs(board, i-1, j, word[1:]) or self.dfs(board, i, j+1, word[1:]) or self.dfs(board, i, j-1, word[1:]):
            return True
        else:
            board[i][j] = tmp

面試題13. 機器人的運動范圍

地上有一個m行n列的方格,從坐標 [0,0] 到坐標 [m-1,n-1] 。一個機器人從坐標 [0, 0] 的格子開始移動,它每次可以向左、右、上、下移動一格(不能移動到方格外),也不能進入行坐標和列坐標的數位之和大於k的格子。例如,當k為18時,機器人能夠進入方格 [35, 37] ,因為3+5+3+7=18。但它不能進入方格 [35, 38],因為3+5+3+8=19。請問該機器人能夠到達多少個格子?

思路:利用回溯法,從坐標0,0位置開始按約束條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並滿足約束,就退回一步重新遍歷。約束條件是上下左右四個格子中存在行坐標和列坐標的數位之和小於k的格子。

  時間復雜度O(n),空間復雜度O(n)

class Solution:
    def movingCount(self, m: int, n: int, k: int) -> int:
        arr = [[1 for i in range(m)] for j in range(n)]
        self.findway(arr,0,0,k)
        return self.count
    def __init__(self):
        self.count = 0
    def findway(self,arr,i,j,k):
        if i < 0 or j< 0 or i >= len(arr) or j >= len(arr[0]):
            return
        #map(function, iterable, ...)根據提供的函數對指定序列做映射, 返回迭代器,map(int, list),['1','2']->[1,2]
        #list(string)把string中的字符轉為列表,'12'->['1','2']
        tmpi = list(map(int, list(str(i))))
        tmpj = list(map(int, list(str(j))))
        if sum(tmpi) + sum(tmpj) > k or arr[i][j] != 1:
            return
        arr[i][j] = 0
        self.count += 1
        self.findway(arr, i+1, j, k)
        self.findway(arr, i-1, j, k)
        self.findway(arr, i, j+1, k)
        self.findway(arr, i, j-1, k)

面試題14- I. 剪繩子

給你一根長度為 n 的繩子,請把繩子剪成整數長度的 m 段(m、n都是整數,n>1並且m>1),每段繩子的長度記為 k[0],k[1]...k[m] 。請問 k[0]*k[1]*...*k[m] 可能的最大乘積是多少?例如,當繩子的長度是8時,我們把它剪成長度分別為2、3、3的三段,此時得到的最大乘積是18。

思路:動態規划。邊界條件:dp[1] = dp[2] = 1;狀態轉移方程:dp[i] = max(dp[i], max((i - j) * j, j * dp[i - j]))。

  時間復雜度:O(n2)空間復雜度:O(n)

class Solution:
    def cuttingRope(self, n: int) -> int:
        dp = [0 for _ in range(n + 1)]  
        dp[1] = dp[2] = 1  
        for i in range(3, n + 1):
            for j in range(i):
                dp[i] = max(dp[i], max((i - j) * j, j * dp[i - j]))
        return dp[n]

面試題14- II. 剪繩子 II

給你一根長度為 n 的繩子,請把繩子剪成整數長度的 m 段(m、n都是整數,n>1並且m>1),每段繩子的長度記為 k[0],k[1]...k[m] 。請問 k[0]*k[1]*...*k[m] 可能的最大乘積是多少?例如,當繩子的長度是8時,我們把它剪成長度分別為2、3、3的三段,此時得到的最大乘積是18。答案需要取模 1e9+7(1000000007),如計算初始結果為:1000000008,請返回 1。

思路:同上

class Solution:
    def cuttingRope(self, n: int) -> int:
        dp = [0 for _ in range(n + 1)]  
        dp[1] = dp[2] = 1  
        for i in range(3, n + 1):
            for j in range(i):
                dp[i] = max(dp[i], max((i - j) * j, j * dp[i - j]))
        return dp[n]% int(1e9+7)

面試題15. 二進制中1的個數

思路1:整數除2取余,時間復雜度O(n),空間復雜度O(1)

class Solution:
    def hammingWeight(self, n: int) -> int:
        count = 0
        while n != 0:
            count += n%2
            n = n//2
        return count

思路2: bin() 返回一個整數的二進制表示,str.count()返回字符串里某個字符出現的次數,時間復雜度O(n),空間復雜度O(1)

class Solution:
    def hammingWeight(self, n: int) -> int:
        #負數用補碼表示,或者n=n&0xffffffff
        if n<0:
            return bin(2**32+n).count('1')
        return bin(n).count('1')

面試題16. 數值的整數次方

實現函數double Power(double base, int exponent),求base的exponent次方。不得使用庫函數,同時不需要考慮大數問題。

思路:遍歷指數exponent求次方,指數為負數的時候,可以先對指數取反,然后把指數 n 做“二進制分解”算出次方的結果之后再取倒數。

  時間復雜度O(n),空間復雜度O(1)

class Solution:
    def myPow(self, x: float, n: int) -> float:
        flag = 0
        result = 1
        if x == 0:
            return False
        if n < 0:
            flag = 1
            n = -n
        while n :
            if n & 1: 
                result *= x
            x *= x
            n >>= 1
        if flag == 1:
            result = 1 / result
        return result

面試題17. 打印從1到最大的n位數

 輸入數字 n,按順序打印出從 1 到最大的 n 位十進制數。比如輸入 3,則打印出 1、2、3 一直到最大的 3 位數 999。

class Solution:
    def printNumbers(self, n: int) -> List[int]:
        return [i for i in range(1, 10**n)]

面試題18. 刪除鏈表的節點

給定單向鏈表的頭指針和一個要刪除的節點的值,定義一個函數刪除該節點。返回刪除后的鏈表的頭節點。

思路:虛擬結點dummy node,遍歷鏈表。

  時間復雜度O(n),空間復雜度O(1)

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

class Solution:
    def deleteNode(self, head: ListNode, val: int) -> ListNode:
        dummy = ListNode(0)  
        dummy.next = head
        if head.val == val: return head.next
        while head and head.next:
            if head.next.val == val:   
                head.next = head.next.next
            head = head.next
        return dummy.next

面試題19. 正則表達式匹配

請實現一個函數用來匹配包含'. '和'*'的正則表達式。模式中的字符'.'表示任意一個字符,而'*'表示它前面的字符可以出現任意次(含0次)。在本題中,匹配是指字符串的所有字符匹配整個模式。例如,字符串"aaa"與模式"a.a"和"ab*ac*a"匹配,但與"aa.a"和"ab*a"均不匹配。

思路:利用動態規划,如果s[0..i-1] 匹配p[0..i-1],定義dp[i][j] 為 true,否則為 false。

  時間復雜度O(n2),空間復雜度O(1)

class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        s_len = len(s)
        p_len = len(p)
        dp = [[False] * (p_len + 1) for _ in range(s_len + 1)]
        dp[0][0] = True
        for i in range(p_len):
            if p[i] == "*" and dp[0][i - 1]:
                dp[0][i + 1] = True
        for i in range(s_len):
            for j in range(p_len):
                if p[j] == s[i] or p[j] == ".":
                    dp[i + 1][j + 1] = dp[i][j]
                elif p[j] == "*":
                    if p[j - 1] != s[i]:
                        dp[i + 1][j + 1] = dp[i + 1][j - 1]
                    if p[j-1] == s[i] or p[j-1] == ".":
                        dp[i+1][j+1] = (dp[i][j+1] or dp[i+1][j]   or  dp[i+1][j-1])
        return dp[-1][-1]

面試題20. 表示數值的字符串

請實現一個函數用來判斷字符串是否表示數值(包括整數和小數)。例如,字符串"+100"、"5e2"、"-123"、"3.1416"、"0123"及"-1E-16"都表示數值,但"12e"、"1a3.14"、"1.2.3"、"+-5"及"12e+5.4"都不是。

思路:分條件判斷,遍歷字符串

  時間復雜度O(n),空間復雜度O(1)

class Solution:
    def isNumber(self, s: str) -> bool:
        num = False
        numAfterE = True
        dot = False
        exp = False
        sign = False
        n = len(s)
        for i in range(n):
            # 如果當前為空格
            if (s[i] == ' '):
                # 如果后面不為空格,而且前面已經有了合格的字符 
                # 不合法的例子: '1e-2 +1.3e'
                if (i < n - 1 and s[i + 1] != ' ' and (num or dot or exp or sign)): return False
            # 如果當前為符號
            elif (s[i] == '+' or s[i] == '-'):
                # 如果前面不是e的話, 而且前面不是空格
                # 不合法的例子: '3-+'
                if (i > 0 and s[i - 1] != 'e' and s[i - 1] != 'E' and s[i - 1] != ' '):
                    return False
                #有了符號
                sign = True
            # 如果當前為數字
            elif (s[i] >= '0' and s[i] <= '9'):
                #有了數字
                num = True
                #數字在e后面
                if(exp):
                    numAfterE = True
            # 如果當前為小數點
            elif (s[i] == '.'):
                # 如果已經有了小數點或者自然指數
                # 不合法的例子 '1.2.'或者'99e2.5'
                if (dot or exp): 
                    return False
                # 有了小數點
                dot = True
            # 如果當前為自然指數
            elif (s[i] == 'e' or s[i] == 'E'):
                # 如果已經有了自然指數或者前面沒有數字
                # 不合法的例子 '2e10e' 或者'e3' 
                if (exp or not num):
                    return False
                # 有了指數,以及指數后面沒有數字
                exp = True
                numAfterE = False
            # 其他字符
            else:
                return False
        # 返回是否是數字以及指數后面是否有數字
        # 不合法的例子 1e
        return num and numAfterE

面試題21. 調整數組順序使奇數位於偶數前面

輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得所有奇數位於數組的前半部分,所有偶數位於數組的后半部分。

思路:1. 遍歷數組,奇數前插入,偶數后插入,時間復雜度O(n2),空間復雜度O(1)。

   2. 冒泡排序,前偶數后奇數相鄰就交換,時間復雜度O(n2),空間復雜度O(1)。

   3. 新建列表,類似雙向隊列,空間換時間,時間復雜度O(n),空間復雜度O(n)。

   4. 新建列表,先插入奇數,后插入偶數,時間復雜度O(n),空間復雜度O(n)。

class Solution:
    def exchange(self, nums: List[int]) -> List[int]:
        #1.遍歷數組,index記奇數索引,奇數往前插入,時間復雜度O(n2),空間復雜度O(1)
        index = -1
        for i in range(len(nums)):
            if nums[i] % 2 == 1:
                index += 1
                nums.insert(index,nums.pop(i))
        return nums

class Solution:
    def exchange(self, nums: List[int]) -> List[int]:
        #2.冒泡排序,前偶數后奇數相鄰就交換,時間復雜度O(n2),空間復雜度O(1)
        for i in range(len(nums)):
            for j in range(len(nums)-i-1):
                if nums[j]%2 == 0 and nums[j+1]%2 == 1:
                    nums[j], nums[j+1] = nums[j+1], nums[j]
        return nums

class Solution:
    def exchange(self, nums: List[int]) -> List[int]:
        #3.新建列表,類似雙向隊列,空間換時間,時間復雜度O(n),空間復雜度O(n)
        res = []
        l = len(nums)
        for i in range(l):
            if nums[l-i-1] % 2 != 0:
                res.insert(0,nums[-i-1])
            if nums[i] % 2 == 0:
                res.append(nums[i])
        return res

class Solution:
    def exchange(self, nums: List[int]) -> List[int]:
        #4.新建列表,先插入奇數,后插入偶數,時間復雜度O(n),空間復雜度O(n)
        res = []
        l = len(nums)
        for i in range(l):
            if nums[i] % 2 != 0:
                res.append(nums[i])
        for i in range(l):
            if nums[i] % 2 == 0:
                res.append(nums[i])
        return res

面試題22. 鏈表中倒數第k個節點

輸入一個鏈表,輸出該鏈表中倒數第k個節點。為了符合大多數人的習慣,本題從1開始計數,即鏈表的尾節點是倒數第1個節點。例如,一個鏈表有6個節點,從頭節點開始,它們的值依次是1、2、3、4、5、6。這個鏈表的倒數第3個節點是值為4的節點。

思路:利用前后指針,設置fast比slow快k個結點,然后遍歷鏈表,當fast過了最后一個結點后,slow就在倒數第k個結點

  時間復雜度O(n),空間復雜度O(1)

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

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        if head == None or k == 0:
            return None
        fast, slow = head, head
        for i in range(k):
            if fast:
                fast = fast.next
            else:
                return None
        while fast:
            fast = fast.next
            slow = slow.next
        return slow

面試題24. 反轉鏈表

定義一個函數,輸入一個鏈表的頭節點,反轉該鏈表並輸出反轉后鏈表的頭節點。

思路:利用前后指針,當前結點的尾結點和前一個結點替換,遍歷鏈表

  時間復雜度O(n),空間復雜度O(1)

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

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        cur = head
        pre = None
        while cur:
            cur.next, pre, cur = pre, cur, cur.next
        return pre

面試題25. 合並兩個排序的鏈表

輸入兩個遞增排序的鏈表,合並這兩個鏈表並使新鏈表中的節點仍然是遞增排序的。

思路:利用雙節點和虛擬結點dummy node,遍歷鏈表

  時間復雜度O(m+n),空間復雜度O(1)

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

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        res = dummy = ListNode(None)
        while l1 and l2:
            if l1.val <= l2.val:
                dummy.next = l1
                dummy = dummy.next
                l1 = l1.next
            else:
                dummy.next = l2
                dummy = dummy.next
                l2 = l2.next
        if l1:
            dummy.next = l1
        if l2:
            dummy.next = l2
        return res.next

面試題26. 樹的子結構

輸入兩棵二叉樹A和B,判斷B是不是A的子結構。(約定空樹不是任意一個樹的子結構)。B是A的子結構, 即 A中有出現和B相同的結構和節點值。

思路:利用遞歸(深度優先搜索),分為兩步:第一步在樹A中找到和B的根結點的值一樣的結點R,第二步再判斷樹A中以R為根節點的子樹是不是包含和樹B一樣的結構。

  時間復雜度O(n),空間復雜度O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
        if not A or not B:
            return False
        return self.isSubStructure(A.left, B) or self.isSubStructure(A.right, B) or self.is_subtree(A, B)
    def is_subtree(self, tree1, tree2):
        if not tree2:
            return True
        if not tree1 or tree1.val != tree2.val:
            return False
        return self.is_subtree(tree1.left, tree2.left) and self.is_subtree(tree1.right, tree2.right)

面試題27. 二叉樹的鏡像

請完成一個函數,輸入一個二叉樹,該函數輸出它的鏡像。

思路:利用遞歸(深度優先搜索),交換所有的非葉結點的左右子結點

  時間復雜度O(n),空間復雜度O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        if root == None:
            return None
        root.left, root.right = root.right, root.left
        self.mirrorTree(root.left)
        self.mirrorTree(root.right)
        return root

面試題28. 對稱的二叉樹

請實現一個函數,用來判斷一棵二叉樹是不是對稱的。如果一棵二叉樹和它的鏡像一樣,那么它是對稱的。

思路:利用遞歸(深度優先搜索),使用前序遍歷和前序遍歷的對稱遍歷(中右左)

  時間復雜度O(n),空間復雜度O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        return self.getisSymmetric(root,root)
        
    def getisSymmetric(self,p,q):
        if p==None and q==None:
            return True
        if p!=None and q!=None:
            return p.val==q.val and self.getisSymmetric(p.left,q.right) and self.getisSymmetric(p.right,q.left)
        return False

面試題29. 順時針打印矩陣

輸入一個矩陣,按照從外向里以順時針的順序依次打印出每一個數字。

思路:利用zip(*zipped)、逆序遍歷[::-1],取首行,去除首行后,對矩陣翻轉來創建新的矩陣,直到新矩陣為[],退出並將取到的數據返回。

  時間復雜度O(n),空間復雜度O(n)

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        res=[]
        while matrix:
            res.extend(matrix.pop(0))
            matrix=list(zip(*matrix))[::-1]
        return res

面試題30. 包含min函數的棧

定義棧的數據結構,請在該類型中實現一個能夠得到棧的最小元素的 min 函數在該棧中,調用 min、push 及 pop 的時間復雜度都是 O(1)。

思路:使用兩個stack,一個為數據棧,另一個為輔助棧。數據棧用於存儲所有數據,輔助棧用於存儲最小值。

  時間復雜度O(1),空間復雜度O(n)

class MinStack:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.Data = []
        self.Min = []

    def push(self, x: int) -> None:
        if len(self.Min) == 0 or self.Min[-1] >= x:
            self.Min.append(x)
        self.Data.append(x)

    def pop(self) -> None:
        if self.Data[-1] == self.Min[-1]:
            self.Min.pop()
        self.Data.pop()

    def top(self) -> int:
        return self.Data[-1]

    def min(self) -> int:
        return self.Min[-1]


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

面試題31. 棧的壓入、彈出序列

輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否為該棧的彈出順序。假設壓入棧的所有數字均不相等。例如,序列 {1,2,3,4,5} 是某棧的壓棧序列,序列 {4,5,3,2,1} 是該壓棧序列對應的一個彈出序列,但 {4,3,5,1,2} 就不可能是該壓棧序列的彈出序列。

思路:借用一個輔助的棧,按照壓棧順序[1,2,3,4,5]壓入,直到棧頂元素是出棧順序[4,5,3,2,1]的第一個元素(4),開始出棧,每出棧一個元素,則將出棧順序向后移動一位,直到不相等,循環等壓棧順序遍歷完成。

  時間復雜度O(n2),空間復雜度O(n)

class Solution:
    def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
        stackData = []
        for i in pushed:
            stackData.append(i)
            while len(stackData) and stackData[-1] == popped[0]:
                stackData.pop()
                popped.pop(0)
        if len(stackData):
            return False
        return True

面試題32 - I. 從上到下打印二叉樹

從上到下打印出二叉樹的每個節點,同一層的節點按照從左到右的順序打印。

思路:1. 利用遞歸(深度優先搜索),遍歷到新層,增加一層數組。

   2. 利用隊列(廣度優先搜索),每一次打印一個結點的時候,如果該結點有子結點,則把該結點的子結點放到一個隊列的末尾。接下來到隊列的頭部取出最早進入隊列的結點,重復前面的打印操作,直至隊列中所有的結點都打印出來為止。

  時間復雜度O(n),空間復雜度O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root: TreeNode) -> List[int]:
        def helper(node, level):
            if not node:
                return
            else:
                sol[level-1].append(node.val)
                if len(sol) == level:  # 遍歷到新層時,只有最左邊的結點使得等式成立
                    sol.append([])
                helper(node.left, level+1)
                helper(node.right, level+1)
        sol = [[]]
        helper(root, 1)
        return sum(sol,[])

class Solution:
    def levelOrder(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        result = []
        queue = [root]
        while len(queue):
            node = queue.pop(0)
            result.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        return result

面試題32 - II. 從上到下打印二叉樹 II

從上到下按層打印二叉樹,同一層的節點按從左到右的順序打印,每一層打印到一行。

思路:1. 利用遞歸(深度優先搜索),遍歷到新層,增加一層數組。

   2. 利用隊列(廣度優先搜索),每一次打印一個結點的時候,如果該結點有子結點,則把該結點的子結點放到一個隊列的末尾。接下來到隊列的頭部取出最早進入隊列的結點,重復前面的打印操作,直至隊列中所有的結點都打印出來為止。

  時間復雜度O(n),空間復雜度O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        def helper(node, level):
            if not node:
                return 
            else:
                sol[level-1].append(node.val)
                if len(sol) == level:  # 遍歷到新層時,只有最左邊的結點使得等式成立
                    sol.append([])
                helper(node.left, level+1)
                helper(node.right, level+1)
        sol = [[]]
        helper(root, 1)
        sol.pop()
        return sol

class Solution:
    def levelOrder(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        result = []
        queue = [root]
        while len(queue):
            out = []
            temp = []
            for node in queue:
                temp.append(node.val)
                if node.left:
                    out.append(node.left)
                if node.right:
                    out.append(node.right)
            result.append(temp)
            queue = out
        return result

面試題32 - III. 從上到下打印二叉樹 III

請實現一個函數按照之字形順序打印二叉樹,即第一行按照從左到右的順序打印,第二層按照從右到左的順序打印,第三行再按照從左到右的順序打印,其他行以此類推。

思路:1. 利用遞歸(深度優先搜索),2. 利用隊列(廣度優先搜索)。

  時間復雜度O(n),空間復雜度O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        def helper(node, level):
            if not node:
                return
            else:
                sol[level-1].append(node.val)
                if len(sol) == level:  # 遍歷到新層時,只有最左邊的結點使得等式成立
                    sol.append([])
                helper(node.left, level+1)
                helper(node.right, level+1)
        sol = [[]]
        helper(root, 1)
        for i in range(1,len(sol)-1,2):
            sol[i]=sol[i][::-1]
        return sol[:-1]

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        result = []
        queue = [root]
        level = 0
        while queue:
            curLayerValues = []
            level += 1
            for _ in range(len(queue)):
                node = queue.pop(0)
                curLayerValues.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            result.append(curLayerValues[::-1]) if (level % 2 == 0) else result.append(curLayerValues)
        return result

面試題33. 二叉搜索樹的后序遍歷序列

輸入一個整數數組,判斷該數組是不是某二叉搜索樹的后序遍歷結果。如果是則返回 true,否則返回 false。假設輸入的數組的任意兩個數字都互不相同。

思路:利用遞歸(深度優先搜索),根據后序遍歷結果的最后一個數字是根結點的值,二叉搜索樹左子樹上所有結點的值均小於它的根結點的值,右子樹上所有結點的值均大於它的根結點的值,先判斷數組的左子樹和右子樹的位置,然后再判斷左子樹、右子樹是不是二叉搜索樹。

  時間復雜度O(n2),空間復雜度O(n)

class Solution:
    def verifyPostorder(self, postorder: List[int]) -> bool:
        if len(postorder) == 0:
            return True
        root = postorder[-1]
        #左右子樹的分界pos
        pos = 0
        while postorder[pos] < root:
            pos += 1
        #在二叉搜索書中右子樹的結點大於根結點
        for i in range(pos, len(postorder)-1):
            if postorder[i] < root:
                return False
        # 判斷 左子樹是否為二叉樹
        left=True
        if  pos>0:
            left=self.verifyPostorder(postorder[0:pos])
        # 判斷 右子樹是否為二叉樹
        right=True
        if pos<len(postorder)-1:
            right=self.verifyPostorder(postorder[pos:-1])
        return left and right

面試題34. 二叉樹中和為某一值的路徑

輸入一棵二叉樹和一個整數,打印出二叉樹中節點值的和為輸入整數的所有路徑。從樹的根節點開始往下一直到葉節點所經過的節點形成一條路徑。

思路:利用遞歸(深度優先搜索),使用前序遍歷,先判斷當前root是否同時滿足條件,然后依次遍歷左右子樹。

  時間復雜度O(n2),空間復雜度O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
        if not root:
            return []
        if not root.left and not root.right and sum == root.val:
            return [[root.val]]
        res = []
        left = self.pathSum(root.left, sum-root.val)
        right = self.pathSum(root.right, sum-root.val)
        for i in left+right:
            res.append([root.val]+i)
        return res

面試題35. 復雜鏈表的復制

請實現 copyRandomList 函數,復制一個復雜鏈表。在復雜鏈表中,每個節點除了有一個 next 指針指向下一個節點,還有一個 random 指針指向鏈表中的任意節點或者 null。

思路:利用字典,字典的key是原復雜鏈表結點,value是復制后新鏈表結點的地址。先復制復雜指針的label和next,復制label和next的同時建立一個hash表來存放新舊復雜鏈表的對應關系,然后再查找random並更新,因為有字典,所以后續只需一步就能找到random

  時間復雜度O(n),空間復雜度O(n)

"""
# Definition for a Node.
class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = int(x)
        self.next = next
        self.random = random
"""
class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        if head == None:
            return None
        res = res_tail = Node(0)
        tem = head
        dict_tem = {}
        while head:
            res_tail.next = Node(head.val)
            res_tail = res_tail.next
            dict_tem[head] = res_tail
            head = head.next
        res = res.next
        res_tail = res  
        head = tem
        while head:
            if head.random:
                res.random = dict_tem[head.random]
            head, res = head.next, res.next
        res = res_tail
        return res

面試題36. 二叉搜索樹與雙向鏈表

輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的循環雙向鏈表。要求不能創建任何新的節點,只能調整樹中節點指針的指向。

思路:利用遞歸(深度優先搜索),使用中序遍歷(遞增排列),將所有的節點保存到一個列表中。對這個list進行遍歷,每個節點的left設為上一個節點,right設為i+1-n節點。

  時間復雜度O(n),空間復雜度O(n)

"""
# Definition for a Node.
class Node:
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
"""
class Solution:
    def treeToDoublyList(self, root: 'Node') -> 'Node':
        if not root:
            return
        self.arr = []        
        self.inorderTraversal(root)
        n = len(self.arr)        
        for i,v in enumerate(self.arr):
            v.left, v.right = self.arr[i-1], self.arr[i+1-n]       
        return self.arr[0]
    
    def inorderTraversal(self, Root):
        if Root==None:
            return 
        self.inorderTraversal(Root.left)
        self.arr.append(Root)
        self.inorderTraversal(Root.right)

面試題37. 序列化二叉樹

請實現兩個函數,分別用來序列化和反序列化二叉樹。

思路:層次遍歷來序列化和反序列化

  時間復雜度O(n),空間復雜度O(n)

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Codec:

    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        s = ""
        queue = []
        queue.append(root)
        while queue:
            root = queue.pop(0)
            if root:
                s += str(root.val)
                queue.append(root.left)
                queue.append(root.right)
            else:
                s += "n"
            s += " "        
        return s

    def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        tree = data.split()
        print(tree)
        if tree[0] == "n":
            return None
        queue = []
        root = TreeNode(int(tree[0]))
        queue.append(root)
        i = 1
        while queue:
            cur = queue.pop(0)
            if cur == None:
                continue
            cur.left = TreeNode(int(tree[i])) if tree[i] != "n" else None
            cur.right = TreeNode(int(tree[i + 1])) if tree[i + 1] != "n" else None
            i += 2
            queue.append(cur.left)
            queue.append(cur.right)
        return root
        

面試題38. 字符串的排列

輸入一個字符串,打印出該字符串中字符的所有排列。你可以以任意順序返回這個字符串數組,但里面不能有重復元素。

思路:利用遞歸,將排列后的字符分為兩部分:第一個字符,以及這個字符之后的所有字符。

  時間復雜度O(n2),空間復雜度O(n)

class Solution:
    def permutation(self, s: str) -> List[str]:
        if len(s)<=1:
            return s.split()
        answer = []
        for i, st in enumerate(s):
            s1 = s[:i]+s[i+1:]
            for y in self.permutation(s1):
                if st+y not in answer:
                    answer.append(st+y)   
        return answer

面試題39. 數組中出現次數超過一半的數字

數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。你可以假設數組是非空的,並且給定的數組總是存在多數元素。

思路:利用字典。時間復雜度O(n),空間復雜度O(n)

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        dic={}
        for num in nums:
            if num not in dic:
                dic[num]=1
            else:
                dic[num]+=1
            if dic[num] > len(nums)/2:
                return num
        return 0

面試題40. 最小的k個數

輸入整數數組 arr ,找出其中最小的 k 個數。例如,輸入4、5、1、6、2、7、3、8這8個數字,則最小的4個數字是1、2、3、4。

思路:創建一個大小為k的數組a來存儲最小的k個數字,遍歷arr,將數組a中最大的值替換。

  時間復雜度O(n2),空間復雜度O(n)

class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        if k > len(arr) or k == 0:
            return []
        a=arr[:k]
        temp_max=max(a)
        for i in arr[k:]:
            if i < temp_max:
                a.remove(temp_max)
                a.append(i)
                temp_max=max(a)
        return sorted(a)

面試題41. 數據流中的中位數

如何得到一個數據流中的中位數?如果從數據流中讀出奇數個數值,那么中位數就是所有數值排序之后位於中間的數值。如果從數據流中讀出偶數個數值,那么中位數就是所有數值排序之后中間兩個數的平均值。

思路:利用最大最小堆(根結點的value是堆里所有結點中value最大/小者),將數據分為兩部分,位於左邊最大堆的最大數據(根)比右邊最小堆的最小數據(根)要小,保證兩個堆中數據的數目之差不能超過1,根據左邊最大的數及右邊最小的數得到中位數。利用小頂堆實現大頂堆:添加元素進去時,取反;取出元素時,也取反。

  獲取中位數的時間復雜度為O(1),插入一個數據的時間復雜度為O(log(n))

from heapq import *

class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.max_h = []
        self.min_h = []

    def addNum(self, num: int) -> None:
        #將num放入小根堆,並彈出小根堆的最小值,取反,放入大根堆
        heappush(self.max_h, -heappushpop(self.min_h, num))
        #彈出大根堆中最大的值,取反,即最小的值,放入小根堆,使得最小最大堆元素相差最多為1,最小堆元素個數大於 等於最大堆元素個數
        if len(self.min_h) < len(self.max_h):
            heappush(self.min_h,-heappop(self.max_h))

    def findMedian(self) -> float:
        if len(self.min_h) != len(self.max_h):
            return self.min_h[0]*1.
        else:
            return (-self.max_h[0]+self.min_h[0])/2.


# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()

面試題42. 連續子數組的最大和

輸入一個整型數組,數組里有正數也有負數。數組中的一個或連續多個整數組成一個子數組。求所有子數組的和的最大值。要求時間復雜度為O(n)。

思路:動態規划,狀態轉移方程是 array[i] = max(array[i-1], 0)+array[i]。

  時間復雜度O(n),空間復雜度O(1)

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        if nums == []:
            return 0
        for i in range(1, len(nums)):
            nums[i] = max(nums[i-1], 0)+nums[i]
        return max(nums)

面試題43. 1~n整數中1出現的次數

輸入一個整數 n ,求1~n這n個整數的十進制表示中1出現的次數。例如,輸入12,1~12這些整數中包含1 的數字有1、10、11和12,1一共出現了5次。

思路:利用遞歸,將一個數字拆分為最高位first和其右邊last,計算數字的量級power,分first==1和first>1兩種情況:

  •         first==1,比如1234,其中1000-1234貢獻分兩部分,這些數的最高位貢獻last+1個1,占有最高位的數字還貢獻self.countDigitOne(last)個1;1-999的貢獻self.countDigitOne(power-1)
  •         first>1,比如3234,1000-1999最高位貢獻power個1,占有最高位的數字3000-3234貢獻self.countDigitOne(last)個1;1-999,1000-1999,2000-2999的貢獻first*self.countDigitOne(power-1)

  時間復雜度O(n),空間復雜度O(n)

class Solution:
    def countDigitOne(self, n: int) -> int:
        if n <= 0: return 0
        if n < 10: return 1
        first = int(str(n)[0])
        last = int(str(n)[1:])
        power = 10**(len(str(n))-1)
        if first == 1:
            return last+1 + self.countDigitOne(power-1) + self.countDigitOne(last)
        else:
            return power + first*self.countDigitOne(power-1) + self.countDigitOne(last)

面試題44. 數字序列中某一位的數字

數字以0123456789101112131415…的格式序列化到一個字符序列中。在這個序列中,第5位(從下標0開始計數)是5,第13位是1,第19位是4,等等。

請寫一個函數,求任意第n位對應的數字。

時間復雜度O(n),空間復雜度O(n)

class Solution:
    def findNthDigit(self, n: int) -> int:
        if n < 10:
            return n
        i = 0
        while n > numbers(i):
            n -= numbers(i)
            i += 1
        num_index = n//i
        index = n%i
        num = 10**(i-1) + num_index
        return int(str(num)[index])

def numbers(n):
    if n == 0:
        return 1
    else:
        return 10**(n-1)*9*n

面試題45. 把數組排成最小的數

輸入一個正整數數組,把數組里所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。

思路:利用lambda表達式、cmp(x,y)、sorted(),將數字轉換成字符串再進行兩兩比較,若ab > ba 則 a 大於 b,然后排序。

  時間復雜度O(nlogn),空間復雜度O(n)

class Solution:
    def minNumber(self, nums: List[int]) -> str:
        from functools import cmp_to_key
        
        def compare(a,b) : 
            return 1 if a+b > b+a else -1
        
        nums = sorted( [str(i) for i in nums] , key=cmp_to_key(compare)) 
        return "".join(nums)

面試題46. 把數字翻譯成字符串

給定一個數字,我們按照如下規則把它翻譯為字符串:0 翻譯成 “a” ,1 翻譯成 “b”,……,11 翻譯成 “l”,……,25 翻譯成 “z”。一個數字可能有多個翻譯。請編程實現一個函數,用來計算一個數字有多少種不同的翻譯方法。

思路:利用動態規划,狀態轉移方程為f(n)=f(n-1)+f(n-2)

  時間復雜度O(n),空間復雜度O(n)

class Solution:
    def translateNum(self, num: int) -> int:
        num = str(num)
        L = len(num)
        dp = [1] * (L + 1)
        for i in range(2, L + 1):
            dp[i] = dp[i - 1]
            if '10' <= num[i - 2] + num[i - 1] <= '25':
                dp[i] = dp[i - 1] + dp[i - 2]
        return dp[-1]

面試題47. 禮物的最大價值

在一個 m*n 的棋盤的每一格都放有一個禮物,每個禮物都有一定的價值(價值大於 0)。你可以從棋盤的左上角開始拿格子里的禮物,並每次向右或者向下移動一格、直到到達棋盤的右下角。給定一個棋盤及其上面的禮物的價值,請計算你最多能拿到多少價值的禮物?

思路:利用動態規划

  時間復雜度O(n2),空間復雜度O(n)

class Solution:
    def maxValue(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        for i in range(1, m):
            grid[i][0] += grid[i-1][0]
        for i in range(1, n):
            grid[0][i] += grid[0][i-1]
        for i in range(1, m):
            for j in range(1, n):
                grid[i][j] += max(grid[i][j-1], grid[i-1][j])
        return grid[m-1][n-1]

面試題48. 最長不含重復字符的子字符串

請從字符串中找出一個最長的不包含重復字符的子字符串,計算該最長子字符串的長度。

思路:滑動窗口,使用哈希表記錄每個字符的下一個索引,然后盡量向右移動尾指針來拓展窗口,並更新窗口的最大長度。如果尾指針指向的元素重復,則將頭指針直接移動到窗口中重復元素的右側。

  時間復雜度O(n2),空間復雜度O(n)

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        n = len(s)
        hashmap = {}
        head, res = 0, 0
        for tail in range(n):
            if s[tail] in hashmap:
                head = max(hashmap[s[tail]], head)
            hashmap[s[tail]] = tail + 1
            res = max(res, tail - head + 1)
        return res

面試題49. 丑數

我們把只包含因子 2、3 和 5 的數稱作丑數(Ugly Number)。求按從小到大的順序的第 n 個丑數。

思路:利用三指針,分別對應數組中乘因子2、3和5的下標,下一個丑數,定義為丑數數組中的數乘以因子,所得的最小值。

  時間復雜度O(n),空間復雜度O(n)

class Solution:
    def nthUglyNumber(self, n: int) -> int:
        if n == 0:
            return 0
        res = [1]
        index2 = 0
        index3 = 0
        index5 = 0
        for i in range(n-1):
            res.append(min(res[index2]*2,res[index3]*3,res[index5]*5))
            if res[-1] == res[index2]*2:
                index2 += 1
            if res[-1] == res[index3]*3:
                index3 += 1
            if res[-1] == res[index5]*5:
                index5 += 1
        return res[-1]

面試題50. 第一個只出現一次的字符

在字符串 s 中找出第一個只出現一次的字符。如果沒有,返回一個單空格。

思路:利用字典,遍歷字符串

  時間復雜度O(n),空間復雜度O(n) 

class Solution:
    def firstUniqChar(self, s: str) -> str:
        dict = {}
        for i in range(len(s)):
            if s[i] not in dict:
                dict[s[i]] = 1
            else:
                dict[s[i]] += 1
        for i in range(len(s)):
            if dict[s[i]] == 1:
                return s[i]
        return " "

面試題51. 數組中的逆序對

在數組中的兩個數字,如果前面一個數字大於后面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數。

思路:分治思想,歸並排序,逆序對的總數 =左邊數組中的逆序對的數量 + 右邊數組中逆序對的數量 + 左右結合成新的順序數組時中出現的逆序對的數量。每次在合並前,先遞歸地處理左半段、右半段,則左、右半段有序,且左右半段的逆序對數可得到,再計算左右半段合並時逆序對的個數。

  時間復雜度O(nlogn),空間復雜度O(n)

class Solution:
    def reversePairs(self, nums: List[int]) -> int:
        data = nums
        if not data:
            return 0
        temp = [i for i in data]
        return self.mergeSort(temp, data, 0, len(data)-1)
       
    def mergeSort(self, temp, data, low, high):
        if low >= high:
            temp[low] = data[low]
            return 0
        mid = (low + high) // 2
        left = self.mergeSort(data, temp, low, mid)
        right = self.mergeSort(data, temp, mid+1, high)
        count = 0
        i = low
        j = mid+1
        index = low
        while i <= mid and j <= high:
            if data[i] <= data[j]:
                temp[index] = data[i]
                i += 1
            else:
                temp[index] = data[j]
                count += mid-i+1
                j += 1
            index += 1
        while i <= mid:
            temp[index] = data[i]
            i += 1
            index += 1
        while j <= high:
            temp[index] = data[j]
            j += 1
            index += 1
        return count + left + right

面試題52. 兩個鏈表的第一個公共節點

輸入兩個鏈表,找出它們的第一個公共節點。

思路:拼接兩個鏈表,pHead1+pHead2與pHead2+pHead1,遍歷鏈表

  時間復雜度O(m+n),空間復雜度O(1)

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

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        p = headA
        q = headB
        while p != q:
            if not p:
                p=headB
            else:
                p=p.next
            if not q:
                q=headA
            else:
                q=q.next
        return p

面試題53 - I. 在排序數組中查找數字 I

統計一個數字在排序數組中出現的次數。

思路:利用二分法分別找到數字在數組中出現的第一個位置和最后一個位置,Bisearch(data, k+0.5)找到最后一個位置+1,Bisearch(data, k-0.5)找到第一個位置

  時間復雜度O(logn),空間復雜度O(n)

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        def Bisearch(data, k):
            low = 0
            high = len(data)-1
            while low <= high:
                mid = (low+high)//2
                if data[mid] > k:
                    high = mid - 1
                elif data[mid] < k:
                    low = mid + 1
            return low
        return Bisearch(nums, target+0.5) - Bisearch(nums,target-0.5)

面試題53 - II. 0~n-1中缺失的數字

一個長度為n-1的遞增排序數組中的所有數字都是唯一的,並且每個數字都在范圍0~n-1之內。在范圍0~n-1內的n個數字中有且只有一個數字不在該數組中,請找出這個數字。

思路:利用二分法找亂序的一側

   時間復雜度O(logn),空間復雜度O(n)

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        left, right = 0, len(nums)
        while left < right:
            mid = left+(right-left)//2
            if nums[mid]==mid: 
                left = mid+1
            else: 
                right = mid
        return(left)

面試題54. 二叉搜索樹的第k大節點

給定一棵二叉搜索樹,請找出其中第k大的節點。

思路:利用遞歸(深度優先搜索),使用中序遍歷

  時間復雜度O(n),空間復雜度O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:
        self.res=[]
        self.dfs(root)
        return self.res[-k]  
    def dfs(self,root):
        if not root:return
        self.dfs(root.left)
        self.res.append(root.val)
        self.dfs(root.right)

面試題55 - I. 二叉樹的深度

輸入一棵二叉樹的根節點,求該樹的深度。從根節點到葉節點依次經過的節點(含根、葉節點)形成樹的一條路徑,最長路徑的長度為樹的深度。

思路:利用遞歸(深度優先搜索)

  時間復雜度O(n),空間復雜度O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if root is None:
            return 0
        return max(self.maxDepth(root.left), self.maxDepth(root.right))+1

面試題55 - II. 平衡二叉樹

輸入一棵二叉樹的根節點,判斷該樹是不是平衡二叉樹。如果某二叉樹中任意節點的左右子樹的深度相差不超過1,那么它就是一棵平衡二叉樹。

思路:利用遞歸(深度優先搜索),使用后序遍歷,在遍歷到一個結點之前已經遍歷了它的左右子樹,可以一邊遍歷一邊判斷每個結點是不是平衡的。

  時間復雜度O(n),空間復雜度O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        return self.dfs(root) != -1
    def dfs(self, pRoot):
        if pRoot is None:
            return 0
        left = self.dfs(pRoot.left)
        right = self.dfs(pRoot.right)
        if left == -1 or right == -1 or abs(left - right) > 1:
            return -1
        return max(left, right) + 1

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM