@@ -120,111 +120,120 @@ func main() {
120
120
121
121
### 1.1.5 高频题二:达标子数组数量问题
122
122
123
- 给定一个整形数组arr,和衣蛾整数num 。某个arr中的子数组sub,如果想达标,必须满足: sub 中最大值-sub中最小值<=num,返回arr中达标子数组的数量
123
+ 给定一个整形数组arr,和一个整数num 。某个arr中的子数组sub,如果想达标,必须满足: sub中最大值-sub中最小值<=num,返回arr中达标子数组的数量
124
124
125
125
> 子数组是连续的
126
126
127
- > 结论1:对于[ L... R] 范围达标,那么[ L... R] 上的子数组都达标。max[ L...R] 肯定比其子数组的max要大,min[ L...R] 肯定比其范围内的子数组要小,那么[ L...R] 上满足max - min < num,则其子数组必定满足
127
+ > 结论1:对于[ L, R] 范围达标,那么[ L, R] 上的子数组都达标。max[ L...R] 肯定比其子数组的max要大,min[ L...R] 肯定比其范围内的子数组要小,那么[ L...R] 上满足max - min < num,则其子数组必定满足
128
128
129
129
> 同理可得结论2:对于[ L...R] 范围不达标,那么扩展范围后的[ L'...R'] 也不达标
130
130
131
131
> 我们建立两个双端队列,一个是窗口最大值的双端队列,一个是窗口最小值的双端队列。我们扩展我们的窗口R加1,每扩展一个判断是否仍然达标,达标继续扩展,不达标就停,可以得到本次子数组的达标情况,接着缩小我们的窗口L加1,继续...。窗口滑动不会回退,整体O(N)
132
132
133
- ``` Java
134
- package class01 ;
133
+ ``` Go
134
+ package main
135
135
136
- import java.util.LinkedList ;
136
+ import (
137
+ " container/list"
138
+ " fmt"
139
+ )
137
140
138
- public class Code02_AllLessNumSubArray {
141
+ // 达标子数组数量问题
139
142
140
- public static int getNum (int [] arr , int num ) {
141
- if (arr == null || arr. length == 0 ) {
142
- return 0 ;
143
- }
144
- // 窗口内最小值的更新结构
145
- LinkedList<Integer > qmin = new LinkedList<Integer > ();
146
- // 窗口内的最大值的更新结构
147
- LinkedList<Integer > qmax = new LinkedList<Integer > ();
148
- int L = 0 ;
149
- int R = 0 ;
150
- // [L..R) -> [0,0) 窗口内无数 [1,1)
151
- // [0,1) -> [0~0] 窗口里只有一个数
152
- int res = 0 ;
153
- // L是开头位置,尝试每一个开头
154
- while (L < arr. length) {
155
-
156
- // 如果此时窗口的开头是L,下面的while工作是:R向右扩到违规为止
157
-
158
- // R是最后一个达标位置的再下一个,通过下文的break终止
159
- while (R < arr. length) {
160
- // R位置的数进入窗口后,最小值的更新结构和最大值的更新结构都要更新
161
- while (! qmin. isEmpty() && arr[qmin. peekLast()] >= arr[R ]) {
162
- qmin. pollLast();
163
- }
164
- qmin. addLast(R );
165
- // R -> arr[R],
166
- while (! qmax. isEmpty() && arr[qmax. peekLast()] <= arr[R ]) {
167
- qmax. pollLast();
168
- }
169
- qmax. addLast(R );
143
+ // getNum 给定样本数组,和一个目标数值num。求arr中达标的子数组,达标的要求为子数组中最大值减去子数组中最小值小于等于num
144
+ func getNum (arr []int , num int ) int {
145
+ if len (arr) == 0 {
146
+ return 0
147
+ }
170
148
171
- // 如果此时不满足了,break。说明窗口已经成长到了第一个不达标的数进来了
172
- if (arr[qmax. getFirst()] - arr[qmin. getFirst()] > num) {
173
- break ;
149
+ // 窗口内最小值的更新结构
150
+ qmin := list.List {}
151
+ // 窗口内的最大值的更新结构
152
+ qmax := list.List {}
153
+
154
+ // 最小值双端队列的头部元素
155
+ var minfe = &list.Element {}
156
+ // 最小值双端队列的尾部元素
157
+ var minee = &list.Element {}
158
+
159
+ // 最大值双端队列的头部元素
160
+ var maxfe = &list.Element {}
161
+ // 最小值双端队列的尾部元素
162
+ var maxee = &list.Element {}
163
+
164
+ L := 0
165
+ R := 0
166
+ // [L..R) -> [0,0) 窗口内无数 [1,1)
167
+ // [0,1) -> [0~0] 窗口里只有一个数
168
+ res := 0
169
+ // L是开头位置,尝试每一个开头
170
+ for L < len (arr) {
171
+ // 如果此时窗口的开头是L,下面的for工作是:R向右扩到违规为止
172
+
173
+ // R是最后一个达标位置的再下一个,通过下文的break终止
174
+ for R < len (arr) {
175
+ // R位置的数进入窗口后,最小值的更新结构和最大值的更新结构都要更新
176
+ for qmin.Len () != 0 && arr[qmin.Back ().Value .(int )] >= arr[R] {
177
+ // 尾部移除
178
+ qmin.Remove (minee)
179
+ if qmin.Len () > 0 {
180
+ minfe = qmin.Front ()
181
+ minee = qmin.Back ()
174
182
}
175
- R ++ ;
176
183
}
177
184
178
- // R是最后一个达标位置的再下一个,第一个违规的位置
179
- res += R - L ;
180
-
181
- // 检查最小值和最大值的更新结构有没有过期
182
- if (qmin. peekFirst() == L ) {
183
- qmin. pollFirst();
184
- }
185
- if (qmax. peekFirst() == L ) {
186
- qmax. pollFirst();
185
+ qmin.PushBack (R)
186
+ minee = qmin.Back ()
187
+ minfe = qmin.Front ()
188
+
189
+ // R -> arr[R],
190
+ for qmax.Len () != 0 && arr[qmax.Back ().Value .(int )] <= arr[R] {
191
+ // 尾部移除
192
+ qmax.Remove (maxee)
193
+ if qmax.Len () > 0 {
194
+ maxfe = qmax.Front ()
195
+ maxee = qmax.Back ()
196
+ }
187
197
}
188
198
189
- // 窗口左边界向右滑动,窗口容量此时减1
190
- L ++ ;
191
-
192
- }
193
- return res;
194
- }
199
+ qmax.PushBack (R)
200
+ maxee = qmax.Back ()
201
+ maxfe = qmax.Front ()
195
202
196
- // for test
197
- public static int [] getRandomArray ( int len ) {
198
- if (len < 0 ) {
199
- return null ;
203
+ if arr[qmax. Front (). Value .( int )]-arr[qmin. Front (). Value .( int )] > num {
204
+ break
205
+ }
206
+ R++
200
207
}
201
- int [] arr = new int [len];
202
- for (int i = 0 ; i < len; i++ ) {
203
- arr[i] = (int ) (Math . random() * 10 );
208
+ // R是最后一个达标位置的再下一个,第一个违规的位置
209
+ res += R - L
210
+ // 检查最小值和最大值的更新结构有没有过期
211
+ if qmin.Front ().Value .(int ) == L {
212
+ qmin.Remove (minfe)
213
+ if qmin.Len () > 0 {
214
+ minfe = qmin.Front ()
215
+ minee = qmin.Back ()
216
+ }
204
217
}
205
- return arr;
206
- }
207
218
208
- // for test
209
- public static void printArray ( int [] arr ) {
210
- if (arr != null ) {
211
- for ( int i = 0 ; i < arr . length; i ++ ) {
212
- System . out . print(arr[i] + " " );
219
+ if qmax. Front (). Value .( int ) == L {
220
+ qmax. Remove (maxfe)
221
+ if qmax. Len () > 0 {
222
+ maxfe = qmax. Front ()
223
+ maxee = qmax. Back ()
213
224
}
214
- System . out. println();
215
225
}
226
+ // 窗口左边界向右滑动,窗口容量此时减1
227
+ L++
216
228
}
217
-
218
- public static void main (String [] args ) {
219
- int [] arr = getRandomArray(30 );
220
- int num = 5 ;
221
- printArray(arr);
222
- System . out. println(getNum(arr, num));
223
-
224
- }
225
-
229
+ return res
226
230
}
227
231
232
+ func main () {
233
+ arr := []int {4 , 2 , 1 , 5 , 6 , 1 , 7 , 22 , 53 , 16 , 24 , 65 , 72 , 17 , 21 , 42 }
234
+ num := 5
235
+ fmt.Println (getNum (arr, num))
236
+ }
228
237
```
229
238
230
239
> 本题根据窗口滑动建立了单调性,上文的结论
0 commit comments