算法:单调栈

题目描述

给定一个不含有重复值的数组 arr,找到每一个 i 位置左边和右边离 i 位置最近且值比 arr[i] 小的位置。返回所有位置相应的信息。

举例:

输入:

arr = {3, 4, 1, 5, 6, 2, 7}

返回如下二维数组作为结果:

{
	{-1,  2}, // -1表示不存在,整体含义为:arr[0]左边离arr[0]最近且比arr[0]小的是arr[-1],
			  // 				      arr[0]右边离arr[0]最近且比arr[0]小的是arr[2],
	{ 0,  2},
	{-1, -1},
	{ 2,  5},
	{ 3,  5},
	{ 2, -1},
	{ 5, -1},
}

题目解析

暴力

class Solution {
    std::vector<std::vector<int>> getNearLess(std::vector<int> arr){
        int size = arr.size();
        std::vector<std::vector<int>> res(size, std::vector<int>(2));
        for (int i = 0; i < size; ++i) {
            int leftLessIndex = -1, rightLessIndex = -1;
            int curr = i - 1;
            while (curr >= 0){
                if(arr[curr] < arr[i]){
                    leftLessIndex = curr;
                    break;
                }
                curr--;
            }
            curr = i +1;
            while (curr < size){
                if(arr[curr] < arr[i]){
                    rightLessIndex = curr;
                    break;
                }
                curr++;
            }
            res[i][0] = leftLessIndex;
            res[i][1] = rightLessIndex;
        }
        return res;
    }
};

单调栈

当数组中没有重复值时

求比arr[i]大的左右值

在这里插入图片描述

(1) 准备一个单调递增栈,从小到大增长。栈中数据存储的是索引
在这里插入图片描述
(2)然后开始遍历原数组,当遍历完3,4时,发现满足要求,就将3,4的索引压入栈中
在这里插入图片描述
(2)当遍历到2时,发现不满足单调栈的要求,当前数不能压栈,怎么办呢?
在这里插入图片描述
无论如何,当前数都是要压栈的,所以就要将栈中的某些数出栈,出栈的瞬间,就得到了这个数的某些信息。

所以1—>4出栈:

  • 右边比1—>4小的数是令它出栈的那个数,就是2—>2
  • 左边比1—>4小的数:因为4出栈之后,栈不为空,所以就是栈顶元素0---->3

那么1—>4出栈了之后,2—>2能不能进栈呢?不能,0—>32—>2大,所以0—>3也要出栈

  • 右边比0—>3小的数是令它出栈的那个数,就是2—>2
  • 左边比0—>3小的数:因为0—>3出栈后,栈为空了,所以左边没有比0—>3小的数,填**-1**

现在2—>2可以进栈了
在这里插入图片描述

(3)继续遍历
在这里插入图片描述
然后可以得到:

  • 3—>62—>24—>1
  • 2—>2-14—>1
    在这里插入图片描述
    (4)继续遍历
    在这里插入图片描述
  • 5—>74—>16---->0

在这里插入图片描述
(5)现在所有数据都遍历完成了,但是栈不为空

我们要倒空栈。因为右边没有可以使得出栈的元素,所以右边都没有把它小的数

  • 6—>0
    • 左边比它小的数:因为出栈时栈顶不为空,所以就是栈顶元素4—>1
    • 右边比它小的数:因为没有可以使得出栈的元素,所以数组右边没有比它小的数,填-1
  • 4—>1-1,-1

当数组中有重复值时

在这里插入图片描述

(1) 准备一个单调递增栈,从小到大增长。栈中数据存储的是索引链表
在这里插入图片描述
(2)遍历数组

在这里插入图片描述
4—>4不能放到5的上面,所以 {3}—>5 要结算答案

  • {3}—>5
    • 右边比它小的数:谁令我出栈?4—>4
    • 左边比它小的数:我压着的那个链表的最后一个位置:3—>5

在这里插入图片描述
那么现在4—>4能不能落在栈顶呢?不能,因为栈顶元素也是4,所以合并
在这里插入图片描述

(3)继续遍历数组

5—>3能不能压栈?不能,栈顶元素必须出栈,出栈的元素要结算答案。出栈的元素是{2,4},所以分别对{2,4}结算答案
在这里插入图片描述

  • 4—>41—>35—>3
  • 2---->41—>35—>3

可以看出,它们的答案是一样的

实现

当没有重复值

class Solution {
    std::vector<std::vector<int>> getNearLessNoRepeat(std::vector<int> arr){
        int size = arr.size();
        std::vector<std::vector<int>> res(size, std::vector<int>(2));
        std::stack<int> stack;
        for (int i = 0; i < size; ++i) {
            while (!stack.empty() && arr[stack.top()] > arr[i]){
                int j = stack.top(); stack.pop();
                int leftLessIndex = stack.empty() ? -1 : stack.top();
                res[j][0] = leftLessIndex;
                res[j][1] = i;
            }
            stack.push(i);
        }
        while (!stack.empty()){
            int j = stack.top(); stack.pop();
            int leftLessIndex = stack.empty() ? -1 : stack.top();
            res[j][0] = leftLessIndex;
            res[j][1] = -1;
        }
        return res;
    }
};

当有重复值

class Solution {
    std::vector<std::vector<int>> getNearLess(std::vector<int> arr){
        int size = arr.size();
        std::vector<std::vector<int>> res(size, std::vector<int>(2));
        std::stack<std::list<int>> stack;
        for (int i = 0; i < size; ++i) {
            while (!stack.empty() && arr[stack.top().front()] > arr[i]){
                std::list<int> js = stack.top(); stack.pop();
                int leftLessIndex = stack.empty() ? -1 : stack.top().back();
                while (!js.empty()){
                    auto j = js.front(); js.pop_front();
                    res[j][0] = leftLessIndex;
                    res[j][1] = i;
                }
            }
            if(!stack.empty() && arr[stack.top().front()] == arr[i]){
                stack.top().push_back(i);
            }else{
                std::list<int> l;
                l.push_back(i);
                stack.push(l);
            }
        }
        while (!stack.empty()){
            std::list<int> js = stack.top(); stack.pop();
            int leftLessIndex = stack.empty() ? -1 : stack.top().back();
            while (!js.empty()){
                auto j = js.front(); js.pop_front();
                res[j][0] = leftLessIndex;
                res[j][1] = -1;
            }
        }
        return res;
    }
};

对数器

其他

求比arr[i]大的左右值

在这里插入图片描述
在这里插入图片描述

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值