• 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/

  • 相关阅读:
    内部类
    三大修饰符:static、final、abstract
    面向对象三大特性
    类和方法
    Vue-创建工程+element UI
    xshell连接虚拟机较慢问题 -----已解决
    Hbase配置
    Hive的安装配置
    Hive内容+配置
    Redis全局命令
  • 原文地址:https://www.cnblogs.com/yeshengCqupt/p/14057027.html
Copyright © 2020-2023  润新知