1. 程式人生 > 實用技巧 >第12周Leetcode記錄

第12周Leetcode記錄

12.1 56.第N個數字

在無限的整數序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...中找到第 n 個數字。

輸入:
11

輸出:
0

說明:
第11個數字在序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... 裡是0,它是10的一部分。

思路

暴力破解

我的解

class Solution:
    @classmethod
    def findNthDigit(self, n: int) -> int:
        li = []
        for i in range(1,n+1):
            li.append(str(i))
        str_li = ''.join(li)
        return str_li[n-1]

最優解

class Solution:
    def findNthDigit(self, n: int) -> int:
        # 首先判斷target是幾位數,用digits表示
        base = 9
        digits = 1
        while n - base * digits > 0:
            n -= base * digits
            base *= 10
            digits += 1
        # 計算target的值
        idx = n % digits  # 注意由於上面的計算,n現在表示digits位數的第n個數字
        if idx == 0: 
            idx = digits
        number = 1
        for i in range(1,digits):
            number *= 10
        if idx == digits:
            number += n // digits - 1
        else:
            number += n // digits
        # 找到target中對應的數字
        for i in range(idx,digits):
            number //= 10
        return number % 10

最優解總結

比如輸入的 n 是 365:

經過第一步計算我們可以得到第 365 個數字表示的數是三位數,n=365-9-90\times2=176n=365−9−90×2=176,digtis = 3。這時 n=176n=176 表示目標數字是三位數中的第 176176 個數字。

我們設目標數字所在的數為 number,計算得到 number=100+176/3=158number=100+176/3=158,idx 是目標數字在 number 中的索引,如果 idx = 0,表示目標數字是 number - 1 中的最後一個數字。(感謝@1m188 更正為 number-1)

根據步驟2,我們可以計算得到 idx = n % digits = 176 % 3 = 2,說明目標數字應該是 number = 158 中的第二個數字,即輸出為 5。

12.2 57. 分式簡化

輸入的cont代表連分數的係數(cont[0]代表上圖的a0,以此類推)。返回一個長度為2的陣列[n, m],使得連分數的值等於n / m,且n, m最大公約數為1。

輸入:cont = [3, 2, 0, 2]
輸出:[13, 4]
解釋:原連分數等價於3 + (1 / (2 + (1 / (0 + 1 / 2))))。注意[26, 8], [-13, -4]都不是正確答案。

輸入:cont = [0, 0, 3]
輸出:[3, 1]
解釋:如果答案是整數,令分母為1即可。

最優解

class Solution:
    def fraction(self, cont: List[int]) -> List[int]:
        numerator = 1 #分子
        denominator = cont[-1] #分母
        for i in range(len(cont)-1,0,-1):
            numerator,denominator = denominator,numerator
            denominator = cont[i-1]*numerator+denominator
        return [denominator,numerator]

總結

通分即可,停止條件為遍歷完陣列中所有數。

12.3 58. 顏色分類

給定一個包含紅色、白色和藍色,一共 n 個元素的陣列,原地對它們進行排序,使得相同顏色的元素相鄰,並按照紅色、白色、藍色順序排列。

此題中,我們使用整數 0、 1 和 2 分別表示紅色、白色和藍色。

輸入:nums = [2,0,2,1,1,0]
輸出:[0,0,1,1,2,2]

輸入:nums = [2,0,1]
輸出:[0,1,2]

我的解

class Solution:
    @classmethod
    def sortColors(self, nums: list) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        len_li = len(nums)
        if len_li == 1:
            return nums
        # [0,p1) 0
        # [p2,n) 1

        # [p1,p2) 2   
        p_0 = 0
        i = 0
        p_1 = 0
        p_2 = len_li - 1

        while i <= p_2:
            if nums[i] == 0:
                nums[i],nums[p_1] = nums[p_1],nums[i]
                i += 1
                p_1 += 1
            elif nums[i] == 1:
                i += 1
            else:
                nums[i],nums[p_2] = nums[p_2],nums[i]
                # i += 1
                p_2 -= 1

總結

荷蘭旗方法,[0,p1) 最小,[p1,p2)中間,[p2,n]最大,i為什麼要小於等於p2作為終止條件,因為p2也要做一次判斷,這樣一次遍歷可以對整個陣列進行排序。

12.4 59. 陣列中第k個最大元素

在未排序的陣列中找到第 k 個最大的元素。請注意,你需要找的是陣列排序後的第 k 個最大的元素,而不是第 k 個不同的元素。

輸入: [3,2,1,5,6,4] 和 k = 2
輸出: 5

輸入: [3,2,3,1,2,4,5,5,6] 和 k = 4
輸出: 4

思路

手寫快速排序。

最優解總結

快速排序:每次快速排序都會有個pivot索引,跟k進行比較,另外一部分不用排序。

堆排序:大根堆。掌握大根堆的建立,刪除,瞭解大根堆的建立,刪除,調整的邏輯

最優解

class Solution {
    public int findKthLargest(int[] nums, int k) {
        int heapSize = nums.length;
        buildMaxHeap(nums, heapSize);
        for (int i = nums.length - 1; i >= nums.length - k + 1; --i) {
            swap(nums, 0, i);
            --heapSize;
            maxHeapify(nums, 0, heapSize);
        }
        return nums[0];
    }

    public void buildMaxHeap(int[] a, int heapSize) {
        for (int i = heapSize / 2; i >= 0; --i) {
            maxHeapify(a, i, heapSize);
        } 
    }

    public void maxHeapify(int[] a, int i, int heapSize) {
        int l = i * 2 + 1, r = i * 2 + 2, largest = i;
        if (l < heapSize && a[l] > a[largest]) {
            largest = l;
        } 
        if (r < heapSize && a[r] > a[largest]) {
            largest = r;
        }
        if (largest != i) {
            swap(a, i, largest);
            maxHeapify(a, largest, heapSize);
        }
    }

    public void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

12.5 60.合併二叉樹

給定兩個二叉樹,想象當你將它們中的一個覆蓋到另一個上時,兩個二叉樹的一些節點便會重疊。

你需要將他們合併為一個新的二叉樹。合併的規則是如果兩個節點重疊,那麼將他們的值相加作為節點合併後的新值,否則不為 NULL 的節點將直接作為新二叉樹的節點。

輸入: 
	Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7                  
輸出: 
合併後的樹:
	     3
	    / \
	   4   5
	  / \   \ 
	 5   4   7

最優解

  • DFS

    class Solution:
        def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
            if not t1:
                return t2
            if not t2:
                return t1
            
            merged = TreeNode(t1.val + t2.val)
            merged.left = self.mergeTrees(t1.left, t2.left)
            merged.right = self.mergeTrees(t1.right, t2.right)
            return merged
    
  • BFS

    class Solution:
        def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
            if not t1:
                return t2
            if not t2:
                return t1
            
            merged = TreeNode(t1.val + t2.val)
            queue = collections.deque([merged])
            queue1 = collections.deque([t1])
            queue2 = collections.deque([t2])
    
            while queue1 and queue2:
                node = queue.popleft()
                node1 = queue1.popleft()
                node2 = queue2.popleft()
                left1, right1 = node1.left, node1.right
                left2, right2 = node2.left, node2.right
                if left1 or left2:
                    if left1 and left2:
                        left = TreeNode(left1.val + left2.val)
                        node.left = left
                        queue.append(left)
                        queue1.append(left1)
                        queue2.append(left2)
                    elif left1:
                        node.left = left1
                    elif left2:
                        node.left = left2
                if right1 or right2:
                    if right1 and right2:
                        right = TreeNode(right1.val + right2.val)
                        node.right = right
                        queue.append(right)
                        queue1.append(right1)
                        queue2.append(right2)
                    elif right1:
                        node.right = right1
                    elif right2:
                        node.right = right2
            
            return merged
    

總結

困在了null和非null的合併,其實直接賦值就可以了,停止遍歷,不需要考慮null子節點的情況。