【算法训练】田忌赛马思维及单调栈相关

今天的几道题主要是涉及到思维上的一些小技巧。比如运用田忌赛马的贪心思维,以及单调栈的每次新元素入栈后,栈内的元素都保持有序(单增或者单减)

一、优势洗牌 870

在这里插入图片描述

1、分析

核心思维就是田忌赛马的贪心,依次用nums1中剩余的最好的去和nums2剩余最好的比,如果比得过就比,如果比不过就用nums1中最差的那个去比。
由于nums2的索引位置不能变,所以我们用大根堆来存放nums的元素以及对应的索引。
对nums1进行一次从小到大排序,然后用left,right指针去操作当前“出战”的元素。
具体看代码及注释

2、代码

class Solution:
    def advantageCount(self, nums1: List[int], nums2: List[int]) -> List[int]:
        hq = []
        for i,num in enumerate(nums2): #构建大根堆,注意到默认是小根堆。保证每次拿出来的都是nums2中的最大值
            heapq.heappush(hq,(-num,i))
        nums1.sort() #对nums1进行升序排列方便给后续比较
        left,right = 0,len(nums1)-1 
        res = [0]*len(nums1) #用于存放最后的结果
        while hq: #当堆为空时说明已经安排完了
            max_num,idx = heappop(hq) #每次都拿出nums2中当前最大的元素的值以及索引
            max_num = -max_num
            if max_num<nums1[right]: #田忌赛马的比较模式:如果比得过就比,然后移动right指针
                res[idx] = nums1[right]
                right -= 1
            else: #如果比不过就拿nums1中最差的值来填补,移动keft指针
                res[idx] = nums1[left]
                left += 1
        return res

二、下一个更大元素I 496

在这里插入图片描述

1、分析

我们首先拿到nums2中每个元素下一个最大元素的值用字典存放(用元素值作为key-因为题目中提示了数组中的所有整数互不相同,以该元素的下一个更大元素作为value),然后再遍历nums1中的元素,拿到相关元素对应下一个更大元素,依次存放到结果数组中即可。

具体见代码及注释。

2、代码

class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        dic = {}
        stack = []
        for i in range(len(nums2)-1,-1,-1): #逆序去入栈和比较
            num = nums2[i]
            while stack and stack[-1]<=num: #如果当前栈不为空,且栈中存在小于当前元素的值,则让这些值出栈
                stack.pop()
            dic[num] = stack[-1] if stack else -1 #如果栈为空证明没有比当前值还大的值所以赋值-1,若栈不为空那么当前栈顶值就是所需要的
            stack.append(num) #将当前元素加入栈中
        return [dic[num] for num in nums1] #依次遍历nums1中元素,取出相关结果放入结果数组即可

JS

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var nextGreaterElement = function(nums1, nums2) {
    let dic = {},stack = [];
    
    for(let i=nums2.length-1;i>=0;i--){
        let num = nums2[i];
        while(stack.length!=0 && stack[stack.length-1]<=num){
            stack.pop()
        }
        dic[num] = stack.length!=0 ? stack[stack.length-1] : -1;
        stack.push(num);
    }
    let res = [];
    nums1.forEach((num)=>{
         res.push(dic[num]);
    })
    return res;

};

三、下一个更大元素II 503

在这里插入图片描述

1、分析

其实总体思路是和上一题一样的,不同区别在于数组是循环的,那么可以采用数组重复一遍方法来模拟整个循环。当然我们也不需要真的复制数组重复一遍,可以采用索引取余的方式来访问。
具体见代码和注释。

2、代码

class Solution:
    def nextGreaterElements(self, nums: List[int]) -> List[int]:
        stack = []
        n = len(nums)
        res = [0]*n #用来存放结果
        for i in range(2*n-1,-1,-1): #逆序访问,注意到这里索引长度加倍了,为的是模拟重复数组
            num = nums[i%n] #用取余的方式来保证访问不越界
            while stack and stack[-1]<=num: 
                stack.pop()
            res[i%n] = stack[-1] if stack else -1
            stack.append(num)
        return res

JS

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var nextGreaterElements = function(nums) {
    let stack = [];
    let res = Array(nums.length).fill(-1)
    for(let i=2*nums.length-1;i>=0;i--){
        let num = nums[i%nums.length];
        while(stack.length!=0 && stack[stack.length-1]<=num){
            stack.pop();
        }
        res[i%nums.length] = stack.length!=0 ? stack[stack.length-1] : -1;
        stack.push(num)
    }
    return res;

};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值