🌈什么是栈?
1.抽象化具象:可以理解为一个细长的乒乓球筒,一端封闭,放球只能从另一端放入球,取出球时也只能从该端取出。先进的球最后出,后进的球最先出。
2.定义:栈是一种线性数据结构,栈中元素只能先进后出,最早进入的元素的位置叫做栈底,最后进入的元素的位置叫做栈顶。
3.实现方法:数组或链表。
4.问用数组和链式结构哪个更好?
数组方便访问元素、尾插尾删(空间不够就扩容);链表方便尾插(用一个tail指针记录尾节点),但不方便尾删(尾删需要遍历一遍链表找到尾节点的前一个节点)。如果非要用链表实现栈,则以头插头删的方式进行入栈和出栈最好,此时尾节点是栈底,头节点是栈顶。
🌈栈的基本操作
1.进栈:把新元素放入栈中,只能从栈顶一侧放入元素,新元素的位置是新的栈顶。
2.出栈:把元素从栈中弹出,只有栈顶元素才可出栈,出栈元素的前一个元素会成为新的栈顶。
🌈数组顺序栈的实现
🎈方式一:用两个指针分别指向栈底和栈顶
假设栈中每个元素储存的信息是包含年龄、身高、体重三个变量的结构体。
☀️1.定义声明部分
STACK_SIZE 4:最开始初始化开辟的空间大小,可以存放4个元素
STACK_INCREASE 2:如果空间不够用,则每次增加2个位置
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#define STACK_SIZE 4
#define STACK_INCREASE 2
typedef struct {
int age;
int height;
double weight;
}Student;
typedef struct {
Student* base;
Student* top;
int stackSize;
}Stack;
☀️2.初始化与销毁栈
用malloc动态开辟STACK_SIZE个空间,有动态开辟函数则一定会紧跟着释放动态内存函数free,因此初始化和销毁放在一起
//初始化一个栈
int InitStack(Stack* S) {
S->base = (Student*)malloc(sizeof(Student) * STACK_SIZE);
//判断是否申请空间成功
if (!S->base) exit(-1);
S->top = S->base;
S->stackSize = STACK_SIZE;
return 1;
}
//销毁栈
int DestroyStack(Stack* S) {
//判断栈是否存在
if (!S->base) return 0;
//如果存在,则free掉空间
free(S->base);
//栈相关信息还原
S->base = S->top = NULL;
S->stackSize = 0;
return 1;
}
☀️3.进栈
先判断是否有进栈空间,没有的话用realloc扩容。又由于realloc可能异地开辟新空间,因此在用base指针接收realloc空间的起始位置后,top也要在base的基础上变化。
//进栈
int Push(Stack* S, Student* stu) {
//判断是否有可进栈空间
//1.没有空间,扩容
if (S->top - S->base == S->stackSize) {
S->base = (Student*)realloc(S->base,sizeof(Student)
* (STACK_SIZE + STACK_INCREASE));
if (!S->base) exit(-1);
S->top = S->base + S->stackSize;
S->stackSize += STACK_INCREASE;
}
//2.有空间,
*S->top++ = *stu;//一定要注意加*,因为是将stu地址中的值拿出来赋给top指向的空间
return 1;
}
☀️4.出栈
stu指针是用来储存出栈的元素的
//出栈,同时存储出栈的数据
int Pop(Stack* S, Student* stu) {
//如果栈为空,返回0
if (S->top == S->base)return 0;
*stu = *(--S->top);
return 1;
}
☀️5.打印、遍历、计数
//打印一组学生信息
void Print(Student* s) {
//判断传入地址是否是空
if (!s)return;
//打印
printf("年龄:%5d,身高:%5d,体重:%5lf\n", s->age, s->height, s->weight);
}
//遍历
int DisplayStack(Stack* S) {
//判断栈是否是空的,为空不遍历
if (!S)return;
//如果栈不为空,逐个遍历
Student* p = S->base;
while (p != S->top) {
Print(p);
p++;
}
return 1;
}
//判断栈中有多少个数据
int Count(Stack* S) {
return(S->top - S->base);
}
}
☀️测试入栈、出栈
测试入栈后
//测试入栈
void testPush(Stack* S, int n) {
Student stu;
for (int i = 0;i < n;i++) {
printf("请输入学生的年龄、身高、体重:\n");
scanf("%d %d %lf", &stu.age, &stu.height, &stu.weight);
Push(S, &stu);
}
DisplayStack(S);
}
//测试出栈
void testPop(Stack* S,Student* stu) {
Pop(S, stu);
DisplayStack(S);
printf("出栈的元素:");
Print(stu);
printf("此时栈中元素个数:%d",Count(S));
}
int main() {
Stack S;
InitStack(&S);
testPush(&S,5);
printf("**********\n");
Student stu;
testPop(&S, &stu);
DestroyStack(&S);
return 0;
}
☀️测试结果
🎈方式二:只用一个指针指向栈底
假设栈中每个元素只包含一个DataType类型的数据。
☀️1.定义声明部分(list.h)
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int DataType;
typedef struct Stack {
DataType* a;
int top;
int capacity;
}Stack;
☀️2.初始化与销毁栈
void InitStack(Stack* ps) {
assert(ps);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
void Destroy(Stack* ps) {
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
☀️3.进栈
void Push(Stack* ps,DataType x) {
//传入指针必须合法
assert(ps);
//判断是否有足够空间入栈,空间不够则扩容
if (ps->top == ps->capacity) {
ps->capacity =ps->capacity== 0 ? 4 : ps->capacity * 2;
DataType* tmp = realloc(ps->a, sizeof(Stack) * ps->capacity);
if (tmp == NULL) {
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
}
ps->a[ps->top++] = x;
}
注意:
1.对ps中的capacity成员赋值时,如果初次入栈capacity为0时,给capacity赋值为4,否则就是原capacity的二倍。
2.动态申请内存时,为何要用realloc而不是malloc?
答:当realloc的第一个参数为空指针的话,其功能和malloc一样。扩容时不管栈为不为空,扩容时不管栈为不为空,都可以用该逻辑。
☀️4.出栈
void Pop(Stack* ps) {
//传入指针必须合法
assert(ps);
//栈内不能没有元素
assert(ps->top>0);
ps->top--;
}
☀️5.得到栈顶元素、判空
DataType GetTopVal(Stack* ps) {
//传入指针必须合法
assert(ps);
//栈内不能没有元素
assert(ps->top > 0);
return ps->a[ps->top-1];
}
bool IsEmpty(Stack* ps) {
assert(ps);
return ps->top == 0;
}
☀️测试
测试思路:先入栈5个元素,再依次出栈并打印出出栈元素。出栈顺序应该与入栈顺序相反。
void test() {
Stack st;
InitStack(&st);
Push(&st, 1);
Push(&st, 2);
Push(&st, 3);
Push(&st, 4);
Push(&st, 5);
while (!IsEmpty(&st)) {
printf("%d ", GetTopVal(&st));
Pop(&st);
}
Destroy(&st);
}
int main() {
test();
return 0;
}