1. 程式人生 > 實用技巧 >第十週LeetCode記錄

第十週LeetCode記錄

11.18 46. 拼接最大數

給定長度分別為 m 和 n 的兩個陣列,其元素由 0-9 構成,表示兩個自然數各位上的數字。現在從這兩個陣列中選出 k (k <= m + n) 個數字拼接成一個新的數,要求從同一個陣列中取出的數字保持其在原陣列中的相對順序。

求滿足該條件的最大數。結果返回一個表示該最大數的長度為 k 的陣列。

輸入:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
輸出:
[9, 8, 6, 5, 3]

輸入:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
輸出:
[6, 7, 6, 0, 4]

最優解

class Solution:
    def maxNumber(self, nums1, nums2, k):

        def pick_max(nums, k):
            stack = []
            drop = len(nums) - k
            for num in nums:
                while drop and stack and stack[-1] < num:
                    stack.pop()
                    drop -= 1
                stack.append(num)
            return stack[:k]

        def merge(A, B):
            ans = []
            while A or B:
                bigger = A if A > B else B
                ans.append(bigger[0])
                bigger.pop(0)
            return ans

        return max(merge(pick_max(nums1, i), pick_max(nums2, k-i)) for i in range(k+1) if i <= len(nums1) and k-i <= len(nums2))

總結

從2個數組共取k個數字,遍歷各種情況。分治解決

pick_max是從nums數組裡取前k位組成新的陣列,merge是將兩數組合併為最大陣列

思考一下時間複雜度和空間複雜度是多少

11.19 47. 不同字元的最小子序列

返回字串 text 中按字典序排列最小的子序列,該子序列包含 text 中所有不同字元一次。

輸入:"cdadabcc"
輸出:"adbc"

輸入:"abcd"
輸出:"abcd"

輸入:"ecbacba"
輸出:"eacb"

輸入:"leetcode"
輸出:"letcod"

思路

跟上面的題型一樣,利用棧來解

我的解

class Solution:
    def smallestSubsequence(self, s: str) -> str:
        from collections import Counter
        s_dic = Counter(s)
        stack = []
        for i in s:
            if i not in stack:
                while stack and stack[-1] >= i and s_dic[stack[-1]] > 0:
                    stack.pop()
                stack.append(i)
            s_dic[i] -= 1

        return ''.join(stack)

11.20 48. 對稱的二叉樹

請實現一個函式,用來判斷一棵二叉樹是不是對稱的。如果一棵二叉樹和它的映象一樣,那麼它是對稱的。

例如,二叉樹 [1,2,2,3,4,4,3] 是對稱的。

    1
   / \
  2   2
 / \ / \
3  4 4  3

但是下面這個 [1,2,2,null,3,null,3] 則不是映象對稱的:

    1
   / \
  2   2
   \   \
   3    3

最優解

class Solution:
    @classmethod
    def isSymmetric(self, root: TreeNode) -> bool:
        
        def recrue(Left,Right):
            if not Left and not Right:
                return True
            
            if not Left or not Right or Left.val != Right.val:
                return False

            return recrue(Left.left,Right.right) and recrue(Left.right,Right.left)

        return recrue(root.left,root.right) if root else True

總結

和29題 另一個樹的子樹同樣的方法,遞迴遍歷對稱樹的定義即可。

11.21 49.最接近原點的k個點

我們有一個由平面上的點組成的列表 points。需要從中找出 K 個距離原點 (0, 0) 最近的點。

(這裡,平面上兩點之間的距離是歐幾里德距離。)

你可以按任何順序返回答案。除了點座標的順序之外,答案確保是唯一的。

輸入:points = [[1,3],[-2,2]], K = 1
輸出:[[-2,2]]
解釋: 
(1, 3) 和原點之間的距離為 sqrt(10),
(-2, 2) 和原點之間的距離為 sqrt(8),
由於 sqrt(8) < sqrt(10),(-2, 2) 離原點更近。
我們只需要距離原點最近的 K = 1 個點,所以答案就是 [[-2,2]]。

輸入:points = [[3,3],[5,-1],[-2,4]], K = 2
輸出:[[3,3],[-2,4]]
(答案 [[-2,4],[3,3]] 也會被接受。)

思路

遍歷每個點,排序取前k。

我的解

class Solution:
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        import math
        def calc_dis(point):
            x, y = point
            return math.sqrt(x ** 2 + y ** 2)

        return sorted(points,key=lambda k:calc_dis(k))[:K]

最優解

優先佇列

我們可以使用一個優先佇列(大根堆)實時維護前 KK 個最小的距離平方。

首先我們將前 KK 個點的編號(為了方便最後直接得到答案)以及對應的距離平方放入優先佇列中,隨後從第 K+1K+1 個點開始遍歷:如果當前點的距離平方比堆頂的點的距離平方要小,就把堆頂的點彈出,再插入當前的點。當遍歷完成後,所有在優先佇列中的點就是前 KK 個距離最小的點。

class Solution:
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        q = [(-x ** 2 - y ** 2, i) for i, (x, y) in enumerate(points[:K])]
        heapq.heapify(q)
        
        n = len(points)
        for i in range(K, n):
            x, y = points[i]
            dist = -x ** 2 - y ** 2
            heapq.heappushpop(q, (dist, i))
        
        ans = [points[identity] for (_, identity) in q]
        return ans

總結

取前幾個後幾個的題型,優先想到用堆處理。

11.23 50. 最大二叉樹

給定一個不含重複元素的整數陣列。一個以此陣列構建的最大二叉樹定義如下:

二叉樹的根是陣列中的最大元素。
左子樹是通過陣列中最大值左邊部分構造出的最大二叉樹。
右子樹是通過陣列中最大值右邊部分構造出的最大二叉樹。
通過給定的陣列構建最大二叉樹,並且輸出這個樹的根節點。

輸入:[3,2,1,6,0,5]
輸出:返回下面這棵樹的根節點:

      6
    /   \
   3     5
    \    / 
     2  0   
       \
        1

最優解

public class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return construct(nums, 0, nums.length);
    }
    public TreeNode construct(int[] nums, int l, int r) {
        if (l == r)
            return null;
        int max_i = max(nums, l, r);
        TreeNode root = new TreeNode(nums[max_i]);
        root.left = construct(nums, l, max_i);
        root.right = construct(nums, max_i + 1, r);
        return root;
    }
    public int max(int[] nums, int l, int r) {
        int max_i = l;
        for (int i = l; i < r; i++) {
            if (nums[max_i] < nums[i])
                max_i = i;
        }
        return max_i;
    }
}

總結

遞迴處理。每次處理最左最右最大。