Satck
Stack定义
stack的特点是先进先出,与queue正好相反;
class stack定义如下:
nemespace std{
template <class T,
class Container = deque<T> >
class stack;
}
第一个代表元素型别,第二个参数用来定义stack内部存放元素所用的实际容器,默认是使用deque;从定义我们可以看出,stack只是很单纯地把各项操作转化为内部容器的对应调用,我们可以使用任何序列式容器来支持stack,只要它支持back(),push_back(),pop_back()等动作。所以我们可以使用deque,也可以使用vector和list;
核心接口
stack核心接口只有三个,反别是push()、top()、pop(),这三个函数成员的作用不再赘述,不太清楚的同学可以查查资料,我们重点放在其接口特点上;
pop()函数移除下一个元素,但并不返回,top()返回下一个元素,但是并不移除,所以如果我们想要处理下一个元素并处理它,那么二者都要用到,需要注意的是:如果stack内没有元素,那么top和pop会导致未定义的行为,所以在调用这两个函数之前,最好使用size()和empty()函数来检验容器是否为空;
stack源码
考虑到stack的源码较少,便于理解,所以这里附上其简化部分:
namespace std{
template <class T,class Container = deque<T> >
class stack {
public:
typedef typename Container::value_type value_type;
typedef typename Container::size_type size_type;
typedef container Container_type;
protected:
Container c;
public:
explicit stack(const Container& = Container());
bool empty() const {return c.empty();}
size_type size() const {return c.size();}
void push(const value_type& x) {c.push_back(x);}
void pop() {c.pop_back();}
value_type& top() {return c.back();}
const value_type& top() const {return c.back();}
};
template<class T,class Container>
bool operator==(const stack<T,Container>&,const stack<T,Container>&);
template<class T,class Container>
bool operator< (const stack<T,Conatiner>&,const stack<T,Container>&);
...//(other copairsion operators)
}
栈的实现
静态数组实现栈
#ifndef STACK_H
#define STACK_H
typedef unsigned long Item;
class Stack
{
private:
enum {MAX = 10};
Item items[MAX];
int top;
public:
Stack();
bool isempty() const;
bool isfull() const;
bool push(const Item &item);
bool pop(Item &item);
Item top();
};
#endif // STACK_H
#include "stack.h"
Stack::Stack()
{
top = 0 ;
}
bool Stack::isempty() const
{
return top == 0;
}
bool Stack::isfull() const
{
return top == MAX;
}
bool Stack::push(const Item &item)
{
if(top < MAX)
{
items[top++] = item;
return true;
}
return false;
}
bool Stack::pop(Item &item)
{
if(top > 0)
{
item = items[--top];
return true;
}
return false;
}
Item Stack::top()
{
if(top > 0)
{
return items[top-1];
}
return 0;
}
上面这段程序有几个巧妙的地方:
- 使用Item而非直接使用unsigned long为程序带来了很大的弹性;
- 可以使用专门函数去判断为空、或满,也可以在入栈或出栈时根据返回值判断;
- 我们不需要关心数组中每个元素,只需要关系其顶端索引即可;
动态数组实现栈
静态数组的缺陷在于我们可能无法自由创建一个制定大小的栈,基于这个原因,因此,可以实现一个动态数组版本的栈。但取而代之的是,我们需要自己释放空间。
typedef unsigned long Item;
class Stack
{
private:
Item *p;
int top;
int m_size;
public:
Stack(int size);
~Stack();
bool isempty();
bool isfull();
bool push(const Item &item);
bool pop();
Item Top();
};
Stack::Stack(int size)
{
m_size = size;
p = new Item[size];
top = 0;
}
Stack::~Stack()
{
delete[] p;
p = nullptr;
}
bool Stack::isempty()
{
return top == 0;
}
bool Stack::isfull()
{
return top == m_size;
}
bool Stack::push(const Item &item)
{
if (isfull())
return false;
p[top++] = item;
return true;
}
bool Stack::pop()
{
if (isempty())
return false;
top--;
return true;
}
Item Stack::top()
{
assert(!isempty());
return p[top-1];
}
链表实现栈
queue
queue与stack
queue和stack十分类似,但存在以下区别:
- queue是先进先出,stack是先进后出;
- queue提供front()函数用于返回首元素;
queue的实现
queue的原理图可以用下图来表示,有两个指针分别指向数组的第一个元素和最后一个元素后面的一个元素,这也就是队首、队尾。那么:
- 入队:rear指针向后移一位。
- 出队:front指针向后移一位。
基于上述原理,我们可以提供一个基于静态数组实现的queue版本,如下:
/*完成自定义的queue*/
#include <assert.h>
#define TYPE int
#define SIZE 100
static TYPE queue[SIZE];
static int front = 0;
static int rear = 0;
bool is_empty()
{
return front == rear;
}
bool is_full()
{
return rear == SIZE;
}
//入队
void en_queue(TYPE value)
{
if (!is_full())
{
queue[rear++] = value;
}
}
//出队
void de_queue()
{
if (!is_empty())
{
front++;
}
}
//获取首元素
TYPE first(void)
{
assert(!is_empty());
return queue[front];
}
//入队优化版本
void en_queue2(TYPE value)
{
if (is_full())
{
if (front == 0)
{
return;
}
else
{
for (int i = front; i < rear; ++i)
queue[i - front] = queue[i];
front = 0;
rear = SIZE - front;
}
}
else
{
queue[rear++] = value;
}
}
需要注意的是,我们提供了一个入队优化版本,这主要是因为如果front指针一直往后移,那么入队的时候前面的空间就浪费了,所以当判断队列已满时,我们可以将元素整体往前移,以避免浪费空间。