目录
一、栈
1. 栈的概念及结构
栈(stack):是限定仅在表尾进行插入或删除操作的一种特殊的线性表。它的特殊之处就在于限制了线性表的插入和删除位置。把允许插入和删除的一端称为栈顶(top),另一端称为栈底;不含任何元素的栈称为空栈。栈又称为后进先出 (Last In First Out) 的线性表,简称LIFO结构。
压栈:栈的插入操作叫做进栈/压栈/入栈。入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
2. 栈的表示和实现
和线性表类似,栈也有两种存储表示方法,即顺序栈(栈的顺序存储结构)和链栈(栈的链式 存储结构)。这两种结构都可以实现,但相对而言数组结构更优一些。
2.1 栈的顺序存储表示的实现
顺序栈(栈的顺序存储结构):是利用一组地址连续的存储单元依次存放自栈底到栈顶的数 据元素,同时附设指针 top 指示栈顶元素在顺序栈中的位置。(以 top=0或top=-1来表示空 栈)。此处顺序栈的实现以 top=0来实现。
对于栈的实现,此处分模块进行
①Stack.h //创建头文件,这部分进行函数的声明及类型的定义
②Stack.c //源文件,主要是函数的实现
下面是所有要实现的函数的声明、类型的定义:都在头文件Stack.h中进行
//所需要包含的头文件
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
//栈的结构定义
typedef int STDataType; //STDataType类型根据实际情况而定,这里假设为int
typedef struct Stack
{
STDataType* a;
int top; //用于栈顶指针
int capacity; //栈当前可使用的最大容量
}SqStack;
//函数的声明
//初始化栈
void SqStackInit(SqStack* ps);
//栈的销毁
void SqStackDestroy(SqStack* ps);
//入栈
void SqStackPush(SqStack* ps, STDataType x);
//出栈
void SqStackPop(SqStack* ps);
//获取栈顶元素
STDataType SqStackTop(SqStack* ps);
//获取栈中有效元素个数
int SqStackSize(SqStack* ps);
//判断栈是否为空
bool SqStackEmpty(SqStack* ps);
以下函数的实现均在 Stack.c中进行。
栈的初始化
//初始化栈
void SqStackInit(SqStack* ps)
{
assert(ps); //以防ps为空指针
ps->a = NULL;
ps->top = 0; //在栈底的位置
ps->capacity = 0; //当前最大容量初始化为0
}
入栈操作
//入栈
void SqStackPush(SqStack* ps, STDataType x)
{
assert(ps);
//栈满动态扩容
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//容量扩容
//动态开辟空间,成功则返回空间首地址
STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);
//判断空间申请是否成功,失败退出
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x; //元素入栈
ps->top++; //栈顶指针加一
}
上面代码个别语句的解释如下:
上述代码中扩容的那条语句首先需要判断当前最大容量是否为0,若为0直接扩容多大,否则扩容当前容量的2倍; 然后就是动态开辟空间此处用的是 realloc 而没有用 malloc 原因是因为此处用realloc可以避免后面由于开辟的空间不足还要使用realloc进行动态扩容。这里由于一开始数组中元素为零,此时realloc和malloc的作用相同的,因此就避免先使用malloc,在使用realloc扩容的问题 .
出栈操作
//出栈
void SqStackPop(SqStack* ps)
{
assert(ps);
assert(ps->top > 0); //避免为空,assert更为暴力,也可if判断
--ps->top; //栈顶指针减一
}
获取栈顶元素
//获取栈顶元素
STDataType SqStackTop(SqStack* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->a[ps->top - 1]; //将栈顶元素返回
}
由于top指向栈底时的值为0,即top=0;top的值总比数组元素下标多一,因此,top-1指向栈顶元素
获取栈中有效元素的个数
//获取栈中有效元素个数
int SqStackSize(SqStack* ps)
{
assert(ps);
return ps->top; //返回当前元素的个数
}
栈的判空操作
//判断栈是否为空
bool SqStackEmpty(SqStack* ps)
{
assert(ps);
return ps->top == 0;
}
栈的销毁
//栈的销毁
void SqStackDestroy(SqStack* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
代码汇总
//Stack.h
//所需要包含的头文件
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
//栈的结构定义
typedef int STDataType; //STDataType类型根据实际情况而定,这里假设为int
typedef struct Stack
{
STDataType* a;
int top; //用于栈顶指针
int capacity; //栈当前可使用的最大容量
}SqStack;
//函数的声明
void SqStackInit(SqStack* ps);
void SqStackDestroy(SqStack* ps);
void SqStackPush(SqStack* ps, STDataType x);
void SqStackPop(SqStack* ps);
STDataType SqStackTop(SqStack* ps);
int SqStackSize(SqStack* ps);
bool SqStackEmpty(SqStack* ps);
//Stack.c
//初始化栈
void SqStackInit(SqStack* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
//入栈
void SqStackPush(SqStack* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
//出栈
void SqStackPop(SqStack* ps)
{
assert(ps);
assert(ps->top > 0);
--ps->top;
}
//获取栈顶元素
STDataType SqStackTop(SqStack* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->a[ps->top - 1];
}
//获取栈中有效元素个数
int SqStackSize(SqStack* ps)
{
assert(ps);
return ps->top;
}
//判断栈是否为空
bool SqStackEmpty(SqStack* ps)
{
assert(ps);
return ps->top == 0;
}
//栈的销毁
void SqStackDestroy(SqStack* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
2.2 栈的链式存储表示的实现
栈的链式存储结构,简称为链栈,它是运算受限的单链表,即插入和删除操作仅限制在表头(对于链式栈对应的是栈顶,即把栈顶放在单链表的头部)位置进行。既然把栈顶放在单链表的头部,那单链表中比常用的头结点也就失去了意义,通常对于链栈来说,是不需要头结点的;基本不存在栈满的情况。
对于空栈来说,链表原定义是头指针指向空,那么链栈的空就是 top=NULL.
对于链栈的实现与顺序栈一样分两个模块。
函数的声明、类型的定义: LinkStack.h
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int SElemType; // SElemType类型根据实际情况而定,这里假设为int
/* 链栈结构 */
typedef struct StackNode
{
SElemType data;
struct StackNode* next;
}StackNode;
typedef struct
{
StackNode* top;
int count;
}LinkStack;
// 构造一个空栈S
void InitStack(LinkStack* S);
//遍历并输出栈中元素
void StackTraverse(LinkStack* S);
// 把S置为空栈
void ClearStack(LinkStack* S);
// 若栈S为空栈,则返回TRUE,否则返回FALSE
bool StackEmpty(LinkStack* S);
// 返回S的元素个数,即栈的长度
int StackLength(LinkStack* S);
// 若栈不空,返回栈顶元素
SElemType GetTop(LinkStack* S);
// 插入元素e为新的栈顶元素
void StackPush(LinkStack* S, SElemType e);
// 出栈,若栈不空,则删除S的栈顶元素,并返回0;否则返回-1
int StackPop(LinkStack* S);
函数的实现:LinkStack.c
初始化为空栈
void InitStack(LinkStack* S)
{
S->top = (StackNode*)malloc(sizeof(StackNode));
if (S->top == NULL)
{
perror("malloc fail");
}
S->top = NULL;
S->count = 0;
}
入栈
// 插入元素e为新的栈顶元素
void StackPush(LinkStack* S, SElemType e)
{
StackNode* s = (StackNode*)malloc(sizeof(StackNode));
if (s == NULL)
{
perror("malloc fail");
exit(-1);
}
s->data = e;
s->next = S->top; // 把当前的栈顶元素赋值给新结点的直接后继
S->top = s; // 将新的结点s赋值给栈顶指针
S->count++;
}
出栈
// 出栈,若栈不空,则删除S的栈顶元素,并返回0;否则返回-1
int StackPop(LinkStack* S)
{
StackNode* p;
if (StackEmpty(S))
return -1;
p = S->top; // 将栈顶结点赋值给p
S->top = S->top->next; // 使得栈顶指针下移一位,指向后一结点
free(p); // 释放结点p
S->count--;
return 0;
}
栈的长度
// 返回S的元素个数,即栈的长度
int StackLength(LinkStack* S)
{
assert(S);
return S->count;
}
获取栈顶元素
// 若栈不空,返回栈顶元素
SElemType GetTop(LinkStack* S)
{
assert(S->top);
return S->top->data;
}
判断栈空
// 若栈S为空栈,则返回TRUE,否则返回FALSE
bool StackEmpty(LinkStack* S)
{
if (S->count == 0)
return true;
else
return false;
}
清空栈为空栈
// 把S置为空栈
void ClearStack(LinkStack* S)
{
StackNode* p, *q;
p = S->top;
while (p)
{
q = p;
p = p->next;
free(q);
}
S->count = 0;
}
栈的遍历
//遍历并输出栈中元素
void StackTraverse(LinkStack* S)
{
StackNode* p;
p = S->top;
while (p)
{
printf("%d ->", p->data);
p = p->next;
}
printf("\n");
}
代码汇总
//LinkStack.h
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int SElemType; // SElemType类型根据实际情况而定,这里假设为int
/* 链栈结构 */
typedef struct StackNode
{
SElemType data;
struct StackNode* next;
}StackNode;
typedef struct
{
StackNode* top;
int count;
}LinkStack;
void InitStack(LinkStack* S);
void StackTraverse(LinkStack* S);
void ClearStack(LinkStack* S);
bool StackEmpty(LinkStack* S);
int StackLength(LinkStack* S);
SElemType GetTop(LinkStack* S);
void StackPush(LinkStack* S, SElemType e);
int StackPop(LinkStack* S);
//
void InitStack(LinkStack* S)
{
S->top = (StackNode*)malloc(sizeof(StackNode));
if (S->top == NULL)
{
perror("malloc fail");
}
S->top = NULL;
S->count = 0;
}
void StackPush(LinkStack* S, SElemType e)
{
StackNode* s = (StackNode*)malloc(sizeof(StackNode));
if (s == NULL)
{
perror("malloc fail");
exit(-1);
}
s->data = e;
s->next = S->top; // 把当前的栈顶元素赋值给新结点的直接后继
S->top = s; // 将新的结点s赋值给栈顶指针
S->count++;
}
int StackPop(LinkStack* S)
{
StackNode* p;
if (StackEmpty(S))
return -1;
p = S->top; // 将栈顶结点赋值给p
S->top = S->top->next; // 使得栈顶指针下移一位,指向后一结点
free(p); // 释放结点p
S->count--;
return 0;
}
int StackLength(LinkStack* S)
{
assert(S);
return S->count;
}
SElemType GetTop(LinkStack* S)
{
assert(S->top);
return S->top->data;
}
bool StackEmpty(LinkStack* S)
{
if (S->count == 0)
return true;
else
return false;
}
void ClearStack(LinkStack* S)
{
StackNode* p, *q;
p = S->top;
while (p)
{
q = p;
p = p->next;
free(q);
}
S->count = 0;
}
void StackTraverse(LinkStack* S)
{
StackNode* p;
p = S->top;
while (p)
{
printf("%d ->", p->data);
p = p->next;
}
printf("\n");
}