目录
一、栈的简介
栈(Stack)是一种重要的数据结构,它遵循“后进先出”(LIFO, Last In First Out)的原则。这意味着最后插入栈中的元素会最先被移除。栈的基本操作主要包括:
- 入栈(push):将一个元素添加到栈顶。
- 出栈(pop):移除栈顶的元素,并返回该元素。
- 取栈顶(top):返回栈顶的元素,但不移除它。
- 判空(empty):返回一个布尔值,指示栈是否为空。
栈有两种存储结构,分别是顺序存储结构和链接存储结构,对应称栈为顺序栈和链栈。本文将介绍这两种栈的实现,并介绍STL库中的栈的使用。作为一般规律,当栈的使用过程中,元素个数变化较大时,建议使用链栈,反之,使用顺序栈。
二、顺序栈的实现
- 构造函数:初始化一个空的顺序栈只需将栈顶指针top_index置为-1。
- 析构函数:顺序栈是静态存储分配,在顺序栈变量退出作用域时自动释放顺序栈所占存储单元,因此,顺序栈无需销毁,析构函数为空。
- 入栈操作:栈顶指针top_index+1,然后将数据存入栈顶位置即可。
- 出栈操作:取出栈顶元素,栈顶指针top_index-1即可。
- 取栈顶操作:取出栈顶元素,栈顶指针top_index不变。
- 判空操作:判断栈顶指针是否为-1即可。
template <typename T> //使用模板,兼容各种数据类型
class SeqStack
{
public:
SeqStack() {top_index = -1;} //构造函数
~SeqStack() {}; //析构函数
void push(T x) //入栈操作
{
if (top_index == size - 1)
{
throw "上溢"; //防止溢出
}
data[++top_index] = x;
}
T pop() //出栈操作
{
if (top_index == -1)
{
throw "下溢"; //防止溢出
}
T temp=data[top_index--];
return temp;
}
T top() const //取栈顶操作
{
if (top_index == -1)
{
throw "下溢"; //防止溢出
}
T temp = data[top_index];
return temp;
}
bool empty() //判空操作
{
return top_index==-1;
}
private:
const static int size = 100000; //可存放的数据量
T data[size]; //用于存放数据的数组
int top_index; //栈顶的索引
};
说明:
上述代码中,T top()后面加上了const关键字,表示该成员函数是只读的,不会修改对象的任何成员变量。
int size 前使用了const和static关键字,const确保size不会被修改,static确保size是类范围的静态变量,所有对象共享该变量,减少内存(不用每创建一个对象的同时都为size申请一块内存)的同时,使下一个语句T data[size]可以使用变量size。
三、链栈的实现
链栈的实现相对复杂,可以的话建议使用顺序栈。
- 构造函数:初始化一个空的链栈只需将栈顶指针top_index置为nullptr。
- 析构函数:链栈是动态存储分配,在链栈变量退出作用域前,要释放链栈的存储空间。
- 入栈操作:只需处理栈顶的情况。
- 出栈操作:只需处理栈顶的情况。
- 取栈顶操作:取出栈顶元素,不修改栈顶指针top_index。
- 判空操作:判断栈顶指针是否为nullptr即可。
template <typename T> //使用模板,兼容各种数据类型
struct Node //结点
{
T data;
Node<T>* next;
};
template <typename T> //使用模板,兼容各种数据类型
class LinkStack
{
public:
LinkStack() //构造函数
{
top_index = nullptr;
}
~LinkStack() //析构函数
{
Node<T>* p = top_index;
while (top_index != nullptr)
{
top_index = top_index->next;
//栈顶指针指向下一个结点
delete p;
//释放前栈顶结点
p = top_index;
//指向新栈顶结点
}
};
void push(T x) //入栈操作
{
Node<T>* s = nullptr;
s = new Node<T>;
//生成一个新结点
s->data = x;
s->next = top_index;
//连接前栈顶结点
top_index = s;
//成为新栈顶结点
}
T pop() //出栈操作
{
Node<T>* p = nullptr;
if (top_index == nullptr)
{
throw "下溢"; //防止溢出
}
T temp = top_index->data;
//取栈顶数据
p = top_index;
//指向栈顶结点
top_index = top_index->next;
//栈顶指针指向下一个结点
delete p;
//释放栈顶结点
return temp;
}
T top() const //取栈顶操作
{
if (top_index == nullptr)
{
throw "下溢"; //防止溢出
}
T temp = top_index->data;
//取栈顶数据
return temp;
}
bool empty() //判空操作
{
return top_index == nullptr;
}
private:
Node<T>* top_index;
};
该链栈的操作基于链表结构,所有操作的时间复杂度均为 O(1),因为入栈、出栈和取栈顶操作都只涉及栈顶结点的操作,而无需遍历整个栈。链栈的优点是可以动态扩展栈的大小,避免固定大小的限制。
四、STL库中的栈
栈这样的数据结构已经在STL库中定义好了,如果没有自行设计栈的要求,可以直接使用库中定义好的栈。现简单介绍其用法如下:
#include <stack> //使用栈需要包含相应头文件
stack<int> s; //定义一个int类型的栈,命名为s
s.push(666); //入栈
s.pop(); //出栈
s.top(); //取栈顶,返回栈顶元素
s.empty(); //判空,返回bool值
s.size(); //查询栈大小,返回栈中元素个数