首页 > 其他分享 >【数据结构】栈

【数据结构】栈

时间:2023-01-19 01:33:06浏览次数:54  
标签:cnt int 元素 栈顶 stk 数据结构 单调

栈是常见的数据结构之一,我将在这篇博客中简要介绍 栈、栈的使用、单调栈、栈和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语言中调用函数时分配给函数的是内存中栈的空间。
因此,函数的递归调用,可以看作是一个栈。
第一层函数在栈底,随着不断递归调用,新的函数不断向栈顶添加,如果这时递归结束条件没有设置好,就会导致栈的内存溢出,程序异常终止。
用栈的方式理解函数的递归调用,或许会更好理解一点。

谢谢观看~

标签:cnt,int,元素,栈顶,stk,数据结构,单调
From: https://www.cnblogs.com/he-zhan/p/17060971.html

相关文章

  • 浅谈Redis底层数据结构(sdshdr-redisObject)
    最近看了点Redis底层的源码分析,特作此记录前提共识:Redis是一个默认为16个数据库的key-value内存数据库Redis底层是由C语言实现文章目录​​C语言源码流程​​​​1、server.......
  • 数据结构-队列
    数据结构-队列队列是一种特殊的线性表,他的性质只允许元素从最后入,从最前出,所以他也满足FIFO(FirstInFirstOut)性质,也就是先进先出。我们可以先试着使用函数在C++中定......
  • 数据结构 玩转数据结构 9-5 Leetcode上线段树相关的问题
    0课程地址https://coding.imooc.com/lesson/207.html#mid=13846 1重点关注1.1线段树区间查询见3.1  2课程内容  3......
  • [数据结构]双向链表(C语言)
    双向链表双向链表概念双向链表也叫双链表,其每个数据结点中都有两个指针,分别指向直接后继和直接前驱。在单向链表中若要找到某个节点的前驱节点,需要先遍历到这个节点,然后......
  • 数据结构:第六章图
    数据结构:第六章图6.1图的概述完全图n个顶点的无向图中边数达到n(n-1)/2成为无向完全图n个顶点的有向图中遍数达到n(n-1)有向图的完全图6.3图的遍历广度优先BFS深度优先,类似......
  • 数据结构 玩转数据结构 9-4 线段树中的区间查询
    0课程地址https://coding.imooc.com/lesson/207.html#mid=13846 1重点关注1.1线段树区间查询见3.1  2课程内容  3......
  • 读编程与类型系统笔记09_泛型数据结构
    1. 恒等函数1.1. 在代数中,恒等函数指的是函数f(x)=x1.2. 恒等逻辑与getNumbers()和assembleWidgets()的问题域解耦,因为恒等逻辑和问题域是正交的,或者说是独立的2.......
  • 数据结构 玩转数据结构 9-3 创建线段树
    0课程地址https://coding.imooc.com/lesson/207.html#mid=13845 1重点关注1.1创建线段树见3.1 1.2代码如何引入方法见3.1 2......
  • 【ES6】JS的Set和Map数据结构
    【ES6】JS的Set和Map数据结构​​一、Set​​​​1、基本用法​​​​2、4种操作方法​​​​3、4种遍历方法​​​​4、Set的应用​​​​1)Set转化为数组​​​​2)去除数组......
  • C/C++数据结构题目[2023-01-16]
    C/C++数据结构题目[2023-01-16]以下内容二选一题目1:校园导航系统的设计与实现问题描述:校园导航系统能够提供校园内场所信息和路径查询。以传媒大学校园为例,校园内包......