目录
一.概念与定义
栈是一种“后进先出”的数据结构。栈可以看做是一种特殊的线性表,只能在栈顶进行插入和删除操作。只有栈顶是允许操作的,而栈底是固定的。
在生活中,栈的结构其实很常见,例如手枪的弹夹,每次只能在顶部进行取子弹和装子弹,又例如浏览器的前进与后退,都是操作的我们最后一个打开的网页
压栈:栈的插⼊操作叫做压栈/⼊栈,⼊数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
以下用一个形象的图来说明
栈常见的有两种存储数据:
1.顺序存储结构:以顺序结构来实现的栈,由于其底层是数组,所以在进行行数据的存储取出时,非常方便,而不足是使用的空间大小是有限的,若空间不够需要重新开辟空间,难免造成空间的浪费。
2.链式存储结构:以链式结构来实现的栈,其底层是链表,因此在空间上并不会对空间的存储上造成浪费,但由于其每一个节点是由指针域和数值域构成,因此在内存上的开销会比较大
两者对比之下,顺序存储结构相对较为优秀,因为其数组的底层结构,在进行入栈出栈操作时时间复杂度为O(1),相比之下链表式存储结构就需要遍历到尾端,并且顺序存贮结构由于其数据为连续存储,不需要指针域,因此占用的内存会相对较少。
二.栈的实现
这里我们以顺序存储结构来实现栈
类似顺序表的定义,我们也需要一个指向数组首元素的指针arr,以及我们栈的容量capacity,以及我们的栈顶top
typedef int STDataType;
typedef struct Stack
{
STDataType* arr;
int top;//栈顶
int capacity;//容量
}ST;
2.1 Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* arr;
int top;//栈顶
int capacity;//容量
}ST;
// 初始化栈
void STInit(ST* ps);
// 销毁栈
void STDestroy(ST* ps);
// 入栈
void STPush(ST* ps, STDataType x);
//出栈
void STPop(ST* ps);
//取栈顶元素
STDataType STTop(ST* ps);
//获取栈中有效元素个数
int STSize(ST* ps);
//栈是否为空
bool STEmpty(ST* ps);
2.2 Stack.c
2.2.1 初始化
初始化栈,将指向栈的变量的地址传递过来使用一级指针接收,实现形参的改变影响到实参。初始化栈很简单,只需对其指针置空、空间大小和栈顶置0即可。
void STInit(ST* ps)
{
assert(ps);
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
2.2.2 入栈
由于栈的数据结构只能队处于栈顶的元素进行操作,因此并不像顺序表一样有头插尾插指定位置插入,但栈的入栈操作其实和顺序表的尾插十分相似。
在进行入栈操作时候,首先需要判断一下有效数据个数(栈顶top)和空间大小是否相等,所以使用if语句,若两者想的,那么则说明需要扩容那,否则不需要
在扩容操作首先需要判断当前的栈是否为空栈,若是空栈我们则需要先给其一片固定的空间大小,若不是空栈则继续扩容,因此我们使用三木操作符进行操作。
在开辟内存时一般使用realloc函数开辟,增容到原空间的2倍可以减少扩容操作的频率。如果每次只增加少量空间,那么在元素数量增长时,需要频繁进行扩容操作,这会降低性能。
若开辟成功则将new赋给ps->arr,以及空间大小的更新,否则打印报错信息。
最后将数据加入到当前的栈顶,并让top自增,即ps->arr[ps->top++] = x
void STPush(ST* ps,STDataType x)
{
assert(ps);
if (ps->capacity == ps->top)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* temp = (STDataType*)realloc(ps->arr,newcapacity * sizeof(STDataType));
if (temp == NULL)
{
perror("relloc fail");
exit(1);
}
ps->arr = temp;
ps->capacity = newcapacity;
}
ps->arr[ps->top++] = x;
}
2.2.3 栈的判空
在进行后续操作时候需要队栈进行判,因此我将其封装成一个函数,方便后续操作(这里也可以选择不封装,直接在后续的assert断言里)
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
2.2.4 出栈
出栈的操作类似于顺序比的尾删,而且更加简单,首先对栈进行判空,之后只需要将栈顶top自减即可,当栈顶位置减1后原栈顶的数据就被舍弃认为是一块无效数据。
void STPop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
ps->top--;
}
2.2.5 取栈顶元素
在取栈顶元素我们首先要对其进行判空,而后return返回我们的栈顶元素ps->arr[top-1]即可
这里需要主要我们取的是栈的元素所以返回值为我们栈中的元素的类型STDatatype
STDataType STTop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
return ps->arr[ps->top - 1];
}
2.2.6 获取栈有效数据个数
栈顶top指的是栈尾,数组有效数据里的最后一个位置,将栈顶元素返回时返回数组的top位置的数据即可。需要注意的是,使用top调用数组元素是需要减1,数组总是从零开始的,top栈顶是从1开始的。
int STSize(ST* ps)
{
assert(ps);
return ps->top;
}
2.2.7 栈的销毁
栈的空间是使用realloc函数开辟的,那便得使用对应得free对空间进行释放,让后将栈的空间大小和,栈顶置0即可。
这里我们需要注意的是若数组本身是空的那就不能对齐进行释放,在使用free释放前还需要使用if语句进行判断。是否为空。
void STDestroy(ST* ps)
{
assert(ps);
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
三.源码
3.1 Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* arr;
int top;//栈顶
int capacity;//容量
}ST;
// 初始化栈
void STInit(ST* ps);
// 销毁栈
void STDestroy(ST* ps);
// 入栈
void STPush(ST* ps, STDataType x);
//出栈
void STPop(ST* ps);
//取栈顶元素
STDataType STTop(ST* ps);
//获取栈中有效元素个数
int STSize(ST* ps);
//栈是否为空
bool STEmpty(ST* ps);
3.2 Stack,c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"
void STInit(ST* ps)
{
assert(ps);
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
void STPush(ST* ps,STDataType x)
{
assert(ps);
if (ps->capacity == ps->top)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* temp = (STDataType*)realloc(ps->arr,newcapacity * sizeof(STDataType));
if (temp == NULL)
{
perror("relloc fail");
exit(1);
}
ps->arr = temp;
ps->capacity = newcapacity;
}
ps->arr[ps->top++] = x;
}
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
void STPop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
ps->top--;
}
STDataType STTop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
return ps->arr[ps->top - 1];
}
int STSize(ST* ps)
{
assert(ps);
return ps->top;
}
void STDestroy(ST* ps)
{
assert(ps);
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->capacity = ps->top = 0;
}