【数据结构】栈

栈是常见的数据结构之一,我将在这篇博客中简要介绍 栈、栈的使用、单调栈、栈和DFS、栈和递归

栈(stack) 又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底
作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。

栈的使用

  • stl库实现

在C++中的stl库中存在已经封装好的栈已经栈相关的函数,我们可以直接使用。

    头文件:#include <stack>
    定义在std空间中: using namespace std;
    创建栈: stack<数据类型> stk(自定义栈名);
    栈的基本操作函数:
        stk.push(X);将元素X放入栈中
        stk.pop();删除栈尾元素
        stk.size();返回栈中元素的个数
        stk.empty();如果栈为空返回true,不空返回false
        stk.top();访问栈顶元素
  • 手写栈

手写栈比使用stl库的效率更高,更节约时间,所以一般在算法竞赛中会选择用数组模拟实现栈。

    int stk[n];创建栈
    int cnt = 0;指向栈顶的指针
    stk[++cnt] = x //x入栈,++cnt是因为通常会空出下标为0的作为判断栈是否为空,其次++cnt使cnt所指是栈顶元素
    cnt--; //弹出栈顶元素
    cnt >0 //cnt > 0则栈不为空,cnt == 0则栈为空
    //cnt的值表示栈里有多少个元素
    stk[cnt];// 栈顶元素

好了,你已经会使用栈了()。

单调栈

单调栈是栈的一个常见的使用方法。
顾名思义,单调栈是保证栈中元素时刻为单调序列的栈。
单调栈的实现:
新的元素入栈时从栈顶开始判断,如果不满足单调条件就弹出栈顶元素,循环直到满足单调条件或者栈为空,再将该元素入栈。

    while(!stk.empty() && stk.top() < / > target)
        stk.pop();
    stk.push(target)
    涉及数组的问题时,通常我们是用栈存储数组下标,而不是数组元素。

单调栈的应用范围我知道的不全面,只能例举几道单调栈道题目。

503. 下一个更大元素 II
由题意可知,这道题使用的是一个单调减的栈。
弹出栈的元素的下一个更大元素就是正要入栈的元素。
我们只需要对[原数组 + 前n - 1个元素]的数组用单调栈遍历一边即可,最后留在栈中的便是没有下一个更大元素的元素对此,我们只需要初始化答案容器(c++里是vector,c里为数组)为-1即可。

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        int n = nums.size();
        vector<int> ans(n,-1);//初始化为-1
        int stk[2*n - 1];//栈存储数组下标,便于填充答案数组
        int cnt = 0;
        for(int i = 0; i < 2*n - 1; i++)
        {
            int tmp = i % n;
            while(cnt > 0 && nums[stk[cnt]] < nums[tmp])
            {
                ans[stk[cnt]] = nums[tmp];
                cnt--;
            }
            stk[++cnt] = tmp;
        }
        return ans;
    }
};

402. 移掉 K 位数字
这道题是贪心+单调栈可以解决。
对数字143XXXX,删掉第一个数字时,显然14XXX > 13XXX,所以选择删掉4依次类推,原理是位数越高,影响越大。也就是贪心,我总是要去掉高位的数字。

char * removeKdigits(char * num, int k){
    if(strlen(num) == k)
        return "0";
    int len = strlen(num);
    char stk[100010]={};
    int head = 0 , i = 0;
    while(i < len)
    {
        while(head > 0 && stk[head] > num[i] && k)
        {
            head--;
            k--;
        }//每弹出一个,去除一位数
        stk[++head] = num[i];
        i++;
    }//单调栈走完后,栈中留下去除一定位数的数据
    head -= k;//由于可能还没有去除k位数字就已经结束了,由于栈中此时是单调增的,我们只需要从后往前去除剩下几位数
    char* ans = (char*)malloc(sizeof(char)*(len+1));
    int q = 1 , j = 0;
    while(q <= head && stk[q]=='0')
        q++;//除去可能存在的前导零
    for(; q <= head; q++)
    {
        ans[j++] = stk[q];
    }//复制处理后的数据
    if(j == 0)//如果没有可以复制的数据,则答案为0,并添加结束的0
    {
        ans[0] = '0';
        ans[1] = 0;
    }
    else
        ans[j] = 0;//字符串末位添加0结束
    return ans;
}

栈与DFS

由于栈先进后出的性质,通常会和DFS(深度优先搜索)练习在一起。
深度优先搜索可以用递归、迭代等方式实现,比较常见的是在二叉树的遍历中(不过二叉树遍历还有一个比较难的算法,问就是不会)。DFS,通俗讲就是一条路走到黑,撞到南墙才回头走另一条岔道。
这和栈先进后出的性质很符合,比如说
二叉树的前序遍历:对深度第二的一个节点 node -> node、node->left -> node -> node、node->right
我们访问到node ,下一步让左子节点进栈,经过条件判断后左子节点没有子节点了,左子节点出栈,右子节点进栈、出栈,然后node出栈,栈顶元素来到了node的父节点。

例题:
733. 图像渲染
由于我写的是BFS,就直接用了官方的代码

class Solution {
public:
    string removeKdigits(string num, int k) {
        vector<char> stk;
        for (auto& digit: num) {
            while (stk.size() > 0 && stk.back() > digit && k) {
                stk.pop_back();
                k -= 1;
            }
            stk.push_back(digit);
        }

        for (; k > 0; --k) {
            stk.pop_back();
        }

        string ans = "";
        bool isLeadingZero = true;
        for (auto& digit: stk) {
            if (isLeadingZero && digit == '0') {
                continue;
            }
            isLeadingZero = false;
            ans += digit;
        }
        return ans == "" ? "0" : ans;
    }
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/remove-k-digits/solutions/484940/yi-diao-kwei-shu-zi-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

栈与递归

由于c语言中调用函数时分配给函数的是内存中栈的空间。
因此,函数的递归调用,可以看作是一个栈。
第一层函数在栈底,随着不断递归调用,新的函数不断向栈顶添加,如果这时递归结束条件没有设置好,就会导致栈的内存溢出,程序异常终止。
用栈的方式理解函数的递归调用,或许会更好理解一点。

谢谢观看~

posted @ 2023-01-19 01:22  瞻鹤  阅读(183)  评论(0编辑  收藏  举报