- 语言:C语言
- 软件:Visual Studio 2022
- 笔记书籍:数据结构——用C语言描述
- 如有错误,感谢指正。若有侵权请联系博主
一、栈的定义
栈作为一种限定性线性表,是将线性表的插入和删除操作限制为仅在表的一端进行,通常将表种允许进行插入、删除操作的一端称为栈顶(top),栈顶的当前位置是动态变化的,他是一个称为栈顶指针的位置指示器来指示;将表的另一端称为栈底。当栈中没有元素时称为空栈。栈的插入操作称为进栈或入栈,删除操作称为出栈或退栈。栈又称为后进先出的线性表。
栈的示意图如下所示:

栈示意图
栈的基本操作除了进栈(栈顶插入)、出栈(删除栈顶)外,还有建立栈(栈的初始化)、判空、判满及取栈顶元素。
ADT Stack{
数据对象:可以是任何类型的数据,但必须性质相同。
结构关系:栈中数据元素之间是线性关系。
基本操作:
1.InitStack(S)
操作前提:S为未初始化的栈。
操作结果:将S初始化为空栈。
2.ClearStack(S)
操作前提:栈S已经存在。
操作结果:将栈S置成空栈。
3.IsEmpty(S)
操作前提:栈S已经存在。
操作结果:判栈S是否为空栈。若S为空栈,则返回TRUE,否则返回FALSE。
4.IsFull(S)
操作前提:栈S已经存在。
操作结果:判栈S是否为满栈。若S栈已满,则返回TRUE,否则返回FALSE。
5.Push(S,x)
操作前提:栈S已经存在。
操作结果:在S的顶部插入(亦称入栈)元素x。若S栈未满,将x插入栈顶位置,并返回TRUE,若栈已满,则返回FALSE,表示操作失败。
6.Pop(S,x)
操作前提:栈S已经存在。
操作结果:删除(亦称出栈)栈S的顶部元素,并用x带回该值,返回TRUE;若栈为空,返回值为FALSE,表示操作失败。
7.GetTop(S,x)
操作前提:栈S已经存在。
操作结果:取栈S的顶部元素赋给x所指向的单元,也称读栈。该操作与Pop(S,x)的不同之处在于,GetTop(S,x)不改变栈顶的位置。若栈为空,返回值为FALSE,表示操作失败。
}
栈作为一种特殊的线性表,在计算机中主要有两种基本存储结构:顺序存储结构和链式存储结构。采用顺序存储的栈简称为顺序栈,采用链式存储结构的栈简称为链栈。
二、顺序栈的表示和实现
1、栈的存储表示
#define Stack_Size 50
typedef struct {
StackElementType elem[Stack_Size]; //StackElementType为自己想使用的数据类型
int top;
}SeqStack;
说明:顺序栈是用顺序存储结构实现的栈,即利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时由于栈的操作的特殊性,还必须附设一个位置指针top(栈顶指针)来动态地指示栈顶元素在顺序栈中的位置。通常以top=-1表示空栈。
2、初始化栈
void InitStack(SeqStack* s) {
s->top = -1;
}
说明:构造一个空栈,给栈顶指针赋值-1。
3、将栈置为空栈
void ClearStack(SeqStack* s) {
memset(s->elem, NULL, sizeof(s->elem)); //将数组中每一位置NULL
s->top = -1;
printf("栈已置空");
}
说明:memset是一个初始化函数,作用是将某一块内存中的全部设置为指定的值memset(void *s,int c,size_t n);
- s指向要填充的内存块。
- c是要被设置的值。
- n是要被设置该值的字符数。
- 返回类型是一个指向存储区s的指针。
4、判断是否为空栈
int IsEmpty(SeqStack s) {
if (s.top == -1) return true;
return false;
}
说明:通过判断栈顶指针是否为-1,从而判断是否是空栈。若空返回true,非空返回false。
5、 判断是否为满栈
int IsFull(SeqStack s) {
if (s.top == (Stack_Size - 1)) return true;
return false;
}
说明:看栈顶指针是否到达数组开辟的大小,满足即满了,返回true,否则返回false。
6、入栈操作
int Push(SeqStack* s, StackElementType x) {
if (s->top == (Stack_Size - 1)) return false;
s->top++;
s->elem[s->top] = x;
return true;
}
说明:入栈前,先判断栈有没有满,满了返回false。未满,将栈顶指针往上加一,将数据存储数组中加一的位置。
7、出栈操作
int Pop(SeqStack* s, StackElementType* x) {
if (s->top == -1) return false;
*x = s->elem[s->top];
s->elem[s->top] = NULL;
s->top--;
return true;
}
说明:先判断是否为空栈,若是空栈返回false,否则,先将栈顶元素取出赋值给x,在将栈顶置空,栈顶指针往下移一位。
8、读栈顶操作
int GetTop(SeqStack* s, StackElementType* x) {
if (s->top == -1) return false;
*x = s->elem[s->top];
return true;
}
说明:只读栈顶,不改变栈顶,所以先判断是否为空栈,在将栈顶元素读出赋值给x。
9、顺序栈的全套代码
#include <iostream>
#include <stdio.h>
#define Stack_Size 50
#define StackElementType int
typedef struct {
StackElementType elem[Stack_Size];
int top;
}SeqStack;
/*初始化栈*/
void InitStack(SeqStack* s) {
s->top = -1;
}
/*将栈置为空栈*/
void ClearStack(SeqStack* s) {
memset(s->elem, NULL, sizeof(s->elem)); //将数组中每一位置NULL
s->top = -1;
printf("栈已置空");
}
/*判断栈是否为空栈*/
int IsEmpty(SeqStack s) {
if (s.top == -1) return true;
return false;
}
/*判断栈是否为满栈*/
int IsFull(SeqStack s) {
if (s.top == (Stack_Size - 1)) return true;
return false;
}
/*入栈操作*/
int Push(SeqStack* s, StackElementType x) {
if (s->top == (Stack_Size - 1)) return false;
s->top++;
s->elem[s->top] = x;
return true;
}
/*出栈操作*/
int Pop(SeqStack* s, StackElementType* x) {
if (s->top == -1) return false;
*x = s->elem[s->top];
s->elem[s->top] = NULL;
s->top--;
return true;
}
/*读栈顶操作*/
int GetTop(SeqStack* s, StackElementType* x) {
if (s->top == -1) return false;
*x = s->elem[s->top];
return true;
}
void SeqStack_text() {
SeqStack S, * Seq;
Seq = &S;
int* s, x;
s = &x;
InitStack(Seq);
printf("空栈返回1,非空返回0:%d\n", IsEmpty(S));
for (int i = 0; i < 55; i++) {
printf("第%d次操作结果,1为成功,0为失败:%d\n", i, Push(Seq, (i * 10)));
}
printf("满栈返回1,非满返回0:%d\n", IsFull(S));
for (int i = 0; i < 30; i++) {
Pop(Seq, s);
printf("第%d次取出栈顶的值为:%d\n", i, *s);
}
printf("空栈返回1,非空返回0:%d\n", IsEmpty(S));
for (int i = 0; i < 5; i++) {
GetTop(Seq, s);
printf("第%d次取出栈顶的值为:%d\n", i, *s);
}
printf("满栈返回1,非满返回0:%d\n", IsFull(S));
printf("清空操作\n");
ClearStack(Seq);
printf("空栈返回1,非空返回0:%d", IsEmpty(S));
}
10、测试结果
三、链栈的表示和实现
1、链栈的存储表示
#define StackElementType int
typedef struct NodeStack {
struct NodeStack* next;
StackElementType data;
}NodeStack,*NodeS;
说明:StackElementType用来替换int,若想换成其他类型,将int换了,printf里输出占位符也换即可。struct后边是结构体名称,typedef是代表这个结构体可以用大括号后边的也就是NodeStack来代替。NodeS表示定义一个结构体指针,用于指向单链表的头指针变量,便于为头指针变量开辟空间。
2、初始化链栈
void InitStack(NodeS* S) {
*S = (NodeStack*)malloc(sizeof(NodeStack));
(*S)->next = NULL;
printf("初始化完成");
}
说明:通过结构体指针将单链表的头指针变量传入函数中,malloc为头指针变量开辟空间,它的用法和顺序栈开辟空间是一样的。第三行是将开辟空间的头结点的指针域赋值为NULL。printf函数可以不要,仅仅是为了看代码运行情况。
3、将栈置为空栈
int ClearStack(NodeS S) {
if (S->next == NULL) return 0;
NodeStack* r,*p;
r = S->next;
while (r != NULL) {
p = r;
r = r->next;
free(p);
}
S->next = NULL;
}
说明:传入的是一个头指针变量,相当于NodeStack* S。先判断栈是否为空,即判断头结点的指针域是否为空。在定义两个结构体指针,一个用于指向结点,一个用于存储要释放的空间。while遍历循环释放空间,用free释放空间(有malloc出现的地方就要记得去释放空间)。最后将头结点置空。
4、判断是否为空栈
int IsEmpty(NodeS S) {
if (S->next == NULL) return true;
return false;
}
说明:即判断头节点的指针域是否为空即可。
5、判断是否为满栈
链表只要电脑内存足够就可以一直存数据。
6、入栈操作
int Push(NodeS S, StackElementType e) {
NodeStack* r = (NodeStack*)malloc(sizeof(NodeStack));
if (r == NULL) return 0;
r->data = e;
r->next = S->next;
S->next = r;
return 1;
}
说明:开辟一个新的空间作为新的一个结点。判断开辟的空间是否为空,若为空证明计算机空间全用完了,没法开辟新的空间,返回入栈0。否则,将函数传入的数据存入新开辟的结点中的数据域中,再将头结点指针域的数据传给新开辟的结点的指针域。最后将新开辟的结点存入头结点的指针域。
7、出栈操作
int Pop(NodeS S, StackElementType* e) {
if (S->next == NULL) return false;
NodeStack* r;
r = S->next;
*e = r->data;
S->next = S->next->next;
free(r);
return true;
}
说明:先判断栈是否为空栈,定义一个结构体指针,用于存放栈顶结点(即首元结点)。将首元结点赋值给定义的r,将首元结点数据赋值给参数e,将首元结点的指针域传给头结点。至此,栈顶结点(首元结点)的数据全拿出来了,最后将栈顶结点空间释放(防止内存泄漏)。
8、读栈顶操作
int GetTop(NodeS S, StackElementType* e) {
if (S->next == NULL) return false;
*e = S->next->data;
return true;
}
说明:读栈顶,只读不取出,故只要访问栈顶(首元结点的数据域)即可。先判断栈是否为空,再去取数据。
9、链栈的全套代码
#include <iostream>
#include <stdio.h>
#define StackElementType int
typedef struct NodeStack {
struct NodeStack* next;
StackElementType data;
}NodeStack,*NodeS;
/*初始化栈*/
void InitStack(NodeS* S) {
*S = (NodeStack*)malloc(sizeof(NodeStack));
(*S)->next = NULL;
printf("初始化完成");
}
/*将栈置为空栈*/
int ClearStack(NodeS S) {
if (S->next == NULL) return 0;
NodeStack* r,*p;
r = S->next;
while (r != NULL) {
p = r;
r = r->next;
free(p);
}
S->next = NULL;
}
/*判断栈是否为空栈*/
int IsEmpty(NodeS S) {
if (S->next == NULL) return true;
return false;
}
/*入栈操作*/
int Push(NodeS S, StackElementType e) {
NodeStack* r = (NodeStack*)malloc(sizeof(NodeStack));
if (r == NULL) return 0;
r->data = e;
r->next = S->next;
S->next = r;
return 1;
}
/*出栈操作*/
int Pop(NodeS S, StackElementType* e) {
if (S->next == NULL) return false;
NodeStack* r;
r = S->next;
*e = r->data;
S->next = S->next->next;
free(r);
return true;
}
/*读栈顶操作*/
int GetTop(NodeS S, StackElementType* e) {
if (S->next == NULL) return false;
*e = S->next->data;
return true;
}
/*打印栈表*/
void PrintStack(NodeS S) {
NodeStack* r;
r = S->next;
while (r != NULL) {
printf("%d ", r->data);
r = r->next;
}
}
void IntStack(NodeS S) {
int i,e;
printf("入栈前栈中的数据:");
PrintStack(S);
printf("\n你要入多少个数据:");
scanf_s("%d",&i);
while (i--) {
scanf_s("%d",&e);
Push(S,e);
}
printf("入栈后数据:");
PrintStack(S);
printf("\n");
}
void StackText() {
NodeS stack, * S;
S = &stack;
printf("-----------------初始化栈--------------\n");
InitStack(S);
printf("\n-----------------判断是否为空栈--------------\n");
printf("空栈1,非空0:%d", IsEmpty(stack));
printf("\n-----------------入栈操作--------------\n");
IntStack(stack);
printf("\n-----------------出栈操作--------------\n");
StackElementType* e,en;
e = &en;
for (int i = 0; i < 3; i++) {
Pop(stack, e);
printf("第%d次出栈数据为:%d\n", i,*e);
}
printf("出栈后,栈中数据为:");
PrintStack(stack);
printf("\n-----------------读栈顶操作--------------\n");
for (int i = 0; i < 3; i++) {
GetTop(stack, e);
printf("第%d次读栈顶数据为:%d\n", i,*e);
}
printf("\n-----------------将栈表置空--------------\n");
printf("置空前,空栈1,非空0:%d\n", IsEmpty(stack));
ClearStack(stack);
printf("置空后,空栈1,非空0:%d\n", IsEmpty(stack));
}
int main() {
StackText();
}