【初阶数据结构】栈
文章目录
一、概念与结构
二、栈的实现
栈的定义
1.初始化
2.入栈
3.判断栈是否为空
4.出栈
5.取栈顶元素
6.获取栈中有效元素个数
2.销毁
三、完整码源
总结
一、概念与结构
栈:
一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:
栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
栈底层结构选型:
从逻辑上,栈是线性的;从物理上,栈的实现是可以用数组或链表来实现。
已知栈是在栈顶插入数据和删除数据,如果使用链表来插入数据,这样每次都要从链表尾部进行插入,时间复杂度为O(n);如果使用数组来插入数据,在数组里就从尾部插入,时间复杂度为O(1).
因此,栈的实现⼀般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。
二、栈的实现
栈的定义
因为栈的底层用数组来实现,在前面学的顺序表底层也是数组,因此栈的定义跟顺序表的定义相似。(这篇文章:【初阶数据结构】顺序表-CSDN博客 详细介绍顺序表,对顺序表不了解,可先去学习顺序表)
//定义栈的数据结构
typedef int STDataType;
typedef struct Stack {
STDataType* arr;
int top;//栈中有效个数
int capacity;//栈中容量
}ST;
1.初始化
代码解析:
栈的初始化和顺序表的初始化大致相似,把数组置为NULL,在把数组容量capacity和有效个数top都置为0.
//初始化
void STInit(ST* ps)
{
assert(ps);
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
2.入栈
代码解析:
在入栈前,先写一个增容函数对指针ps指向的数组的大小进行判断,判断数组容量是否已满,数组容量满了就进行两倍扩容,扩容成功后在写入栈函数,对数据进行插入。
//方法:先写增容函数再写入栈函数
void STCheakCapacity(ST* ps)
{
assert(ps);
if (ps->top = ps->capacity)
{
//空间满时增容
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity);
if (tmp == NULL)
{
perror("realloc fail\n");
exit(1);
}
ps->arr = tmp;
ps->capacity = newCapacity;
}
}
void STPush(ST* ps, STDataType x)
{
STCheakCapacity(ps);
ps->arr[ps->top++] = x;
}
3.判断栈是否为空
代码解析:
通过bool函数判断栈是否为空,如果为空就返回ps->top==0.
//判断栈是否为空
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
4.出栈
代码解析:
先断言判断栈是否为空,在不为空的情况下只需要将ps指针指向的top--。(注:出栈不是把真正的栈顶元素进行删除,是改变top的位置,把元素拿出来)
//出栈
void STPop(ST* ps)
{
assert(!STEmpty(ps));
ps->top--;
}
5.取栈顶元素
代码解析:
先断言判断栈是否为空,在不为空的情况下直接返回栈顶元素。(注:因为top在栈顶的位置,在取元素需要top-1)
//取栈顶元素
STDataType STTop(ST* ps)
{
assert(!STEmpty(ps));
return ps->arr[ps->top - 1];
}
6.获取栈中有效元素个数
代码解析:
先断言判断栈是否为空,在不为空的情况下直接返回有效元素个数。
//获取栈中有效元素个数
int STsize(ST* ps)
{
assert(ps);
return ps->top;
}
2.销毁
代码解析:
栈的销毁就是手动释放空间,先判断指针ps指向的数组这块空间是否为空,不为空就对数组进行释放并置为NULL,并把数组容量capacity和有效个数top都置为0.
//销毁
void STDestroy(ST* ps)
{
assert(ps);
if (ps->arr)
free(ps->arr);
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
三.完整码源
Stack.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//定义栈的数据结构
typedef int STDataType;
typedef struct Stack {
STDataType* arr;//用数组来对栈的实现会更好,时间复杂度为O(1)
int top;//栈中有效个数
int capacity;//栈中容量
}ST;
//初始化:把数组置为NULL,让top=capacity=0;这样栈为空
void STInit(ST* ps);
//销毁:把数组free掉后,再对数组置为NULL,再让top=capacity=0;这样栈被销毁
void STDestroy(ST* ps);
//增容
void STCheakCapacity(ST* ps);
//入栈:当top=capacity时,说明空间已满增容后从栈顶直接插入数据只需对top++即可;
// 可以在入栈函数内直接写增容函数或写增容函数被调用到入栈函数内
void STPush(ST* ps, STDataType x);
//判断栈是否为空:当top=capacity=0时,栈为空,就返回0;不为空时,就执行下面的操作
bool STEmpty(ST* ps);
//出栈:断言后,不为空,就直接对top--即可
void STPop(ST* ps);
//取栈顶元素:直接取,top无需改变;断言后不为空,就直接return ps->arr[ps->top-1]
STDataType STTop(ST* ps);
//获取栈中有效元素个数:直接return ps->top
int STsize(ST* ps);
Stack.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"
//初始化
void STInit(ST* ps)
{
assert(ps);
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
//销毁
void STDestroy(ST* ps)
{
assert(ps);
if (ps->arr)
free(ps->arr);
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
//入栈
//方法:先写增容函数再写入栈函数
void STCheakCapacity(ST* ps)
{
assert(ps);
if (ps->top = ps->capacity)
{
//空间满时增容
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity);
if (tmp == NULL)
{
perror("realloc fail\n");
exit(1);
}
ps->arr = tmp;
ps->capacity = newCapacity;
}
}
void STPush(ST* ps, STDataType x)
{
STCheakCapacity(ps);
ps->arr[ps->top++] = x;
}
//判断栈是否为空
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
//出栈
void STPop(ST* ps)
{
assert(!STEmpty(ps));
ps->top--;
}
//取栈顶元素
STDataType STTop(ST* ps)
{
assert(!STEmpty(ps));
return ps->arr[ps->top - 1];
}
//获取栈中有效元素个数
int STsize(ST* ps)
{
assert(ps);
return ps->top;
}
Test.c
#include"Stack.h"
void test()
{
ST st;//创建栈
STInit(&st);//初始化
STPush(&st, 1);//入栈
STPush(&st, 2);
STPush(&st, 3);
STPush(&st, 4);
//STPop(&st);//出栈
//STPop(&st);
//STPop(&st);
//STPop(&st);
while (!STEmpty(&st))
{
STDataType top = STTop(&st);//取栈顶元素
printf("%d", top);
STPop(&st);
//int top = STTop(&st);//获取栈中有效元素个数
//printf("%d", top);
//STPop(&st);
}
STDestroy(&st);//销毁
}
int main()
{
test();
return 0;
}
总结
非常感谢大家阅读完这篇博客。希望这篇文章能够为您带来一些有价值的信息和启示。如果您发现有问题或者有建议,欢迎在评论区留言,我们一起交流学习。