栈 Stack
在第一节中我们提到,数据结构的逻辑结构分为线性结构和非线性结构,其中线性结构又可分为一般线性结构、受限线性结构和推广结构。栈和队列从数据结构上看和线性表很类似,但是他们在普通线性表的基础上有额外的要求限制,因此说他们是受限的线性结构。这一节我们主要来了解什么是栈。
1 栈的基本概念
1.1 栈的定义
栈是只允许在表尾进行插入或删除操作的线性表。从定义上来看,栈依然属于线性表,只是它的操作仅限在表的一端执行。允许变化的一端通常称为栈顶,一般用表尾构成;而始终固定的一端被称为栈底,由表头构成。
由于栈只能在表尾端进行插入或删除操作,因此,最后插入的元素位于栈顶,在删除时会被第一个弹出栈表,就像一叠盘子,最后放置的盘子位于最顶端,也会被最先拿走。例如上图中的元素3是最后一个入栈的,也是第一个出栈的,栈的这种特性被称为“后入先出”(Last In First Out,LIFO)。
1.2 栈的基本操作
由于栈只有表尾一端可以操作,所以栈的基本操作大部分都是围绕表尾进行的。栈的主要操作如下:
主要操作 | 操作结果 |
---|---|
InitStack(&S) | 初始化一个空栈 S。 |
DestroyStack(&S) | 销毁栈,并释放栈 S 占用的存储空间。 |
ClearStack(&S) | 将 S 清空为空栈。 |
StackEmpty(S) | 判断栈 S 是否为空,若为空则返回 true,否则返回 false。 |
StackLength(S) | 返回栈 S 的元素个数,即栈的长度。 |
GetTop(S, &x) | 读取栈顶元素,若栈非空,则用 x 返回栈顶元素。 |
Push(&S, x) | 入栈,若栈 S 未满,则将 x 插入,使之成为新栈顶。 |
Pop(&S, &x) | 出栈,若栈 S 非空,则弹出栈顶元素,并用 x 返回。 |
2 栈的顺序表示和实现
栈是一种线性表,属于逻辑结构。同其他的线性表一样,栈也有两种不同的存储方式:顺序存储和链式存储。
2.1 顺序栈
顺序栈是采用顺序存储的栈,利用一组地址连续的存储单元来存放自栈顶到栈底的数据元素,同时设置一个指针指向栈顶元素。
静态顺序栈使用静态数组来实现(如上图所示),使用一个整型变量 top 来记录栈顶元素的位置,即元素在数组的下标。由于数组的第一位元素下标为0,因此一般将 top 指针指向 -1 来表示空栈。当有元素时, top 指针指向栈顶元素。
动态顺序栈使用动态数组来实现(如上图所示),和静态顺序栈最大的区别就是栈顶指针的表示不同。动态顺序栈一般有两个指针 base 和 top,一个指向栈底,一个指向栈顶。当栈表为空时,base 和 top 指向同一个位置;当栈表不为空时,top 始终指向栈顶元素的下一位(逻辑上的下一位,在图上表示则为栈顶元素的上方)。此时,栈的有效长度可以用 top-base 来获取。
2.2 顺序栈的实现
2.2.1 顺序栈类型定义
/********** 动态顺序栈的类型定义 **********/
#define INITSIZE 10
#define INCREMENT 10
typedef int ElemType;
typedef struct {
ElemType *base; // 栈底指针
ElemType *top; // 栈顶指针
int capacity; // 栈当前的最大容量
} SqStack;
2.2.2 主要操作的实现
栈的主要操作都是围绕表尾进行的,所以重点是处理栈顶指针 top 。详情见附录。
2.3 共享栈
共享栈是一种特殊的顺序栈,利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一维数组,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中部延伸,如下图所示:
和静态顺序栈一样,如果栈表不为空,栈顶指针指向栈顶元素;如果栈表为空,他们的栈顶指针分别等于 -1 和 MaxSize。当插入新元素时,都需要先移动栈顶指针,再赋值。当两个栈顶指针相邻时(B.top - A.top = 1),即代表共享栈已满。
共享栈的优点是可以更好地利用存储空间,只有在整个存储空间都被占满时才发生上溢(超出最大范围)。
2.3.1 共享栈类型定义
/********** 共享栈类型定义 **********/
#define MAXSIZE 10
typedef int ElemType;
typedef struct {
ElemType data[MAXSIZE];
int top1;
int top2;
} SharedStack;
2.3.2 主要操作的实现
共享栈的插入和删除操作需要先选择待操作的栈是哪一个,再开始执行操作。其余部分和顺序栈的操作一致,详情见附录。
3 栈的链式表示和实现
采用链式存储的栈称为链栈。链栈的优点是便于多个栈共享存储空间,且不存在栈满上溢的情况。
由于栈的操作仅在表的一端完成,正好符合单链表可以在表头快速执行插入和删除操作的特点,所以一般将栈顶指针指向单链表的表头。此时,链栈就是一个只能在表头进行操作的单链表。
链栈的实现基本和单链表一致,所以这里不再赘述。实现详情见附录。
相关章节
第一节 【绪论】数据结构的基本概念
第二节 【绪论】算法和算法评价
第三节 【线性表】线性表概述
第四节 【线性表】线性表的顺序表示和实现
第五节 【线性表】线性表的链式表示和实现
第六节 【线性表】双向链表、循环链表和静态链表
第七节 【栈和队列】栈
第八节 【栈和队列】栈的应用
第九节 【栈和队列】栈和递归
第十节 【栈和队列】队列
附录
顺序栈的实现
静态顺序栈的实现
/*
* File name: StaticSqStack.h
* -----------------------
* 静态顺序栈的实现。
*/
#ifndef _STATIC_SEQUENTIAL_STACK_h_
#define _STATIC_SEQUENTIAL_STACK_h_
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
/********** 静态顺序栈的类型定义 **********/
#define MAXSIZE 10
typedef int Elemtype;
typedef struct {
Elemtype data[MAXSIZE];
int top; // 栈顶指针,指向-1表示空栈
} SqStack;
/********** 静态顺序栈的实现 **********/
/*
* Function: 初始化操作
* ----------------------------
* 初始化一个空栈S
*/
void InitStack(SqStack &S){
S.top = -1; // 指针归零
}
/*
* Function: 销毁栈操作
* ----------------------------
* 静态数组的销毁不需要手动执行,因此无需销毁操作。
*/
void DestoryStack(SqStack &S){}
/*
* Function: 清空栈操作
* ----------------------------
* 将S清为空栈。
*/
void ClearStack(SqStack &S){
S.top = -1; // 指针归零
}
/*
* Function: 判空操作
* ----------------------------
* 判断栈S是否为空,若为空则返回true,否则返回false。
*/
bool StackEmpty(SqStack S){
return S.top==-1;
}
/*
* Function: 求栈长操作
* ----------------------------
* 返回栈S的元素个数,即栈的长度。
*/
int StackLength(SqStack S){
return S.top+1;
}
/*
* Function: 读取栈顶元素操作
* ----------------------------
* 读取栈顶元素,若栈非空,则用e返回栈顶元素。
*/
bool GetTop(SqStack S, Elemtype &e){
if (StackEmpty(S)){
return false;
}
e = S.data[S.top];
return true;
}
/*
* Function: 入栈操作
* ----------------------------
* 若栈S未满,则将e插入,使之成为新栈顶。
*/
bool Push(SqStack &S, Elemtype e){
if (StackLength(S)<MAXSIZE){ // 如果还有空间,则继续插入操作
S.data[++S.top] = e;
return true;
} else {
printf("Out of space!\n");
return false;
}
}
/*
* Function: 出栈操作
* ----------------------------
* 若栈S非空,则弹出栈顶元素,并用e返回。
*/
bool Pop(SqStack &S, Elemtype &e){
if (StackEmpty(S)){
return false;
} else {
e = S.data[S.top--];
return true;
}
}
/*
* Function: 输出操作
* ----------------------------
* 按从表头到表尾的顺序输出
*/
void Print(SqStack S){
for (int i=0;i<=S.top;i++){
printf("%d ", S.data[i]);
}
printf("\n");
}
#endif // _STATIC_SEQUENTIAL_STACK_h_
动态顺序栈的实现
/*
* File name: DynamicSqStack.h
* -----------------------
* 动态顺序栈的实现
*/
#ifndef _DYNAMIC_SEQUENTIAL_STACK_h_
#define _DYNAMIC_SEQUENTIAL_STACK_h_
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
/********** 动态顺序栈的类型定义 **********/
#define INITSIZE 10
#define INCREMENT 10
typedef int ElemType;
typedef struct {
ElemType *base; // 栈底指针
ElemType *top; // 栈顶指针
int capacity; // 栈当前的最大容量
} SqStack;
/********** 主要操作的实现 **********/
/*
* Function: 初始化操作
* ----------------------------
* 初始化一个空栈S
*/
void InitStack(SqStack &S){
S.capacity = INITSIZE; // 设定初始最大容量
S.base = new ElemType[S.capacity]; // 创建空栈表
S.top = S.base; // 将栈顶指针归零
}
/*
* Function: 销毁栈操作
* ----------------------------
* 销毁栈,并释放栈S占用的存储空间。
*/
void DestoryStack(SqStack &S){
S.top=S.base;
delete[] S.base;
S.capacity = 0;
}
/*
* Function: 清空栈操作
* ----------------------------
* 将S清为空栈。
*/
void ClearStack(SqStack &S){
S.top = S.base;
}
/*
* Function: 判空操作
* ----------------------------
* 判断栈S是否为空,若为空则返回true,否则返回false。
*/
bool StackEmpty(SqStack S){
return S.top==S.base;
}
/*
* Function: 求栈长操作
* ----------------------------
* 返回栈S的元素个数,即栈的长度。
*/
int StackLength(SqStack S){
return S.top-S.base;
}
/*
* Function: 读取栈顶元素操作
* ----------------------------
* 读取栈顶元素,若栈非空,则用e返回栈顶元素。
*/
bool GetTop(SqStack S, ElemType &e){
if (StackEmpty(S)){
return false;
}
e = *(S.top-1); // 指针向前移动一位,指向栈顶元素
return true;
}
/*
* Function: 入栈操作
* ----------------------------
* 若栈S未满,则将e插入,使之成为新栈顶。
*/
bool Push(SqStack &S, ElemType e){
if (StackLength(S)>=S.capacity){ // 如果没有空间,则首先进行扩容操作
// 重新分配base指向内存的大小
S.base = (ElemType *) realloc (S.base, (S.capacity+INCREMENT) * sizeof(ElemType));
S.top = S.base + S.capacity;
S.capacity += INCREMENT;
}
*S.top++ = e; // ++优先级高于*,所以可分解为:*S.top = e; S.top = S.top+1;
return true;
}
/*
* Function: 出栈操作
* ----------------------------
* 若栈S非空,则弹出栈顶元素,并用e返回。
*/
bool Pop(SqStack &S, ElemType &e){
if (StackEmpty(S)){
return false;
} else {
e = *--S.top;
return true;
}
}
/*
* Function: 输出操作
* ----------------------------
* 按从表头到表尾的顺序输出
*/
void Print(SqStack S){
for (int i=0;i<StackLength(S);i++){
printf("%d ", S.base[i]);
}
printf("\n");
}
#endif // _DYNAMIC_SEQUENTIAL_STACK_h_
顺序栈检测程序
/*
* File name: SenquentialStackTest.cpp
* -----------------------
* 检测静态顺序栈和动态顺序栈
*/
// 引用静态顺序栈
// #include "StaticSqStack.h"
// 引用动态顺序栈
#include "DynamicSqStack.h"
int main(){
SqStack S;
InitStack(S);
int n;
ElemType e;
char helpInfo[] =
"*****************************\n"
"Sequential Stack check: \n"
"\t-2-Quit\n"
"\t1-Push\n"
"\t2-Pop\n"
"\t3-Empty check\n"
"\t4-Get Length\n"
"\t5-Get top\n"
"\t6-Clear\n"
"\t7-Print\n"
"*****************************\n";
while (n!=-2){
printf(helpInfo);
scanf("%d", &n);
switch(n){
case 1:
printf("Enter the new value: ");
scanf("%d", &e);
Push(S, e);
break;
case 2:
if (Pop(S, e)){
printf("The last value %d is popped.\n", e);
} else {
printf("The Stack is empty.\n");
}
break;
case 3:
if (StackEmpty(S)){
printf("The Stack is empty.\n");
} else {
printf("The Stack is not empty.\n");
}
break;
case 4:
printf("The length of Stack is: %d\n", StackLength(S));
break;
case 5:
if (GetTop(S, e)){
printf("The top value is: %d\n", e);
} else {
printf("The Stack is empty.\n");
}
break;
case 6:
ClearStack(S);
printf("All cleared.\n");
break;
case 7:
printf("Stack is: ");
Print(S);
break;
}
}
DestoryStack(S);
return 0;
}
共享栈的实现
共享栈的实现
/*
* Filename: SharedStack.h
* -----------------------
* 实现共享栈。
*/
#ifndef _SHARED_STACK_h_
#define _SHARED_STACK_h_
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
/********** 共享栈类型定义 **********/
#define MAXSIZE 10
typedef int ElemType;
typedef struct {
ElemType data[MAXSIZE];
int top1;
int top2;
} SharedStack;
/********** 共享栈的实现 **********/
/*
* Function: 初始化操作
* ----------------------------
* 初始化一个空的共享栈S
*/
void InitStack(SharedStack &S){
S.top1 = -1; // 指针归零
S.top2 = MAXSIZE;
}
/*
* Function: 清空栈操作
* ----------------------------
* 将S清为空栈。
*/
void ClearStack(SharedStack &S){
InitStack(S); // 指针归零
}
/*
* Function: 判满操作
* ----------------------------
* 判断栈S是否已满,若已满则返回true,否则返回false。
*/
bool StackFull(SharedStack S){
return S.top2-S.top1==1;
}
/*
* Function: 求栈长操作
* ----------------------------
* 返回栈S的元素个数,即总的栈长。
*/
int StackLength(SharedStack S){
return (S.top1+1 + MAXSIZE-S.top2);
}
/*
* Function: 入栈操作
* ----------------------------
* 若栈S未满,则选择一个链表将e插入,使之成为新栈顶。
* 使用变量n(0或1)来指示选择插入的栈。
*/
bool Push(SharedStack &S, ElemType e, int n){
if (!StackFull(S)){ // 如果还有空间,则继续插入操作
if (n){ // 如果n为1,插入反向增长的栈
S.data[--S.top2] = e;
} else {
S.data[++S.top1] = e;
}
return true;
} else {
printf("Out of space!\n");
return false;
}
}
/*
* Function: 出栈操作
* ----------------------------
* 若栈S非空,则选择一个栈弹出栈顶元素,并用e返回。
* 使用变量n(0或1)来指示选择插入的栈。
*/
bool Pop(SharedStack &S, ElemType &e, int n){
if (n){ // 如果n为1,选择反向增长的栈
if (S.top2==MAXSIZE){ // 如果该栈为空,返回false
return false;
} else {
e = S.data[S.top2++];
return true;
}
} else { // 如果n为0,选择正向增长的栈
if (S.top1==-1){
return false;
} else {
e = S.data[S.top1--];
return true;
}
}
}
/*
* Function: 输出操作
* ----------------------------
* 将两个表的元素分别按栈内的顺序输出。
*/
void Print(SharedStack S){
for (int i=0;i<=S.top1;i++){
printf("%d ->", S.data[i]);
}
printf("\n");
for (int j=MAXSIZE-1;j>=S.top2;j--){
printf("%d ->", S.data[j]);
}
printf("\n");
}
#endif // _SHARED_STACK_h_
共享栈检测程序
/*
* Filename: SharedStack.cpp
* -----------------------
* 检测共享栈的各个方法。
*/
#include "SharedStack.h"
int main(){
SharedStack S;
InitStack(S);
int n;
ElemType e;
char helpInfo[] =
"*****************************\n"
"Sequential Stack check: \n"
"\t-2-Quit\n"
"\t1-Push\n"
"\t2-Pop\n"
"\t3-Full check\n"
"\t4-Get Length\n"
"\t5-Clear\n"
"\t6-Print\n"
"*****************************\n";
while (n!=-2){
printf(helpInfo);
scanf("%d", &n);
switch(n){
case 1:
printf("Enter the new value: ");
scanf("%d", &e);
printf("Choose a stack<0/1>: ");
scanf("%d", &n);
Push(S, e, n);
break;
case 2:
printf("Choose a stack<0/1>: ");
scanf("%d", &n);
if (Pop(S, e, n)){
printf("The last value %d is popped.\n", e);
} else {
printf("The Stack is empty.\n");
}
break;
case 3:
if (StackFull(S)){
printf("The Stack is full.\n");
} else {
printf("The Stack is not full.\n");
}
break;
case 4:
printf("The length of Stack is: %d\n", StackLength(S));
break;
case 5:
ClearStack(S);
printf("All cleared.\n");
break;
case 6:
printf("Stack is: \n");
Print(S);
break;
}
}
return 0;
}
链栈的实现
链栈的实现
/*
* Filename: LinkedStack.h
* -----------------------
* 使用单链表来实现链栈
*/
#ifndef _SINGLE_LINKED_LIST_h_
#define _SINGLE_LINKED_LIST_h_
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
/********** 链栈的结点的类型定义 **********/
typedef int ElemType;
typedef struct LNode{
ElemType data; // 数据域
struct LNode *next; // 指针域
} LNode, *LinkedStack;
/********** 主要操作的实现 **********/
/*
* Function: 初始化操作
* ----------------------------
* 初始化一个空栈
*/
void InitStack(LinkedStack &S){
S = new LNode;
S->next = NULL;
}
/*
* Function: 清空栈操作
* ----------------------------
* 将S清为空栈。
*/
void ClearStack(LinkedStack &S){
LinkedStack q; // 指向待删除结点
while (S->next!=NULL){ // 不断删除第一个结点
q = S->next;
S->next = q->next;
delete q;
}
}
/*
* Function: 销毁栈操作
* ----------------------------
* 销毁栈,并释放栈 S 占用的存储空间。
*/
void DestroyStack(LinkedStack &S){
ClearStack(S);
delete S;
}
/*
* Function: 判空操作
* ----------------------------
* 判断栈S是否为空,若为空则返回true,否则返回false。
*/
bool StackEmpty(LinkedStack S){
return !S->next;
}
/*
* Function: 求栈长操作
* ----------------------------
* 返回栈S的元素个数,即栈的长度。
*/
int StackLength(LinkedStack S){
int count=0;
LinkedStack tmp=S;
while (tmp->next!=NULL){
tmp = tmp->next;
count++;
}
return count;
}
/*
* Function: 读取栈顶元素操作
* ----------------------------
* 读取栈顶元素,若栈非空,则用e返回栈顶元素。
*/
bool GetTop(LinkedStack S, ElemType &e){
if (StackEmpty(S)){
return false;
}
e = S->next->data;
return true;
}
/*
* Function: 入栈操作
* ----------------------------
* 使用头插法将e插入,使之成为新栈顶。
*/
void Push(LinkedStack &S, ElemType e){
LNode *n = new LNode;
n->data = e;
n->next = S->next;
S->next = n;
}
/*
* Function: 出栈操作
* ----------------------------
* 若栈S非空,则弹出栈顶元素,并用e返回。
*/
bool Pop(LinkedStack &S, ElemType &e){
LinkedStack q;
if (StackEmpty(S)){
return false;
} else {
q = S->next;
e = q->data;
S->next = q->next;
delete q;
return true;
}
}
/*
* Function: 输出操作
* ----------------------------
* 由于单链表的特性,更方便按从栈尾到栈头的顺序输出。
* 后续可以用递归法来正向输出。
*/
void Print(LinkedStack S){
LinkedStack tmp=S;
while (tmp->next!=NULL){
tmp = tmp->next;
printf("%d <- ", tmp->data);
}
printf("base\n");
}
#endif // _SINGLE_LINKED_LIST_h_
链栈的检测程序
/*
* Filename: LinkedStackTest.cpp
* -----------------------
* 用来检测链栈各方法。
*/
#include "LinkedStack.h"
int main(){
LinkedStack S;
InitStack(S);
int n;
ElemType e;
char helpInfo[] =
"*****************************\n"
"Sequential Stack check: \n"
"\t-2-Quit\n"
"\t1-Push\n"
"\t2-Pop\n"
"\t3-Empty check\n"
"\t4-Get Length\n"
"\t5-Get top\n"
"\t6-Clear\n"
"\t7-Print\n"
"*****************************\n";
while (n!=-2){
printf(helpInfo);
scanf("%d", &n);
switch(n){
case 1:
printf("Enter the new value: ");
scanf("%d", &e);
Push(S, e);
break;
case 2:
if (Pop(S, e)){
printf("The last value %d is popped.\n", e);
} else {
printf("The Stack is empty.\n");
}
break;
case 3:
if (StackEmpty(S)){
printf("The Stack is empty.\n");
} else {
printf("The Stack is not empty.\n");
}
break;
case 4:
printf("The length of Stack is: %d\n", StackLength(S));
break;
case 5:
if (GetTop(S, e)){
printf("The top value is: %d\n", e);
} else {
printf("The Stack is empty.\n");
}
break;
case 6:
ClearStack(S);
printf("All cleared.\n");
break;
case 7:
printf("Stack is: ");
Print(S);
break;
}
}
DestoryStack(S);
return 0;
}