数据结构入门:栈实现全解析

个人专栏:《数据结构-初阶》《经典OJ题目》《C语言》

欢迎各位大佬交流!

目录

一、栈的概念及结构

1、栈的基本概念

2、栈的结构

二、代码实现

0、初始化

1、入栈

2、出栈

3、返回栈顶元素

4、获取栈中有效元素个数

5、检测栈是否为空

6、销毁栈

一、栈的概念及结构

1、栈的基本概念

栈(Stack)是一种遵循后进先出(LIFO, Last In First Out)原则的线性数据结构;

仅允许在表的一端(称为栈顶,Top)进行插入(入栈,Push)和删除(出栈,Pop)操作,另一端称为栈底(Bottom);

2、栈的结构

栈的存储结构既可以用数组来实现,也能用链表来实现;

注意:此处我们说的链表是单链表!不考虑双向链表是因为双向链表空间较大,且双向链表功能复杂,性能较低!

那么到底是用数组来实现栈?还是链表呢?

我们通过一些操作的时间复杂度进行分析:

a、对于入栈而言:如果是链表实现的栈,首先要找到栈顶元素,显然需要O(N)级别的时间复杂度;

而如果是数组实现的栈,我们可以直接使用 top 这个下标,申请完空间之后直接尾插即可;

b、对于出栈而言:如果是链表实现的栈,首先要找到倒数第二个节点,显然依旧是O(N)级别的时间复杂度;

而如果是数组实现的栈,直接 top-- 即可;

综上,我们选择用数组来实现栈;

二、代码实现

注意:我们是用数组来实现栈

说明:由于栈的实现较为简单,因此所有函数都封装好之后再一起测试

首先来完成准备工作:同样创建三个文件,Stack,c、Stack.h、test.c;

接着在 .h 文件中包含头文件及结构体的定义

cpp 复制代码
//Stack.h
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int STDataType;

//定义栈结构体
typedef struct stack
{
	STDataType* a; //动态数组
	int top;       //栈顶
	int capacity;  //容量
}Stack;

0、初始化

初始化就是将结构体中的指针置为空,栈顶和容量均值为0即可;

这样就代表我们的 top = 0 表示无元素;

因此判断需要扩容的条件就是 top == capacity

而如果将 top 初始化为 -1,即代表我们的 top == -1 表示无元素;

此时判断需要扩容的条件是 top + 1 == capacity;

两者初始化均可;

我们采用 top == 0 作为标准

cpp 复制代码
//初始化
void STInit(Stack* ps)
{
    assert(ps);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}

1、入栈

分析逻辑:

我们需要结构体指针以及入栈元素x;

首先判断空间够不够;如果不够,则申请空间;

直接利用 top 表示栈顶元素进行插入即可

最后记得 top++

cpp 复制代码
//入栈
void STPush(Stack* ps, STDataType x)
{
    assert(ps);
	//判断空间够不够
	if (ps->top == ps->capacity)
	{
		//申请空间
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		STDataType* newa = (STDataType*)realloc(ps->a,sizeof(STDataType) * newcapacity);
		if (newa == NULL)
		{
			printf("realloc failed!\n");
			return;
		}
		ps->a = newa;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}

2、出栈

分析逻辑:

直接改变索引即可;即 top-- ;

cpp 复制代码
//出栈
void STPop(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);
	ps->top--;
}

3、返回栈顶元素

分析逻辑:

直接返回下标为 top - 1 的元素即可;

cpp 复制代码
//获取栈顶元素
STDataType StackTop(Stack* ps)
{
	assert(ps);
	return ps->a[ps->top - 1];
}

4、获取栈中有效元素个数

分析逻辑:

返回 top - 1 的值即可

cpp 复制代码
//获取栈中有效元素个数
int StackSize(Stack* ps)
{
    assert(ps);
	return ps->top;
}

5、检测栈是否为空

为空返回非0值,不为空返回0

分析逻辑:

判断 top 是否为0

cpp 复制代码
//检测栈是否为空
bool StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->top == 0;
}

6、销毁栈

分析逻辑:

先 free 指针指向的空间,接着将指针置为空;

最后将 top 和 capacity 置为0即可;

cpp 复制代码
//销毁栈
void StackDestroy(Stack* ps)
{
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

三、测试代码

我们在 test.c 文件中进行测试

1、测试入栈

cpp 复制代码
void test()
{
	Stack st;
	StackInit(&st);
	//入栈1 2 3 4 5
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);
	StackPush(&st, 5);

	while (!StackEmpty(&st))
	{
		printf("%d ", StackTop(&st));
		StackPop(&st);
	}
	printf("\n");

	StackDestroy(&st);
}

int main()
{
	test();
	return 0;
}

我们运行来看一下:

没有问题!

2、测试出栈

cpp 复制代码
void test()
{
	Stack st;
	StackInit(&st);
	//入栈1 2 3 4 5
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);
	StackPush(&st, 5);

	StackPop(&st);
	StackPop(&st);
	StackPop(&st);
	StackPop(&st);


	while (!StackEmpty(&st))
	{
		printf("%d ", StackTop(&st));
		StackPop(&st);
	}
	printf("\n");

	StackDestroy(&st);
}

出栈四次后,栈内还有一个元素1;

此时再执行两次出栈函数,看是否会引发断言

没有问题

3、测试获取栈顶元素

其实已经在入栈时测试过了

4、测试返回栈中有效元素个数

5、测试检测栈是否为空

如有不足之处恳请指出!!!

相关推荐
念何架构之路2 小时前
数组和切片实战
数据结构·算法·排序算法
重生之我是Java开发战士2 小时前
【数据结构】AVL树解析
数据结构·算法
小π军2 小时前
STL之multiset 常见API介绍
数据结构·c++·算法
Shan12052 小时前
浅谈:从经典算法到实战优化的案例分析
数据结构
Resistance丶未来2 小时前
DeepSeek-V4 新手快速上手指南
数据结构·python·gpt·算法·机器学习·claude·claude 4.6
光子物联单片机2 小时前
STM32传感器模块编程实践(二十)ESP8266实现MQTT连接OneNET上传温湿度数据
c语言·stm32·单片机·嵌入式硬件·mqtt
Lazionr2 小时前
数据结构队列详解:从概念到代码实现
c语言·数据结构
人道领域2 小时前
【LeetCode刷题日记】150.逆波兰表达式求值
java·数据结构·算法·leetcode
此生决int2 小时前
快速复习之数据结构篇——顺序表
数据结构