目录
栈的定义
形象比喻
栈的相关术语
栈的抽象数据类型(栈Stack的ADT)
顺序栈
顺序栈类的声明
顺序栈类成员函数的实现
基本效率分析
顺序栈的应用(小测试)
main.cpp
共享栈
双共享栈
链式栈
链式栈基本操作分析
链式栈类的声明
链式栈基本操作的实现
栈的定义
如果元素到达线性结构的时间越晚,离开的时间就越早,这种线性结构称为栈(Stack)或堆栈;
因为元素之间的关系是由到达、离开的时间决定的,因此栈通常被称为时间有序表。
而到达和离开的含义就是插入和删除操作,因此栈可以看作是插入和删除操作位置受限的线性表。
形象比喻
乒乓球盒的进球和出球,它遵循了最后进盒的球反而最先出盒,即所谓的后进先出(LIFO, Last In First Out)或先进后出(FILO, First In Last Out)结构。
栈的相关术语
栈的首部(元素最早到达的部分)称为栈底(bottom) ,栈结构的尾部(元素最晚到达的部分)称为栈顶(top) 。
为了保证栈的先进后出或后进先出的特点,元素的插入和删除操作都必须在栈顶进行。元素从栈顶删除的行为,称为出栈或者弹栈操作(pop); 元素在栈顶位置插入的行为,称为进栈或者压栈操作(push)。
读取栈顶元素数据值的操作,称为取栈顶内容操作(top)。
当栈中元素个数为零时,称为空栈。
栈的抽象数据类型(栈Stack的ADT)
Data: { xi | xiÎ ElemSet, i=1,2,3,……n, n > 0} 或 Φ; ElemSet为元素集合。
Relation: {<xi,xi+1>|xi,xi+1ÎElemSet, i=1,2,3,……n-1}, x1为栈底,xn为栈顶。
Operations:
操作 | 前提 | 结果 |
initialize | 无 | 栈初始化为一个空栈。 |
isEmpty | 无 | 栈Stack空返回true,否则返回false。 |
isFull | 无 | 栈Stack满返回true,否则返回false。 |
top | 栈Stack非空。 | 返回栈顶元素的值,栈顶元素不变。 |
push | 栈Stack非满,已知待插入的元素。 | 将该元素压栈,使其成为新的栈顶元素。 |
pop | 栈Stack非空。 | 将栈顶元素弹栈,该元素不再成为栈顶元素。 |
destroy | 无 | 释放栈Stack占用的所有空间。 |
顺序栈
栈的顺序存储即使用连续的空间存储栈中的元素。可以将栈底放在数组的0下标位置,进栈和出栈总是在栈的同一端(栈顶)进行,顺序方式存储的栈称为顺序栈。
栈空标志:top=-1
栈满标志:top=maxSize-1
顺序栈类的声明
顺序栈的描述:数组指针array,数组大小maxSize,栈顶下标Top。
class illegalSize{};
class outOfBound{};
template <class elemType>
class seqStack
{ private:
elemType *array; //栈存储数组,存放实际的数据元素。
int Top; //栈顶下标。
int maxSize; //栈中最多能存放的元素个数。
void doubleSpace();
public:
seqStack(int initSize = 100); //初始化顺序栈
bool isEmpty () { return ( Top == -1 ); } ; //栈空返回true,否则返回false。
bool isFull () { return (Top == maxSize-1); };//栈满返回true,否则返回false。
elemType top ();// 返回栈顶元素的值,不改变栈顶
void push (const elemType &e );//将元素e压入栈顶,使其成为新的栈顶。
void pop (); //将栈顶元素弹栈。
~seqStack(){ delete []array;}; //释放栈占用的动态数组
};
顺序栈类成员函数的实现
template <class elemType>
seqStack<elemType>::seqStack(int initSize)//初始化顺序栈
{ array = new elemType[initSize];
if (!array) throw illegalSize();
Top=-1; maxSize=initSize;
}
template <class elemType>
elemType seqStack<elemType>::top ()// 返回栈顶元素的值,不改变栈顶
{ if (isEmpty()) throw outOfBound();
return array[Top];
}
template <class elemType>
void seqStack<elemType>::push(const elemType &e )
//将元素e压入栈顶,使其成为新的栈顶元素。
{ if (isFull()) doubleSpace();
//栈满时从新分配2倍的空间,并将原空间内容拷入
array[++Top] = e; // 新结点放入新的栈顶位置。
}
template <class elemType>
void seqStack<elemType>::pop()//将栈顶元素弹栈。
{ if (Top==-1) throw outOfBound();
Top--;
}
基本效率分析
函数initialize(seqStack)、isEmpty、isFull、top、pop、destroy(~seqStack)的时间复杂度均为O(1)。
push因某时可能扩大空间,造成O(n)时间消耗,但按照“分期付款式”法,分摊到单次的插入操作,时间复杂度仍为O(1)。
顺序栈的应用(小测试)
编写程序从键盘上依次输入一串字符(以回车键结束)。要求将该串字符按照输入顺序的逆序在屏幕上输出。
在程序中可以建立一个顺序栈,将输入的字符依次入栈,最后再依次出栈,便能得到逆序结果。
main.cpp
#include <iostream>
#include "seqStack.h"
using namespace std;
int main()
{ // 声明一个栈。
seqStack<char> s;
char ctemp;
//从键盘输入若干字符(结束用回车),
//依照输入次序分别进栈
cout<<"Input the elements," <<press enter to an end: ";
ctemp = cin.get();
while ( ctemp!='\n')
{ s.push(ctemp); ctemp = cin.get(); }
//将栈中的结点逐个出栈,并输出到屏幕上。
cout<<"output the elements in the stack one by one:";
while (!s.isEmpty())
{
ctemp = s.top();
s.pop();
cout<<ctemp;
}
cout<<endl;
return 0;
}
共享栈
在实际应用中,有时需要同时使用多个数据类型相同的栈。
栈中的元素个数因进栈、出栈操作动态地变化,所有栈不一定同时达到栈满,有时一些栈满而另一些栈尚余空间。
为了提高空间使用率,可以在同一块连续的空间中设置多个栈。
多个栈间共享空间,这些栈称为“共享栈”。
共享栈的特点是每个栈拥有一个连续的小空间, 所有共享栈拥有一个大的连续空间。
top指向实际栈顶的后一个位置
假设有m个栈,第i个栈空的条件:top[i]=bottom[i];
第i个栈栈满条件为: 当i<m-1时,top[i]=bottom[i+1];
当i=m-1时,top[i]=maxSize。
双共享栈
可以将两个栈相向设置,即两个栈的栈底分别设置在连续空间的两个端点。
栈空的条件top[i]=bottom[i], i=0或1,两个栈不一定同时为空;
栈满的条件top[0]=top[1],即两个栈当中只剩下一个空位置的时候栈满,两个栈必定同时栈满。
链式栈
用不连续的空间和附加指针来存储元素及元素间的关系。
栈顶指针top指向处于栈顶的结点,即单链表中的首结点。
链式栈基本操作分析
进栈操作push的实现,按照以下操作顺序:
1)申请新的结点空间,data字段为进栈元素值,next字段指向首结点。
2)栈顶指向新结点。
出栈操作pop的实现,按照以下操作顺序:
1) 记住栈顶结点的地址。
2)将原栈顶的直接后继设为新的栈顶。
3)释放原来栈顶结点空间。
链式栈类的声明
class outOfBound{};
template <class elemType>
class linkStack;
template <class elemType>
class Node
{ friend class linkStack<elemType>;
private:
elemType data;
Node *next;
public:
Node(){next = NULL;}
Node(const elemType &x, Node *p=NULL)
{ data = x; next = p; }
};
template <class elemType>
class linkStack
{
private:
Node<elemType> *Top;
public:
linkStack(){ Top = NULL; }; //初始化栈,使其为空栈
bool isEmpty(){ return (Top==NULL); }; //栈为空返回true,否则返回false。
bool isFull(){ return false; }; //栈满true,否则false。结点空间不连续,故总能满足
elemType top();
void push(const elemType &e);
void pop();
~linkStack();
};
链式栈基本操作的实现
template <class elemType>
elemType linkStack<elemType>::top()
{
if (!Top) throw outOfBound();//栈空
return Top->data;
}
template <class elemType>
void linkStack<elemType>::push(const elemType &e)
{ Top = new Node<elemType>(e, Top); }
template <class elemType>
void linkStack<elemType>::pop()
{
Node<elemType> *tmp;
if (!Top) throw outOfBound();//栈空
tmp = Top; //用tmp记住原栈顶结点空间,用于弹栈后的空间释放
Top = Top->next; //实际将栈顶结点弹出栈
delete tmp;//释放原栈顶结点空间
}
template <class elemType>
linkStack<elemType>::~linkStack()
{
Node<elemType> *tmp;
while (Top)
{
tmp = Top;
Top=Top->next;
delete tmp;
}
}