Skip to content

Commit 9cbec44

Browse files
author
Obsession-kai
committed
update day27/31
1 parent 4a1b87b commit 9cbec44

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// 题目链接:https://leetcode.cn/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/?envType=study-plan&id=lcof
2+
// day27/31
3+
// 第 27 天主题为:栈与队列(困难)
4+
// 包含两道题目:
5+
// 剑指offer59-I.滑动窗口的最大值
6+
// 剑指offer59-II.队列的最大值
7+
package main
8+
9+
//可以先去暴力解决,对每个滑动窗口,遍历所有元素得到最大值。
10+
//对长度为 n 的数组 nums,窗口数量为 n-k+1,暴力解法时间复杂度为 O((n-k+1)*k) = O(nk)
11+
func maxSlidingWindow(nums []int, k int) []int {
12+
n := len(nums)
13+
if n == 0 || k == 0{
14+
return []int{}
15+
}
16+
res := []int{}
17+
for i := 0;i < n-k+1;i++{
18+
res = append(res,max(nums[i:i+k]))
19+
}
20+
return res
21+
}
22+
23+
24+
func max(sli []int) (res int){
25+
res = sli[0]
26+
for _,i := range sli{
27+
if i > res{
28+
res = i
29+
}
30+
}
31+
return
32+
}
33+
34+
//法二:单调队列
35+
//然后想一想,暴力没有用到题目中哪些条件,可以针对这些条件去做改进
36+
//相邻的两个窗口,有重叠元素 k-1 个,这 k-1 个元素中的最大值对两个窗口来说是相同的,这就是我们改进的方向
37+
//
38+
//设想一下,一个窗口中两个元素下标分别为 i 和 j,其中 i 在 j 的左侧(i<j)
39+
//- 若 nums[i] < nums[j],在滑动窗口右移的过程中,只要 nums[i] 在窗口中,则 nums[j] 也一定还在窗口中,这是 i<j 所保证的。
40+
// 因此,由于 nums[j] 的存在,nums[i] 一定不会是滑动窗口最大值,我们可以将 nums[i] 永久删除。
41+
//- 若 nums[i] >= nums[j],nums[i] 出队后,nums[j] 是有可能成为队列最大值的,nums[j] 需要保留
42+
//
43+
//因此,我们可以使用一个队列来存储还没有被删除的元素。在队列中,这些元素的值是单调递减的
44+
//当窗口向右滑动时,我们需要把一个新的元素放入队列中,为了保持队列的性质,我们需要不断将新元素与队列队尾元素进行比较,
45+
//若新元素较大,则队尾元素出队,不断进行此操作,直至队列为空 或 新的元素小于等于队尾的元素
46+
//
47+
//由于队列的元素是严格单调递减的,且队列中元素属于该窗口,所以队首元素就是该窗口的最大值
48+
//此时,我们需要考虑最后一个问题,若当前窗口的最大值为窗口最左侧元素,那进入下一个窗口前队列中该元素应该出队,因为该元素并不属于下一个窗口
49+
//
50+
//该队列元素单调递减,满足这种单调性的队列一般称作 单调队列。
51+
func maxSlidingWindow_2(nums []int, k int) []int {
52+
n := len(nums)
53+
monoQ := []int{}
54+
res := make([]int,0,n-k+1)
55+
for i:=0;i<n;i++{
56+
for len(monoQ) > 0 && nums[i] > monoQ[len(monoQ)-1]{
57+
monoQ = monoQ[:len(monoQ)-1]
58+
}
59+
monoQ = append(monoQ,nums[i])
60+
// 若单调队列最大值非窗口元素
61+
// 将该元素出队
62+
if i >= k && monoQ[0] == nums[i-k]{
63+
monoQ = monoQ[1:]
64+
}
65+
// i=k-1时,窗口元素数量达到 k
66+
// 开始向 res 数组添加窗口最大值
67+
if i >= k-1{
68+
res = append(res,monoQ[0])
69+
}
70+
}
71+
return res
72+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//题目链接:https://leetcode.cn/problems/dui-lie-de-zui-da-zhi-lcof/?envType=study-plan&id=lcof
2+
package main
3+
4+
//有了上一题的基础后,这一题就简简单单了
5+
//和第一题的解析类似,这里我们也可以使用单调队列。把队列看作是第一题的窗口,只不过滑动规则有所差异,
6+
//pop 出队时,窗口的左指针右移,并对我们实现的单调队列进行相应操作,求队列最大值时,只需返回单调队列队首元素即可。
7+
type MaxQueue struct {
8+
// 存储队列
9+
q []int
10+
// 单调队列
11+
monoQ []int
12+
}
13+
14+
// 构造函数
15+
func Constructor() MaxQueue {
16+
return MaxQueue{[]int{},[]int{}}
17+
}
18+
19+
// 取最大值,若队列长度为 0,返回-1,否则返回单调队列队首元素
20+
func (this *MaxQueue) Max_value() int {
21+
if len(this.q) == 0{
22+
return -1
23+
}
24+
return this.monoQ[0]
25+
}
26+
27+
// 入队,存储队列直接入队
28+
// 单调队列需要判断队尾元素与新元素大小关系
29+
// 若新元素大于队尾元素,出队
30+
// 直至单调队列长度为 0或者 新元素值 小于等于 队尾元素
31+
func (this *MaxQueue) Push_back(value int) {
32+
this.q = append(this.q,value)
33+
for len(this.monoQ)>0 && value>this.monoQ[len(this.monoQ)-1]{
34+
this.monoQ = this.monoQ[:len(this.monoQ)-1]
35+
}
36+
this.monoQ = append(this.monoQ,value)
37+
}
38+
39+
// 出队,先判断存储队列长度,若为 0,返回-1
40+
// 获取存储队列队首元素后,存储队列队首元素出队
41+
// 若该元素等于单调队列队首元素,则单调队列队首元素出队
42+
func (this *MaxQueue) Pop_front() int {
43+
if len(this.q) == 0{
44+
return -1
45+
}
46+
res := this.q[0]
47+
this.q = this.q[1:]
48+
if res == this.monoQ[0]{
49+
this.monoQ = this.monoQ[1:]
50+
}
51+
return res
52+
}
53+
54+
55+
/**
56+
* Your MaxQueue object will be instantiated and called as such:
57+
* obj := Constructor();
58+
* param_1 := obj.Max_value();
59+
* obj.Push_back(value);
60+
* param_3 := obj.Pop_front();
61+
*/

0 commit comments

Comments
 (0)