前言
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。 出栈:栈的删除操作叫出栈,出数据也是在栈顶(也就是只有一个口,即作入口又作出口)
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的 代价比较小。虽然单链表的头作栈顶,入栈出栈的效率也和数组一样高,但毕竟链表要多存一个指针,空间浪费较大,再者数组的的缓存命中率更高
因为顺序表的尾插尾删不用挪动前面的数据,而头插头删,需要挪动后面的大量数据,所以顺序表更适合尾插尾删,所以用数组实现栈时,一般用顺序表的尾作为栈顶,此时栈的数据的插入和删除,就是用顺序表的尾插尾删
因为单链表的尾插尾删需要取找尾,时间复杂度是O(n),所以单链表更适合头插头删,所以用单链表实现栈,一般用头节点作为栈顶,这样栈的数据的删除和插入就是单链表的头删头插,如果是双向循环链表则无所谓
stack.h文件
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>//在c语言中不支持直接使用bool类型,必须先引此头文件
//以下静态数组栈一般不实用,一般都用动态数组栈
//typedef int STDataType;
//#define N 10
//typedef struct Stack
//{
// STDataType _a[N];
// int _top; // 栈顶
//}Stack;
typedef int STDataType;
//创建动态数组栈结构体模板
typedef struct Stack
{
STDataType* a;
int top;//栈顶,类似栈内有效元素个数的统计
int capacity;//栈的容量
}ST;
//动态数组栈的初始化
void StackInit(ST* ps);
//动态数组栈的销毁,释放动态申请的空间
void StackDestroy(ST* ps);
//往栈顶放数据:入栈
void StackPush(ST* ps, STDataType x);
//删除栈顶元素:一般和StackTop函数配合使用,称为出栈
void StackPop(ST* ps);//一般在循环语句中起迭代作用
//获取栈顶元素
STDataType StackTop(ST* ps);
//获取栈内有效元素个数
int StackSize(ST* ps);
//判断栈是否为空
bool StackEmpty(ST* ps);
stack.c文件
#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"
void StackInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0; // 类似栈内有效元素个数的统计
ps->capacity = 0;
}
void StackDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
void StackPush(ST* ps, STDataType x)
{
assert(ps);
//判断是否需要扩容
if (ps->top == ps->capacity)
{
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//如果容量为0,则增容为4,如果不是,则直接增容为原容量的2倍
STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newCapacity);
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);//如果扩容失败就终止程序
}
ps->a = tmp;
ps->capacity = newCapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
void StackPop(ST* ps)//也就在循环结构中起迭代的作用
{
assert(ps);
assert(!StackEmpty(ps));//栈不为空才能删除数据
ps->top--;//和顺序表一样,实际上并未删除数据,只是后面我们通过top当作数组下标来访问栈内数据,给人造成数据被删除了的假象,
//只有后面调用StackDestroy才是真的清空数据了
}
STDataType StackTop(ST* ps)//取栈顶数据
{
assert(ps);
assert(!StackEmpty(ps));//数组栈不为空才能访问栈顶元素
return ps->a[ps->top - 1];//注意数组下标是从0开始的。所以这里下标是有效元素个数top-1
}
int StackSize(ST* ps)
{
assert(ps);
return ps->top;//有效元素个数
}
bool StackEmpty(ST* ps)
{
assert(ps);
/*if (ps->top == 0)
{
return true;
}
else
{
return false;
}*/
return ps->top == 0;
}
test.c文件
#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"
void TestStack1()
{
ST st;
StackInit(&st);
StackPush(&st, 1);
StackPush(&st, 2);
StackPush(&st, 3);
StackPush(&st, 4);
StackPop(&st);
StackPop(&st);
StackPop(&st);
StackPop(&st);
//StackPop(&st);
//printf("%d", StackTop(&st));
StackDestroy(&st);
}
void TestStack2()
{
ST st;
StackInit(&st);
StackPush(&st, 1);
StackPush(&st, 2);
StackPush(&st, 3);
StackPush(&st, 4);
printf("%d ", StackTop(&st));
StackPop(&st);
printf("%d ", StackTop(&st));
StackPop(&st);
StackPush(&st, 5);
StackPush(&st, 6);
//遍历数组栈内元素,必须遵循栈的特性:后进先出。也就是每次都访问栈顶数据
//就是StackPop函数一般和StackTop函数一起使用,称为出栈
while (!StackEmpty(&st))//栈不为空才能访问
{
printf("%d ", StackTop(&st));//打印栈顶元素
//删除栈顶元素(并未实际删除,只是top--了)才能让下一个元素成为新的栈顶,才能访问到下一个元素
StackPop(&st);//实际上就是迭代的作用
}
StackDestroy(&st);
}
int main()
{
TestStack2();
return 0;
}