顺序栈(数组形式)的实现

🌈什么是栈?

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;
}

☀️测试结果

在这里插入图片描述

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值