栈的结构特点和操作
栈是限定只能在表的一端进行插入和删除的线性表,在表中,允许插入和删除的一端称为“栈顶”,不允许插入和删除的一端称为“栈底”。栈的元素进出是按照“先进后出,后进先出”的顺序进行。和线性表类似,栈也有两种存储方法顺序栈和链栈。
顺序栈
顺序栈是指利用顺序存储分配实现的栈。同时附设top指针指示栈顶元素在顺序栈中的位置。用一维数组来描述顺序栈中数据元素的存储区域,并预设一个最大的存储空间。对于top指针,由于数组的第一个元素的下标为0,所以初始化top=-1;表示是个空栈。输出栈内数据元素时,可以利用循环变量 i <= S.top;来输出。由于顺序栈的插入和删除只能在栈顶进行,所以顺序栈的一些相关操作比顺序表要简单的多。具体C语言实现代码如下:
#include<stdio.h>
#define TRUE 1
#define FALSE 0
/***************************************************************************************************
栈的顺序存储表示
****************************************************************************************************/
const int STACK_INIT_SIZE = 100; //顺序栈默认的初始分配最大空间量
const int STACKINCREMENT = 10; //默认的增补空间量
typedef struct {
int *elem; //存储数据的数组
int top; //栈顶指针
int stacksize; //当前分配的最大容量
int incrementsize; //约定的增补空间量
}SqStack;
/*****************************************************************************************************
建表
******************************************************************************************************/
void InitStack_Sq(SqStack &S, int maxsize = STACK_INIT_SIZE, int incresize = STACKINCREMENT)
{
//构造一个空栈S,初始化分配的最大空间为maxsize,预设的增补空间量为incresize
S.elem = new int[maxsize]; //为顺序栈分配最大容量为maxsize的数组空间
S.top = -1; //顺序栈中当前所含元素个数为零
S.stacksize = maxsize; //该顺序栈可容纳maxsize个数据元素
S.incrementsize = incresize; //需要时每次扩容incresize个元素的空间
}
/***************************************************************************************************
返回栈顶元素
****************************************************************************************************/
bool GetTop_Sq(SqStack S, int &e)
{
//若栈不空,用e返回栈顶元素并返回TRUE,否则返回FALSE
if (S.top == -1)
return FALSE;
e = S.elem[S.top];
return TRUE;
}
/*****************************************************************************************************
插入
******************************************************************************************************/
void incrementStacksize(SqStack &S)
{
int *a;
a = new int[S.stacksize + S.incrementsize];
for (int i = 0; i <=S.top; i++)
a[i] = S.elem[i];
delete[]S.elem;
S.elem = a;
S.stacksize = S.stacksize + S.incrementsize;
}
void Push_Sq(SqStack &S, int e)
{
//插入元素e为新的栈顶元素
if (S.top == S.stacksize - 1)
incrementStacksize(S); //若空间不够则扩容重新分配空间,类似于顺序表
S.elem[++S.top] = e;
}
/*****************************************************************************************************
删除
******************************************************************************************************/
bool Pop_Sq(SqStack &S, int &e)
{
//若栈不空,则删除S的栈顶元素,并用e返回其值,并返回TRUE,否则饭返回FALSE
if (S.top == -1)
return FALSE;
e = S.elem[S.top--];
return TRUE;
}
/******************************************************************************************************
主函数
*******************************************************************************************************/
void main()
{
int i;
int e1; //用于接收栈顶元素
int e2 = 11; //用于插入新的栈顶元素
int e3; //用于接收删除的栈顶元素
SqStack S;
InitStack_Sq(S);
for (i = 0; i < 10; i++)
{
S.elem[i] = i + 1;
S.top++;
}
printf("顺序栈中数据元素为:");
for (i = 0; i <= S.top; i++)
printf("%d ", S.elem[i]);
printf("\n");
if (GetTop_Sq(S, e1))
printf("返回此时栈顶元素为:%d\n", e1);
else
printf("这是一个空栈。\n");
Push_Sq(S, e2);
printf("插入新的栈顶元素后新栈的数据元素为:");
for (i = 0; i <= S.top; i++)
printf("%d ", S.elem[i]);
printf("\n");
if (Pop_Sq(S, e3))
{
printf("删除的栈顶元素为%d,删除后的栈内数据元素为:",e3);
for (i = 0; i <= S.top; i++)
printf("%d ", S.elem[i]);
}
else
printf("这是一个空栈。");
}
链栈
链栈是利用链式分配实现的栈。对于顺序栈,虽然在满员状态下可以重新分配空间扩容,但是也是不得已而为之,所以当无法预先估计栈可能达到的最大容量时,应该采用链栈更合适。链栈的结点结构和链表的结点结构相同,值得注意的是,链栈中指针的方向是从栈顶指向栈底。
同顺序栈一样,链栈相比于单链表来说操作也简单了不少。值得注意的是,对于需要用 e 返回数值的函数来说,定义函数时形参部分需要用 int &e;来表示。原因是函数是布尔类型的函数,若要让e的值改变,需要传地址进入函数,所以用&e。同时,对于链栈的赋值,可以用提前赋好值的数组,也可以用scanf函数进行赋值,本质上是不断调用 void Push_L(LinkStack &S, int e) 插入函数。
#include<stdio.h>
#define TRUE 1
#define FALSE 0
/**********************************************************************
定义结构类型
***********************************************************************/
typedef struct LNode {
int data;
struct LNode *next;
}LNode, *LinkStack;
/************************************************************************
建表
************************************************************************/
void InitStack_L(LinkStack &S)
{
//创建一个空的链栈,即设栈顶指针为空
S = NULL;
}
/*************************************************************************
插入
**************************************************************************/
void Push_L(LinkStack &S, int e)
{
//在链栈的栈顶插入新的栈顶元素e
LNode *p = new LNode; //为新的栈顶元素分配结点
p->data = e;
p->next = S; //插入新的栈顶元素
S = p; //修改栈顶指针
}
/***************************************************************************
删除
****************************************************************************/
bool Pop_L(LinkStack &S, int &e)
{
//若栈不空则删除栈顶元素,并用e返回其值,并返回TRUE,否则返回FALSE
if (S)
{
LNode *p;
p = S;
S = S->next; //修改栈顶指针
e = p->data; //返回栈顶元素
delete p; //释放结点空间
return TRUE;
}
else
return FALSE;
}
/******************************************************************************
从栈底到栈顶依次输出
*******************************************************************************/
void StackTraverse(LinkStack S)
{
LinkStack SS; //辅助栈 用来逆序输出
SS = NULL;
LNode *p; //用来遍历目标栈的指针
p = S;
while (p != NULL)
{
LNode *s;
s = new LNode; //新建结点s用于插入辅助栈SS中
s->data = p->data;
s->next = SS;
SS = s;
p = p->next;
}
p = SS;
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
}
/******************************************************************************
主函数
*******************************************************************************/
void main()
{
int i;
int e1; //用来接收新插入的数据元素
int e2; //用来返回删除的栈顶元素
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
LinkStack S;
LNode *p;
InitStack_L(S);
for (i = 0; i < 10; i++)
{
e1 = a[i];
Push_L(S, e1);
}
printf("此链栈的数据元素为:");
for (p = S; p != NULL; p = p->next)
printf("%d ", p->data);
printf("\n");
if (Pop_L(S, e2))
{
printf("删除的栈顶元素为%d,删除后的栈内数据元素为:", e2);
for (p = S; p != NULL; p = p->next)
printf("%d ", p->data);
printf("\n");
}
else
printf("这是一个空栈。");
printf("栈底到栈顶输出:");
StackTraverse(S);
}
本笔记所依据的教材为严薇敏版的《数据结构及应用算法教程》
所引用的图片来源于华中师范大学云课堂
所有代码在Visual Studio 2017上均可正常运行
如有错误欢迎指出