目 录:
本文主要用于讲解一种在面试笔试中经常使用到的一种算法结构:单调栈结构。
1、单调栈结构
单调栈结构:给一个数组,求任何位置左边和右边离它最近的比它大/小的数。
要求:栈中元素需要满足单调性、单调递增/递减;
时间复杂度:O(N)
单调栈的工作过程:
1、新元素加入栈前,会在栈顶端把破坏栈单调性的元素都删除,直到栈为空或者栈满足单调性才能加入新元素;
2、单调栈是 O(N) 级的时间复杂度,所有元素只会进入栈一次,并且出栈后再也不会进栈;
3、单调栈可以找到元素向左遍历第一个比它小(大)的元素,也就是说在元素进栈前它向左拓展的区间已经确定,在出栈前它能向右拓展的区间也能确定(左区间好理解,仔细体会右区间的确定,若该元素至遍历结束后也未出栈,那么就是说在原数组中,该元素的右方向没有一个元素可以比它大/小,那么该元素的右边界就是原数组的大小(就是没有右边界),否则它的右边界就是令它出栈的元素)。
- 注意:单调栈分为两种情况:数据无重复和数据有重复的。
1.1、单调栈结构的实现:无重复元素
public class MonotonicStack {
/**
* 数组中午重复元素
* @param arr :数组
* @return res:每个位置上元素左边和右边离它最近的最小元素
*/
public static int[][] getNearLessNoRepeat(int[] arr){
if(arr == null || arr.length < 1){
return null;
}
// 两列:左边最小、右边最小
int[][] res = new int[arr.length][2];
Stack<Integer> stack = new Stack<>();
for(int i = 0; i < arr.length; i++){
while(!stack.isEmpty() && arr[stack.peek()] > arr[i]){
// 如果当前数比栈顶元素小,则弹出栈中比arr[i]大的数,直到找到比它小的或者栈底
int popIndex = stack.pop();
// 当前弹出元素左边比它小的元素
int leftLessIndex = stack.isEmpty() ? -1 : stack.peek();
// 当前弹出元素右边比它小的元素:谁让它弹出的谁就是右边比它小的
int rightLessIndex = i;
res[popIndex][0] = leftLessIndex;
res[popIndex][1] = rightLessIndex;
}
// 找到正确位置,则将当前元素压入栈中
stack.push(i);
}
// for循环结束后,栈中还有元素
while(!stack.isEmpty()){
int popIndex = stack.pop();
int leftLessIndex = stack.isEmpty() ? -1 : stack.peek();
res[popIndex][0] = leftLessIndex;
// 右边不存在比它小的了
res[popIndex][1] = -1;
}
return res;
}
}
1.2、单调栈结构的实现:有重复元素
有重复元素时,只需要在每个位置拉一个链表出来就行了。每次插入元素都是插入到链表的尾部,这样任一元素左边比它小的最近的元素就是栈中左边位置链表的尾元素。
public class MonotonicStack {
/**
* 数组中有重复元素
* @param arr : 数组
* @return res:每个位置上元素左边和右边离它最近的最小元素
*/
public static int[][] getNearLess(int[] arr){