今天的几道题主要是涉及到思维上的一些小技巧。比如运用田忌赛马的贪心思维,以及单调栈的每次新元素入栈后,栈内的元素都保持有序(单增或者单减)
一、优势洗牌 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;
};