118.C++ 中的stack

118.C++ 中的stack

1.stack的概念

先进后出的线性表。

栈是一个STL中的容器适配器,在std命名空间中,它限制插入和删除都在一个位置上(栈顶上),底层是deque(双端队列)。

要使用stack,需要添加头文件

#include <stack>

栈顶:进行数据插入和删除操作的一端

栈底:栈顶的另一端

入栈:栈的插入操作,也叫做压栈/进栈,入数据在栈顶

出栈:栈的删除操作,出数据也在栈顶

2.stack的构造

stack<typename> name;
构造函数 功能
explicit stack (const container_type& ctnr); 构造一个stack,其内部容器被初始化为ctnr的副本。
explicit stack (container_type&& ctnr = container_type()); 构造一个stack,其内部容器通过移动构造来获取ctnr的值。
template <class Alloc> explicit stack (const Alloc& alloc); 构造一个stack,其内部容器使用alloc作为参数构造。
template <class Alloc> stack (const container_type& ctnr, const Alloc& alloc); 构造一个stack,其内部容器使用cntr和alloc作为参数构造。
template <class Alloc> stack (container_type&& ctnr, const Alloc& alloc); 同上,移动构造
template <class Alloc> stack (const stack& x, const Alloc& alloc); 构造一个stack,其内部容器使用x的内部容器作为第一个参数,而alloc作为第二个来构造。
template <class Alloc> stack (stack&& x, const Alloc& alloc); 同上,移动构造

例子:

#include<iostream>
#include<stack>
#include<list>
#include<deque>
using namespace std;

int main()
{
    deque<int> mydeck(3, 100);        //创建一个deque含3个100
    list<int> mylist(2, 200);         //创建一个list含2个200

    stack<int> first;                 //创建一个空的堆栈
    stack<int> second(mydeck);       //用mydeck的拷贝作为堆栈的初始值
    //第二个参数模板默认即为 deque,因此可以不写。

    stack<int, list<int>> third; //空堆栈,列表作为底层容器
    stack<int, list<int>> fourth(mylist); //用mylist的拷贝作为堆栈的初始值

    cout << "size of first: " << first.size() << endl;
    cout << "size of second: " << second.size() << endl;
    cout << "size of third: " << third.size() << endl;
    cout << "size of fourth: " << fourth.size() << endl;

    return 0;
}

输出:

size of first: 0
size of second: 3
size of third: 0
size of fourth: 2

3.stack容器内元素的访问

由于栈(stack)本身就是一种后进先出的数据结构,在STL中的stack只能通过top()来访问栈顶元素
向栈中插入一个元素:s.push(x);

#include <iostream>
#include <stack>
using namespace std;

int main()
{
    stack<int> s;
    for (int i = 1; i <= 5; i++) s.push(i);

    cout << s.top();

    return 0;
}

4.stack中的函数

4.1push()

s.push(x);把x入栈,时间复杂度为O(1),实例见stack容器内元素的访问

4.2top()

s.top();获得栈顶元素,时间复杂度为O(1),实例见stack容器内元素的访问

4.3pop()

s.pop();用来弹出栈顶元素,时间复杂度为O(1)

#include <iostream>
#include <stack>
using namespace std;

int main()
{
    stack<int> s;
    for (int i = 1; i <= 5; i++) s.push(i);

    for (int i = 1; i <= 3; i++) s.pop();
    //把 5 4 3 依次弹出
    cout << s.top();

    return 0;
}

输出:

2

4.4empty()

s.empty();可以检测stack内是否为空,返回true为空,返回false是非空,时间复杂度为O(1)

#include <iostream>
#include <stack>
using namespace std;

int main()
{
    stack<int> s;

    if (s.empty()) cout << "EMPTY" << endl;
    else cout << "NOT EMPTY" << endl;

    for (int i = 1; i <= 5; i++) s.push(i);

    if (s.empty()) cout << "EMPTY" << endl;
    else cout << "NOT EMPTY" << endl;

    return 0;
}

输出:

EMPTY
NOT EMPTY

4.5size()

s.size();返回stack内元素的个数,时间复杂度为O(1)

#include <iostream>
#include <stack>
using namespace std;

int main()
{
    stack<int> s;
    for (int i = 1; i <= 5; i++) s.push(i);

    cout << s.size();

    return 0;
}

输出:

5

4.6empty():判断栈是否为空,为空返回true,否则返回false。

#include <iostream>
#include <stack>
using namespace std;

int main()
{
    stack<int> s;
    if(s.empty())
        cout << "s为空";
    else
        cout << "s不为空"  << endl;
   
    return 0;
}

输出:

s为空

5.顺序栈

栈的顺序存储:同一般线性表的顺序存储结构完全相同。利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端。

●附设 top 指针,指示栈顶元素在顺序栈中的位置。

●另设 base 指针,指示栈底元素在顺序栈中的位置。但是,为了方便操作,通常top指示真正的 栈顶元素之上 的下标地址。

●另外,用stacksize表示栈可使用的最大容量。

使用数组作为顺序栈存储方式的特点:

简单、方便、但容易产生溢出(数组大小固定)。

上溢(overflow):栈已经满,又要压入元素。是一种错误,使问题处理无法进行。

下溢(underflow):栈已经空,还要弹出元素。一般不认为是错误,而是一种结束条件,即问题处理结束。

5.1顺序栈的定义

#define MAXSIZE 100
typedef struct 
{
    SElemType* base;    //栈底指针
    SElemType* top;     //栈顶指针
    int stacksize;      //栈的最大容量
} SqStack;

但是顺序栈的top和base指针可以是 int 类型用来存放数组下标,并不是真正意义上的指针变量。

存数组单元的地址也可以,在做一些操作时(比如top-base,得到的值是两个数组单元的距离),和存放下标的方式区别不大。

5.2顺序栈的初始化

算法思路:

1.开辟一片大小为stacksize的空间

2.top和base都指向栈底

算法描述:

Status InitStack(SqStack& S)//构造一个空栈
{  
    S.base = new SElemType[MAXSIEZE];
    //或S.base = (SElemType*)malloc(MAXSIZE*sizeof(SElemType));
    if (!S.base) exit(OVERFLOW);    //存储分配失败
    S.top = S.base; //栈顶指针等于栈底指针
    S.stacksize = MAXSIZE;
    
    return OK;
}

5.3顺序栈的销毁

算法思路:

  • 把整个栈空间释放掉

    1.释放掉base指向的空间

    2.把stacksize设置为0

算法描述:

Status DestroyStack(SqStack& S)
{
    if (S.base)
    {
        delete S.base;
        S.stacksize = 0;
        S.base = S.top = NULL;
    }
    return OK;
}

5.4判断顺序栈是否为空

算法思路:

  • top指针和base指针都指向栈底,此时top == base

算法描述:

Status StackEmpty(SqStack S) 
{
    //若栈为空,返回TRUE,否则返回FALSE
    if (S.top == S.base)
        return TRUE;
    else
        return FALSE;
}

5.5求顺序栈的长度

算法思路:

  • 指针相减,差为两个指针的间隔,也就是元素个数。

算法描述:

int StackLength(SqStack S) 
{
    return S.top - S.base;
}

5.6取栈顶元素

算法思路:

1.判断栈是否存在并且非空

2.返回栈顶元素

算法描述:

Status GetTop(S, &e)
{
    if (S.top == S.base) return ERROR;
    e = *(S.top - 1);
    return OK;
}

5.7清空顺序栈

算法思路:

●把栈当作是空就行了,也就是top也指向栈底位置。

算法描述:

Status ClearStack(SqStack S)
{
    if (S.base) S.top = S.base;
    return OK;
}

5.8顺序栈的入栈

算法思路

1.判断是否栈满,若满则出错(上溢)

2.将元素存到top所指的位置

3.top上移

算法描述:

Status Push(SqStack& S, SElemType e) 
{
    if (S.top - S.base == S.stacksize)   //栈满
        return ERROR;
    *S.top++ = e;       //(*S.top=e;S.top++;)
    return OK;
}

5.9顺序栈出栈

算法思路:

1.判断是否栈空,若空则出错(下溢)

2.top下移

3.取top指针所指位置上的元素

算法描述:

Status Pop(SqStack& S, SElemType& e)
{
    //若栈不空,则“删除”栈顶元素,用e返回,“删除”成功返回OK,失败返回ERROR
    if (S.top == S.base) //if(StackEmpty(S))
        return ERROR;
    e = *--S.top;	//--S.top;e = *S.top;
    return OK;
}

6.链栈

逻辑都是相似的。

●链栈是运算受限的单链表,只能在链表头部进行操作。

几个注意的点:

●链表的头指针就是栈顶

●不需要头结点

●基本不存在栈满的情况

●空栈相当于头指针指向空

●插入和删除仅在栈顶处执行

6.1链栈的定义

typedef struct StackNode
{
    SElemType data;
    struct StackNode* next;
} StackNode, * LinkStack;
LinkStack S;

如同定义单链表。

6.2链栈的初始化

算法描述:

void InitStack(LinkStack& S) 
{
    //构造一个空栈,栈顶指针置空
    S = NULL;
    return OK;
}

6.3判断链栈是否为空

算法思路:

●头指针不指向任何元素时为空

算法描述:

Status StackEmpty(LinkStack S)
{
    if (S == NULL) return TRUE;
    else return FALSE;
}

6.4取栈顶元素

算法思路:

  • 直接取出S所指的元素

算法描述:

SElemType GetTop(LinkStack S) 
{
    if (S != NULL)
        return S->data;
}

6.5链栈的入栈

算法思路:

1.生成新结点p

2.将新节点插入栈顶

3.修改栈顶指针

算法描述:

Status Push(LinkStack& S, SElemType e)
{
    p = new Stacknode;	//生成新结点p
    p->data = e;	//将新节点的数据域置为e
    p->next = S;	//将新节点插入栈顶
    S = p;			//修改栈顶指针
    return OK;
}

6.6链栈的出栈

算法思路:

1.判断栈是否存在且非空

2.保存当前栈顶地址

3.修改栈顶指针

4.释放刚才的栈顶空间

算法描述:

Status Pop(LinkStack& S, SElemType& e) 
{
    if (S == NULL) return ERROR;
    e = S->data;
    p = S;
    S = S->next;
    delete P;
    return OK;
}

7.栈与一般线性表的区别

一般线性表
逻辑结构 一对一 一对一
存储结构 顺序表、链表 顺序栈、链栈
运算规则 随机存取 后进先出(LIFO)

参考:

栈(stack)(数据结构与算法基础)

STL—stack

posted @ 2023-07-24 09:44  CodeMagicianT  阅读(62)  评论(0编辑  收藏  举报