1. 程式人生 > 實用技巧 >leetcode 402.移掉K位數字/leetcode 5614. 找出最具競爭力的子序列(單調棧/dfs)

leetcode 402.移掉K位數字/leetcode 5614. 找出最具競爭力的子序列(單調棧/dfs)

  • 題目描述——leetcode 5614找出最具競爭力的子序列
給你一個整數陣列 nums 和一個正整數 k ,返回長度為 k 且最具 競爭力 的 nums 子序列。

陣列的子序列是從陣列中刪除一些元素(可能不刪除元素)得到的序列。

在子序列a 和子序列b 第一個不相同的位置上,如果a中的數字小於 b 中對應的數字,那麼我們稱子序列 a 比子序列 b(相同長度下)更具 競爭力 。 例如,[1,3,4] 比 [1,3,5] 更具競爭力,在第一個不相同的位置,也就是最後一個位置上,4 小於 5 。



示例 1:

輸入:nums = [3,5,2,6], k = 2
輸出:[2,6]
解釋:在所有可能的子序列集合 {[
3,5], [3,2], [3,6], [5,2], [5,6], [2,6]} 中,[2,6] 最具競爭力。 示例 2: 輸入:nums = [2,4,3,3,5,4,9,6], k = 4 輸出:[2,3,3,4]
  • 解法一:超時的dfs

為什麼一想到用dfs呢?因為最近正在練習dfs模板....覺得這道題和全排列是一個道理,只是每次排列需要在當前元素索引後面的位置搜尋元素,然後再對結果進行排序,排序後第一個肯定是我們要找的陣列(思路很垃圾,因為不會優化....)

那麼這裡套的就是dfs解決數字全排列的模板,菜雞的我只會套模板....

List<List<Integer>> res = new
LinkedList<>(); /* 主函式,輸入一組不重複的數字,返回它們的全排列 */ List<List<Integer>> permute(int[] nums) { // 記錄「路徑」 LinkedList<Integer> track = new LinkedList<>(); backtrack(nums, track); return res; } // 路徑:記錄在 track 中 // 選擇列表:nums 中不存在於 track 的那些元素 // 結束條件:nums 中的元素全都在 track 中出現
void backtrack(int[] nums, LinkedList<Integer> track) { // 觸發結束條件 if (track.size() == nums.length) { res.add(new LinkedList(track)); return; } for (int i = 0; i < nums.length; i++) { // 排除不合法的選擇 if (track.contains(nums[i])) continue; // 做選擇 track.add(nums[i]); // 進入下一層決策樹 backtrack(nums, track); // 取消選擇 track.removeLast(); } }

遍歷nums陣列,每次搜尋索引i+1後面的陣列,然後找到所有的排列,這裡vis可以用來判斷當前元素是否被用掉。

class Solution:
    def mostCompetitive(self, nums: List[int], k: int) -> List[int]:
        self.res = list()
        vis = [0] * len(nums)
        r = list()

        def dfs(nums, u, r, vis):
            # r = list
            if u == k:
                self.res.append(copy.deepcopy(r))
                return

            for i in range(0, len(nums)):
                if vis[i] == 0:
                    r.append(nums[i])
                    vis[i] = 1
                    dfs(nums[i + 1:], u + 1, r, vis[i+1:])
                    vis[i] = 0
                    r.pop()

        dfs(nums, 0, r, vis)
        self.res.sort()
        return self.res[0]

超時了,看大佬的解答,這道題正確的開啟方式其實是單調棧!不過還是期待哪位dfs大佬優化一個不超時的dfs版本。

  • 解法二:單調棧

為什麼想到用單調棧?

這裡可能要說起leetcode 402題了,移除K位數字。

我們看看這道題題目:

給定一個以字串表示的非負整數num,移除這個數中的 k 位數字,使得剩下的數字最小。

注意:

num 的長度小於 10002 且≥ k。
num 不會包含任何前導零。
示例 1 :

輸入: num = "1432219", k = 3
輸出: "1219"
解釋: 移除掉三個數字 4, 3, 和 2 形成一個新的最小的數字 1219。
示例 2 :

輸入: num = "10200", k = 1
輸出: "200"
解釋: 移掉首位的 1 剩下的數字為 200. 注意輸出不能有任何前導零。
示例 3 :

輸入: num = "10", k = 2
輸出: "0"
解釋: 從原數字移除所有的數字,剩餘為空就是0。

移除k個數字,和剩餘k個數字,都是同樣的道理。就相當於剩餘length-k個數字或者是k個數字具有單調性質,那麼我們是不是可以用一個單調棧始終維護一個單調的序列,每次被棧pop出去的就是被移除的,被棧push進去的就是需要留下的。

虛擬碼的實現是這樣的:

<!-- 單調棧虛擬碼 -->
        for (遍歷這個陣列) {
            if (棧頂元素小於等於當前比較元素) {
                入棧;
            } else {
                while (棧不為空 && 棧頂元素大於當前元素&&還有能減去的元素) {
                    棧頂元素出棧;
                    更新結果;
                }
                當前資料入棧;
            }

            while(如果還要元素需要刪除){
                棧頂元素出棧;
            }
        }

直接看leetcode 5614. 找出最具競爭力的子序列的程式碼

class Solution:
    def mostCompetitive(self, nums: List[int], k: int) -> List[int]:
        stack = list()
        count = len(nums) - k #需要被移除的數
        for i in range(0, len(nums)):
            if not stack or nums[i] >= stack[-1]:
                stack.append(nums[i]) 
            else:
                while stack and nums[i] < stack[-1] and count != 0:
                    stack.pop()
                    count -= 1
                stack.append(nums[i])
        while count != 0:
            stack.pop()
            count -= 1
        return stack

那麼leetcode 402.移掉K位數字是不是就很簡單了?

每次只要遍歷的元素大於棧頂,就入棧,小於棧頂且需要刪除元素,那麼棧頂就出棧,將此元素入棧,這樣pop掉的元素正好是刪除的個數,如果刪除的元素小於k,則繼續將棧頂的元素pop出去。

後面為啥要判斷res是不是為空,是為了通過‘100’這樣的測試用例

class Solution:
    def removeKdigits(self, num: str, k: int) -> str:
        num = [int(i) for i in num]
        stack = list()
        for i in range(0, len(num)):
            if not stack or stack[-1] < num[i]:
                stack.append(num[i])
            else:
                while stack and stack[-1] > num[i] and k != 0:
                    stack.pop()
                    k -= 1
                stack.append(num[i])
        while k != 0:
            stack.pop()
            k -= 1
        res = (''.join(str(i) for i in stack)).lstrip('0')
        if res:
            return res
        else:
            return '0'

參考連結:https://leetcode-cn.com/problems/find-the-most-competitive-subsequence/solution/js-mo-ni-yi-ge-dan-diao-di-zeng-zhan-by-akumu213/