C语言数据结构之堆栈(Stack)
栈的定义及原理
概述
- 栈是一个先进后出的数据结构,类似于一个弹匣,栈中的数据就相当于弹匣中的子弹
- 在栈中进行操作的时候 只预留了一个端口进行数据的存取操作,即无论是向数据结构中添加新的数据还是将其中的已有数据删除,只能从栈的一段进行操作
图解
- 数据入栈示意图
先将数据压入目前栈顶,也就是TOP指针指向的位置,然后再将指针向上移动,这样就完成了一个数据入栈的操作
由上图可以看出栈进行数据存储时的特点为:数据从栈顶进入,且一次只能进入一个数据,同理可知们进行数据出栈操作的时候,元素也只能从栈顶离开,即离开栈的每个元素在离开前必须是栈顶部的元素
所以出栈原理也就如下图所示
- 数据出栈示意图
先将TOP指针向下移动
然后将当前TOP指向的元素出栈就行
优缺点
-
优点:存取速度比堆要快,仅次于直接位于cpu中的寄存器,另外,栈数据是可以共享的
-
缺点:存在栈中的数据大小和生存期必须是确定的,缺乏灵活性
栈的主要操作
栈的数据操作同其他数据结构相同,都支持数据的增、删、遍历等操作
- 栈的初始化
InitStack(SqStack* S);
- 栈的销毁(包括释放内存)
DestoryStack(SqStack* S);
- 栈的数据清空(不释放内存)
ClearStack(SqStack* S);
- 判断栈是否为空
StackEmpty(SqStack* S);
- 返回栈中有效元素的数量
StackLength(SqStack* S);
- 返回栈顶元素
Gettop(SqStack* S,SElement *e);
- 压栈
Push(SqStack* S, SElement* e);
- 出栈
Pop(SqStack* S, SElement* e);
- 栈的遍历
StackTraverse(SqStack* S, void(Visit)(SElement));
实现代码
声明:为了保证代码的可读性和可移植性,编写了一个用于宏定义的Status.h的头文件,进行代码中所有需要用到的变量,望理解
1. Status.h文件代码
/*
* 注:
* 本次修订的目的包括降低耦合,争取每个模块都可以单独运行
* 但是Status这个模块会被所有其他模块引用,引用次数很多。
* 如果直接将Status模块复制到其它模块中,则会导致太多重复代码,
* 因此这里生成一个公共静态库让其它模块共享比较划算
*/
#ifndef STATUS_H
#define STATUS_H
#include <stdio.h>
/* 状态码 */
#define TRUE 1 // 真/是
#define FALSE 0 // 假/否
#define OK 1 // 通过/成功
#define ERROR 0 // 错误/失败
//系统中已有此状态码定义,要防止冲突
#ifndef OVERFLOW
#define OVERFLOW -2 //堆栈上溢
#endif
//系统中已有此状态码定义,要防止冲突
#ifndef NULL
#define NULL ((void*)0)
#endif
/* 状态码类型 */
typedef int Status;
/* 布尔类型 */
typedef int Boolean;
// 读取数据
int ReadData(FILE* fp, char* format, ...);
// 摁下回车键以继续运行
void PressEnterToContinue();
// 函数暂停一段时间,time不代表具体的时间
void Wait(long time);
// 跳过输入端的行分割符,如'\r'、'\n'、'\r\n'
void skipLineSeparator(FILE* fp);
#endif
2. SqStack.h文件代码
#ifndef SQSTACK_H
#define SQSTACK_H
#include<stdio.h>
#include<stdlib.h>
#include"Status.h"
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef int SElement;
typedef struct //使用结构体来声明一个栈的数据结构
{
SElement* base;
SElement* top;
int stacksize;
}SqStack;
//初始化一个栈
Status InitStack(SqStack* S);
//销毁结构,释放顺序栈的空间内存
Status DestoryStack(SqStack* S);
//将栈中的数据置空,不释放内存空间
Status ClearStack(SqStack* S);
//判断栈是否为空
Status StackEmpty(SqStack* S);
//返回栈中有效元素的数量
int StackLength(SqStack* S);
//返回栈顶的元素,用e进行接收
Status Gettop(SqStack* S,SElement *e);
//压栈
Status Push(SqStack* S, SElement* e);
//出栈
Status Pop(SqStack* S, SElement* e);
//遍历
Status StackTraverse(SqStack* S, void(Visit)(SElement));
#endif
3. SqStack.c函数实现文件代码
#include "SqStack.h"
Status InitStack(SqStack* S)
{
if (S == NULL) //无论哪个函数,都要判断栈为空的情况,并返回ERROR
{
return ERROR;
}
S->base = (SElement*)malloc(STACK_INIT_SIZE * sizeof(SElement)); //初始化的malloc和后面重新分配的realloc都是给base 分配空间
if (S->base == NULL) //分配后都要判断是否分配成功
{
exit(OVERFLOW);
}
S->top = S->base; //初始化的时候,top和base是指向一起的,再falsh动画中很明显
S->stacksize = STACK_INIT_SIZE; //将目前栈的可用空间设定为最大空间
return OK;
}
Status DestoryStack(SqStack* S)
{
if (S == NULL)
{
return ERROR;
}
free(S->base); //释放base的空间
S->base = NULL;
S->top = NULL; //base 与top全部指空
S->stacksize = 0; //可用空间置零
}
Status ClearStack(SqStack* S)
{
if (S = NULL || S->base == NULL) //栈为空或者是base为空都要返回ERROR
{
return ERROR;
}
S->top = S->base;
return OK;
}
Status StackEmpty(SqStack* S)
{
if (S->top == S->base)
{
return TRUE;
}
else return FALSE;
}
int StackLength(SqStack* S) //长度直接返回top与base的差
{
if (S->base == NULL)
{
return 0;
}
return (int)(S->top - S->base);
}
Status Gettop(SqStack* S, SElement* e)
{
if (S->base == NULL || S->top == S->base)
{
return 0;
}
*e = *(S->top - 1); //top指针的指向是为空的,他的-1后指向的位置才有数据
//元素e传递的是指针类型,所以要使用*e才能传递它的值
return OK;
}
Status Push(SqStack* S, SElement e)
{
if (S == NULL || *(S->base) == NULL)
{
return ERROR;
}
//如果栈已经满了,就将存储空间扩容
if (S->top - S->base >= S->stacksize){
S->base = (SElement*)realloc(S->base, (S->stacksize + STACKINCREMENT) * sizeof(SElement));
if (S->base == NULL)
{
exit(OVERFLOW);
}
S->top = S->base + S->stacksize;
S->stacksize = S->stacksize + STACKINCREMENT;
}
*(S->top++) = e; //进栈先赋值,栈顶指针再自增,先将top指向为空间的值设为传入的值,再移动top指针
return OK;
}
Status Pop(SqStack* S, SElement* e)
{
if (S == NULL || S->base == NULL)
{
return ERROR;
}
if (S->top == S->base)
{
return ERROR;
}
*e = *(--(*S).top); //与push不同,先移动指针再操作指针指向的值,因为由上面可知,top是指空的
return OK;
}
Status StackTraverse(SqStack *S, void(Visit)(SElement))
{
SElement* p = S->base;
if (S->base == NULL)
{
return ERROR;
}
while (p < S->top) //同样由上面可知,top指空的,所以一直遍历到top下面的元素
{
Visit(*p);
p = p + 1;
}
printf("\n");
return OK;
}
4. SqStack_main功能测试主函数代码
#include<stdio.h>
#include "SqStack.h"
#include "Status.h"
typedef int SElemType;
void PrintElem(SElement e);
void PrintElem(SElement e)
{
printf("%d", e);
}
//进行功能测试
int main()
{
SqStack s;
int i = 0;
SElement e;
printf("函数InitStack \n");
{
printf("初始化顺序栈 s . . .\n");
InitStack(&s);
}
printf("函数StackEmpty \n");
{
StackEmpty(&s) ? printf("s为空") : printf("s不为空");
}
printf("函数push \n");
{
for (int i = 1; i <= 6; i++)
{
Push(&s, 2 * i);
printf("将 \"%2d\" 压入栈s . . . \n", 2 * i);
}
}
printf("打印栈元素");
StackTraverse(&s,PrintElem);
return 0;
}
该主题文章将持续更新,后面会陆续发布《数据结构C语言(严蔚敏版)》中的所有数据结构的代码实现,以及部分算法习题的详解,求个关注