数据结构初级:栈 ,什么是栈?如何用C语言实现栈?
1. 什么是栈?
栈:栈是一种数据结构,是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素的操作,进行数据插入和删除操作的一端被称为栈顶,另一端被成为栈底.栈中的元素遵循先进后出LIFO(Last In First Out)的原则.
我们可以把栈简单的想象成一个桶(因为想要把东西放入桶中或者从桶里拿走东西只能通过桶口),从桶的顶端放入东西则是入栈,从顶端拿走东西则是出栈.
并且,我们一般将给栈顶插入一个元素这个动作称为压栈(Push),将删除栈顶元素这个动作成为弹栈(Pop).
2. 栈的代码实现剖析
当我们想创建一个栈的时候,一般有两种数据结构可以选择,一个是顺序表(类似数组),另一个则是链表(有后继在内存中不连续存储的数据结构).根据我们上面所讲的栈的定义,用顺序表来实现可以说是非常方便了,因为顺序表的特殊性,它的内存在计算机中是连续存储的,并且在不改变位置的情况下,在顺序表的表尾插入和删除数据是相对来说比较方便的.
因此,下面以顺序表为基础来实现是个完整的栈.
首先我们需要分析,当我们需要用顺序表来实现栈的时候,应该用第一个放入元素的位置来当栈底,最后一个元素放入的位置来当栈顶方便插入和删除元素:
那么,我们首先要创建一个结构体,结构体中有 a: 顺序栈(用顺序表来实现的栈一般称为顺序栈), top:栈顶元素的下一个位置, capacity: 栈的空间大小
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top; //这里的top指向的是栈顶元素的下一个位置,当栈内为空时则为0
int capacity;
}Stack;
2.1 初始化栈
然后我们初始化一下顺序栈,因为栈内还没有任何元素,因此栈的头指针先置空为NULL;
top因为还没有任何元素,因此top指向0;
而capacity也是因为还没有任何元素也没有开辟空间,因此也是0;
//栈的初始化
void StackInit(Stack* ps)
{
assert(ps); //因为传进来的是结构体的指针,因此不能为NULL
ps->a = NULL;
ps->top = 0; //这里的top指向的是栈顶元素的下一个位置,当栈内为空时则为0
ps->capacity = 0; //初始内存为0
}
2.2 栈的销毁
初始化完成后一定要销毁栈,将所申请的空间还给操作系统
//栈的销毁
void StackDestroy(Stack* ps)
{
assert(ps); //断言
free(ps->a);
ps->a = NULL; //置空防止野指针
ps->capacity = 0; //销毁后为0
ps->top = 0; //销毁后为0
}
2.3 压栈(Push)
从栈顶插入一个元素到栈中
//入栈(从栈顶插入一个元素到栈中)
void StackPush(Stack* ps, STDataType data)
{
assert(ps);
//首先要判断一下栈中是否已满,若已满,则要扩容
if (ps->top == ps->capacity)
{
//若栈本身就是空栈,则先申请一部分内存,若非空且满了,则扩容到原来内存大小的两倍
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//申请内存,先创建一个临时数组,直接用realloc来扩容
STDataType* tmpa = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
if (tmpa == NULL)
{
perror("realloc false");
return;
}
ps->a = tmpa;
ps->a[ps->top ] = data;
ps->capacity = newCapacity;
}
else
{
//若栈中没满,则直接赋值
ps->a[ps->top] = data;
}
//赋值完成后栈中元素+1,则top向后走一位
ps->top++;
}
2.4 判断栈中是否为空
判断栈中是否为空,若为空则返回true,不为空则返回false
//判断栈中是否为空,若为空则返回true,不为空则返回false
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
2.5 获取栈顶的元素
注意:top指向的是栈顶元素的下一个位置,因此栈顶元素的位置应该在top-1处
//获取栈顶的元素
STDataType StackTop(Stack* ps)
{
assert(ps);
//若栈内为空,则无法获取
assert(!StackEmpty(ps));
//top指向的是栈顶元素的下一个位置,因此栈顶元素的位置应该在top-1处
return ps->a[ps->top - 1];
}
2.6 弹栈(Pop)
删除栈顶的元素
//弹栈(删除栈顶的元素)
void StackPop(Stack* ps)
{
assert(ps);
//若栈中为空则无法删除
assert(!StackEmpty(ps));
ps->top--;
}
2.7 获取栈内有效元素个数
//获取栈中有效元素个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
3. 顺序栈的源码(环境:vs2022)
Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<errno.h>
#include<assert.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}Stack;
//栈的初始化
void StackInit(Stack* ps);
//栈的销毁
void StackDestroy(Stack* ps);
//入栈(从栈顶插入一个元素到栈中)
void StackPush(Stack* ps, STDataType data);
//判断栈中是否为空,若为空则返回true,不为空则返回false
bool StackEmpty(Stack* ps);
//获取栈顶的元素
STDataType StackTop(Stack* ps);
//弹栈(删除栈顶的元素)
void StackPop(Stack* ps);
//获取栈中有效元素个数
int StackSize(Stack* ps);
Stack.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"
//栈的初始化
void StackInit(Stack* ps)
{
assert(ps); //因为传进来的是结构体的指针,因此不能为NULL
ps->a = NULL;
ps->top = 0; //这里的top指向的是栈顶元素的下一个位置,当栈内为空时则为0
ps->capacity = 0; //初始内存为0
}
//栈的销毁
void StackDestroy(Stack* ps)
{
assert(ps); //断言
free(ps->a);
ps->a = NULL; //置空防止野指针
ps->capacity = 0; //销毁后为0
ps->top = 0; //销毁后为0
}
//入栈(从栈顶插入一个元素到栈中)
void StackPush(Stack* ps, STDataType data)
{
assert(ps);
//首先要判断一下栈中是否已满,若已满,则要扩容
if (ps->top == ps->capacity)
{
//若栈本身就是空栈,则先申请一部分内存,若非空且满了,则扩容到原来内存大小的两倍
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//申请内存,先创建一个临时数组,直接用realloc来扩容
STDataType* tmpa = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
if (tmpa == NULL)
{
perror("realloc false");
return;
}
ps->a = tmpa;
ps->a[ps->top ] = data;
ps->capacity = newCapacity;
}
else
{
//若栈中没满,则直接赋值
ps->a[ps->top] = data;
}
//赋值完成后栈中元素+1,则top向后走一位
ps->top++;
}
//判断栈中是否为空,若为空则返回true,不为空则返回false
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
//获取栈顶的元素
STDataType StackTop(Stack* ps)
{
assert(ps);
//若栈内为空,则无法获取
assert(!StackEmpty(ps));
//top指向的是栈顶元素的下一个位置,因此栈顶元素的位置应该在top-1处
return ps->a[ps->top - 1];
}
//弹栈(删除栈顶的元素)
void StackPop(Stack* ps)
{
assert(ps);
//若栈中为空则无法删除
assert(!StackEmpty(ps));
ps->top--;
}
//获取栈中有效元素个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"
void test0()
{
//定义一个栈
Stack st;
StackInit(&st);
printf("此时栈内有%d个元素\n", StackSize(&st));
//分别插入 1,2,3,4,5
printf("分别插入 1,2,3,4,5\n");
StackPush(&st, 1);
StackPush(&st, 2);
StackPush(&st, 3);
StackPush(&st, 4);
StackPush(&st, 5);
//获取栈顶元素,打印后将之弹出
printf("获取栈顶元素,打印后将之弹出\n");
int i = 5;
while (i--)
{
printf("%d ", StackTop(&st));
StackPop(&st);
}
printf("\n");
printf("此时栈内有%d个元素\n", StackSize(&st));
}
int main()
{
test0();
return 0;
}
4. 运行结果
完结撒花❀❀❀~