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

个人专栏:《数据结构-初阶》《经典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、测试检测栈是否为空

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

相关推荐
ChoSeitaku8 分钟前
04.数组
java·开发语言·数据结构
HZ·湘怡11 分钟前
基于动态数组的栈(顺序栈)01
数据结构·算法
nazisami11 分钟前
红黑树详解
数据结构·c++·面向对象·红黑树
Chen_harmony13 分钟前
十八、C语言内存函数
c语言·算法
雁迟22 分钟前
第九章:列表 List 数据类型
数据结构·r语言
程序猿编码23 分钟前
并发SSH口令审计器:多进程协作的安全检测工具设计与原理(C/C++代码实现)
c语言·安全·ssh
m0_5474866630 分钟前
郑州轻工业大学《数据结构》期末试卷及答案2018-2022学年(AB卷)
数据结构·期末试卷·郑州轻工业大学
变量未定义~39 分钟前
字符串哈希匹配字符串
数据结构·算法·哈希算法
bnmoel1 小时前
数据结构深度剖析栈与队列:结构、边界实现与进出操作全解析
c语言·数据结构·算法··队列
ChoSeitaku1 小时前
06_可变参数_递归_类和对象_封装
java·数据结构·算法