题目:
给你一个整数数组 nums ,和一个表示限制的整数 limit,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit 。
如果不存在满足条件的子数组,则返回 0 。
算法:
方法一:双指针 + 单调队列
划定一个窗口[left, right],如何判断这个窗口内所有数绝对值之差小于limit,比较窗口内最大值和最小值是否小于等于limit即可。
问题在于left(right)右移时,我们如何更新和查询新区间的最大值和最小值?用什么数据结构呢?
答案是单调队列。
1.用两个单调队列minQ(从左到右数据非严格递增,minQ[0]保存区间最小值),maxQ(从左到右数据非严格递减,maxQ[0]保存区间最大值)。
比如nums=[1,2,5,3],maxQ依次为:[1],[2],[5],[5,3],我们不禁要问2,5入队列的时候,把1,2从maxQ挤出去是否合理?答案是合理,因为left只会往右移动,对于区间[1,2,5]的最大值来说,因为随着left的右移,1,2会先移出区间,而他们都小于5,他们移出去不影响区间最大值仍然为5,但是3是在5后面出现的,5移除区间了,3就是最大值。
所以每个数字进入maxQ的时候,把比他小的数字都可以从maxQ移出。
2.right右移时,将minQ中大于nums[right]的数全部出队列,将maxQ中小于nums[right]的数全部出队列,将nums[right]入minQ和maxQ,这样minQ,maxQ始终保存了区间nums[left, right]的最大值的递减序列和最小值的递增序列
3.left左移一个位置时,判断nums[left]是不是minQ的最小值和maxQ的最大值,如果是,出相应队列。minQ,maxQ始终保存了区间nums[left, right]的最大值的递减序列和最小值的递增序列
4.如果minQ或者maxQ长度为0了,意味着什么?最大值不存了 只能想到nums元素都相等的情况
func longestSubarray(nums []int, limit int) int {
minQ := make([]int, 0)
maxQ := make([]int, 0)
ans := 0
for left, right := 0, 0; right < len(nums); right ++ {
for len(minQ) > 0 && minQ[len(minQ) - 1] > nums[right] {
minQ = minQ[:len(minQ) - 1]
}
for len(maxQ) > 0 && maxQ[len(maxQ) - 1] < nums[right] {
maxQ = maxQ[:len(maxQ) - 1]
}
minQ = append(minQ, nums[right])
maxQ = append(maxQ, nums[right])
for len(minQ) > 0 && len(maxQ) > 0 && maxQ[0] - minQ[0] > limit {
if nums[left] == minQ[0] {
minQ = minQ[1:]
}
if nums[left] == maxQ[0] {
maxQ = maxQ[1:]
}
left ++
}
if right - left + 1 > ans {
ans = right - left + 1
}
}
return ans
}
方法二:双指针
注意到长度小于等于ans字符数符合条件,则长度大于等于ans的子数组必然不符合条件,存在二段性。因此我们猜测是否存在长度为mid的子数组,子数组中最大值和最小值的差小于等于limit,并根据猜测结果,调整mid大小。
func longestSubarray(nums []int, limit int) int {
n := len(nums)
// 注意left,right是猜测的longest subarray,取值范围是[0,n]
left, right := 0, n
for left < right {
mid := (left + right + 1) >> 1
if check(nums, limit, mid) {
left = mid
} else {
right = mid - 1
}
}
return right
}
func check(nums []int, limit, length int) bool {
minQ := make([]int, 0)
maxQ := make([]int, 0)
for left, right := 0, 0; right < len(nums); right ++ {
for len(minQ) > 0 && minQ[len(minQ) - 1] > nums[right] {
minQ = minQ[:len(minQ) - 1]
}
for len(maxQ) > 0 && maxQ[len(maxQ) - 1] < nums[right] {
maxQ = maxQ[:len(maxQ) - 1]
}
minQ = append(minQ, nums[right])
maxQ = append(maxQ, nums[right])
if right - left + 1 == length {
if len(minQ) > 0 && len(maxQ) > 0 && maxQ[0] - minQ[0] <= limit {
return true
}
if len(minQ) > 0 && nums[left] == minQ[0] {
minQ = minQ[1:]
}
if len(maxQ) > 0 && nums[left] == maxQ[0] {
maxQ = maxQ[1:]
}
left ++
}
}
return false
}