栈和队列及其C++实现(一)
===============================================
一、简介
栈和队列都是一种特殊的线性表,但与普通线性表的不同的是,数据往栈和队列中放入或是从中删除,其位置是特定的。
栈中的数据如同餐馆里碟起来的盘子,放入盘子时必定是放在最顶部,取出盘子时也必定是从定必取,可以发现,往栈中放入盘子的次序刚好与把它们依次取出来的顺序相反,所以栈实现的是一种先进后出(FILO)的结构。
而队列恰好相反,实现的是后进先出(LIFO)的结构。队列顾名思义,就如同排队的队伍,进入队伍的人排在队伍最后,从队伍出来的人是队伍中最前面的人,也就是队头。
这里先介绍栈的实现。
二、数据结构及函数接口
由于数据插入或是删除都在栈顶进行,而栈底是不需要移动的,因此栈更适合用数组存放数据。我们只需要在类中定义一个指向栈底的指针和指向栈顶的指针以及栈的容量就行了。栈底指针是不会移动的,栈顶指定会因为插入、删除操作不断移动,要注意的是,栈顶指定永远指向即将要存放数据的位置,而不是已经存放了数据的位置。
对于栈必要的操作有插入(Push,或称为压栈)、删除(Pop,或称为出栈)、判断栈是否为空(Empty)、判断栈是否为满(Full)、返回栈顶元素值(Top)
以下为c++栈类:
template<typename type>
class Stack{
private:
type *base;//栈底指针
type *top;//栈顶指针
int size;//栈的容量
public:
Stack();//构造函数,容量默认为10
~Stack();///析构函数
void push(type data);
type pop();
type Top();
bool empty();
bool full();
};
三、实现
显然,empty和full函数是最容易实现的,当栈顶指定和栈底指针指向同一个位置时,栈就是空的,当栈顶指针和栈底指针之差等于size时,栈就是满的。
template<typename type>
bool Stack::empty()
{
if (base == top)
return true;
return false;
}
template<typename type>
bool Stack::full()
{
if (top - base == size)
return true;
return false;
}
构造函数中需要用new运算符为数组分配内存空间,空间大小为栈的容量size。析构函数中只需将数组内存释放即可。
template<typename type>
Stack<type>::Stack():size(size)
{
size = 10;
base = top = new type[size];
}
template<typename type>
Stack<type>::~Stack()
{
delete[] top;
delete[] base;
}
压栈操作中,只需将栈顶指针向移动一个单位。若栈已满,则需要将栈扩容,即重新为栈分配更大的内存空间,并把原来数据全部移动到新的空间内。
template<typename type>
template<typename type>
void Stack<type>::push(type data)
{
if (full()){//栈满,需要扩容
type *temp = new type[2 * size];//临时的数组temp
for (int i = 0; i < size; i++){//复制原数组
temp[i] = base[i];
}
this->base = temp;
this->top = this->base + size;
this->size *= 2;//更新size的值
}
*top++ = data;//将data赋值给当前top指针,然后top++
}
出栈比入栈更简单,只需将top指针自减就可以了。若栈为空,则抛出异常。
template<typename type>
type Stack<type>::pop()
{
/*if (empty())
throw exceprion();*/
//省略了异常处理
return *--top;
}
返回栈顶元素只需把出栈时的–top去掉就行,直接返回*top
template<typename type>
type Stack<type>::Top()
{
/*if (empty())
throw exceprion();*/
//省略了异常处理
return *(top - 1);
}
以上pop和Top函数省略了异常处理,是为了测试的简化。而且我认为,初学时没有太大的必要纠结于这些细枝末节,抓住核心代码才是理解数据结构的关键。
四、完整代码
template<typename type>
class Stack{
private:
type *base;//栈底指针
type *top;//栈顶指针
int size;//栈的容量
public:
Stack();//构造函数,容量默认为10
~Stack();///析构函数
void push(type data);
type pop();
type Top();
bool empty();
bool full();
};
template<typename type>
bool Stack<type>::empty()
{
if (base == top)
return true;
return false;
}
template<typename type>
bool Stack<type>::full()
{
if (top - base == size)
return true;
return false;
}
template<typename type>
Stack<type>::Stack():size(size)
{
size = 10;
base = top = new type[size];
}
template<typename type>
void Stack<type>::push(type data)
{
if (full()){//栈满,需要扩容
type *temp = new type[2 * size];//临时的数组temp
for (int i = 0; i < size; i++){//复制原数组
temp[i] = base[i];
}
this->base = temp;
this->top = this->base + size;
this->size *= 2;//更新size的值
}
*top++ = data;//将data赋值给当前top指针,然后top++
}
template<typename type>
type Stack<type>::pop()
{
/*if (empty())
throw exceprion();*/
//省略了异常处理
return *--top;
}
template<typename type>
type Stack<type>::Top()
{
/*if (empty())
throw exceprion();*/
//省略了异常处理
return *(top - 1);
}
template<typename type>
Stack<type>::~Stack()
{
delete[] top;
delete[] base;
}
五、C++标准库STL中的栈
c++标准库中也封装了栈的数据结构,它的实现和上文类似,也是用到了template模板。其类名为stack(都是小写),调用时需要加上头文件#include <stack>,初始化时需要按照模板的格式,如
stack<int> test;
以下为函数接口:
- void push(type data); 压栈
- void pop(); 元素出栈,注意不返回出栈元素的值
- void empty(); 判断栈是否为空
- int size(); 返回栈中元素个数
- type top(); 返回栈顶元素
示例代码
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack <int> myStack;//定义栈
myStack.push(5);//压栈
myStack.push(6);
myStack.push(7);
myStack.pop(); //出栈
cout<<myStack.top()<<endl;//输出栈顶元素
cout<<myStack.size()<<endl;//输出栈元素数量
cout<<myStack.empty()<<endl;//栈是否为空
return 0;
}
六、相关算法题
《数据结构》P90迷宫问题
表达式计算问题
中缀表达式转后缀表达式问题
(待完善)