【数据结构初阶】五、线性表中的栈(顺序表实现栈)

=========================================================================

相关代码gitee自取:

C语言学习日记: 加油努力 (gitee.com)

=========================================================================

接上期:

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表)_高高的胖子的博客-CSDN博客

=========================================================================

1 . 栈(Stack)

栈的概念和结构:

栈的概念

  • 一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作
  • 进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底
  • 栈中的数据元素遵守
    后进先出(LIFO -- Last In First Out)的原则 -- 后进入的元素会先出来

压栈和出栈

  • 栈的插入操作叫做进栈/压栈/入栈 -- Push
    入数据在栈顶
  • 栈的删除操作叫做出栈 -- Pop
    出数据也在栈顶

栈的结构

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

2 . 栈的实现

使用 顺序表(数组) 或 链表(链式结构) 都可以实现栈,

使用链表的话,可以使用 尾插(头插) 和 尾删(头删) 实现 栈的"后进先出",

( 尾插 -- 压栈 ; 尾删 -- 出栈 )

使用顺序表同样可以使用 尾插 和 尾删 实现,

但顺序表的尾插和尾删操作效率较高,处理数据时的缓存利用率也较高,

所以下面用顺序表(数组)实现栈:

(详细解释在图片的注释中,代码分文件放下一标题处)

实现具体功能前的准备工作

  • 包含之后会用到的头函数
  • 创建栈数据类型 -- 栈中存储数据的类型
  • 创建栈结构体(类型) -- 类型中应有控制栈内元素指针、栈顶值、栈大小
图示

---------------------------------------------------------------------------------------------

STInit函数 -- 对栈类型进行初始化

  • assert断言栈类型指针不为空
  • 将栈内元素控制指针置为空,
    将栈容量(大小)置为0,
    将栈顶值定义为0
图示

---------------------------------------------------------------------------------------------

STDestroy函数 -- 销毁栈类型

  • assert断言栈类型指针不为空
  • 释放栈内元素控制指针并置空,
    栈容量置为0,
    栈顶值置为0
图示

---------------------------------------------------------------------------------------------

STPush函数 -- 进行压栈

  • assert断言栈类型指针不为空
  • 为栈开辟动态空间并进行检查
  • 开辟成功后,栈内元素控制指针指向开辟的空间,
    重新设置capacity栈容量
  • 将压栈的值(x)放入栈
  • 调整栈顶值"下标"top的位置
图示

---------------------------------------------------------------------------------------------

STPop函数 -- 进行出栈

  • assert断言栈类型指针不为空,
    assert断言栈不为空
  • 把栈顶向下移动一个单位即可实现"删除"(出栈)
图示

---------------------------------------------------------------------------------------------

STTop函数 -- 获取栈顶元素

  • assert断言栈类型指针不为空,
    assert断言栈不为空
  • 返回栈顶元素
图示

---------------------------------------------------------------------------------------------

STSize函数 -- 计算栈中元素个数

  • assert断言栈类型指针不为空
  • 返回top,即栈中元素个数
图示

---------------------------------------------------------------------------------------------

STEmpty函数 -- 判断栈是否为空

  • assert断言栈类型指针不为空
  • 判断top栈顶值是否为空,
    是则返回true,否则返回false
图示

---------------------------------------------------------------------------------------------

总体测试:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

3 . 对应代码

Stack.h -- 栈头文件

cpp 复制代码
#pragma once

//包含之后需要的头文件:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

//动态顺序表:

//定义栈的栈顶数据元素:
typedef int STDataType;
  
//定义栈类型:
typedef struct Stack
{
	//为栈开辟动态空间时的头指针:
	//控制栈内元素的指针
	STDataType* a;

	//栈顶值(用于定义栈顶):
	int top;
	//类似于数组下标

	//栈存放数据容量(栈的大小):
	int capacity;

}ST; //重命名为ST


//静态顺序表:
//define N 10
//struct Stack
//{
//	int a[N];
//	int top;
//};


//初始化栈函数 -- 对栈类型进行初始化
//接收栈类型指针
void STInit(ST* ps);

//销毁栈函数 -- 销毁栈类型
//接收栈类型指针
void STDestroy(ST* ps);

//因为只有压栈和出栈操作
//只操作栈顶元素,所以没有
//头插(尾插)头删(头删)等其他操作

//压栈函数 -- 进行压栈
//接收栈类型指针(ps)、进行压栈的值(x)
void STPush(ST* ps, STDataType x);

//出栈函数 -- 进行出栈
//接收栈类型指针
void STPop(ST* ps);

//栈顶元素函数 -- 获取栈顶元素
//接收栈类型指针
STDataType STTop(ST* ps);

//栈中元素函数 --计算栈中元素个数
//接收栈类型指针
int STSize(ST* ps);

//判空函数 -- 判断栈是否为空
//接收栈类型指针
bool STEmpty(ST* ps);

---------------------------------------------------------------------------------------------

Stack.c -- 栈函数实现文件

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

//包含栈头文件:
#include "Stack.h"

//初始化栈函数 -- 对栈类型进行初始化
//接收栈类型指针
void STInit(ST* ps)
{
	//assert断言栈类型指针不为空:
	assert(ps != NULL);

	//将栈内元素控制指针置为空:
	ps->a = NULL;
	//将栈容量(大小)置为0:
	ps->capacity = 0;
	//将栈顶值定义为0:
	ps->top = 0;
}



//销毁栈函数 -- 销毁栈类型
//接收栈类型指针
void STDestroy(ST* ps)
{
	//assert断言栈类型指针不为空:
	assert(ps != NULL);

	//释放栈内元素控制指针:
	free(ps->a);
	//并将其置为空:
	ps->a = NULL;
	//栈容量置为0:
	ps->capacity = 0;
	//栈顶值置为0:
	ps->top = 0;
}



//压栈函数 -- 进行压栈
//接收栈类型指针(ps)、进行压栈的值(x)
void STPush(ST* ps, STDataType x)
{
	//assert断言栈类型指针不为空:
	assert(ps != NULL);

	//为栈开辟动态空间:
	if (ps->top == ps->capacity)
		//栈顶值 等于 栈大小
		//说明空间不够,需要扩容
	{
		//只有压栈时容量会增大可能需要扩容
		//只有这个函数会进行扩容操作,
		//所以没必要单独写一个扩容函数
		
		//进行扩容:
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		//因为栈容量capacity初始化为0,
		//所以可以使用三目操作符进行增容:
		//ps->capacity == 0 ? 4 : ps->capacity * 2
		//如果为0则直接增容到4,不为0则增容2倍
	
		//开辟动态空间:
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType)*newCapacity);
		//这里直接使用realloc进行动态空间开辟
		//如果realloc函数接收头指针(第一个参数)为空,
		//那么它的作用相当于malloc函数

		//对开辟空间进行检查:
		if (tmp == NULL)
			//返回空指针,开辟失败:
		{
			//打印错误信息:
			perror("realloc fail");
			//终止程序:
			exit(-1);
		}

		//开辟成功后,
		//栈内元素控制指针指向开辟空间:
		ps->a = tmp;
		
		//重新设置capacity栈容量:
		ps->capacity = newCapacity;
	}

	//将压栈的值(x)放入栈:
	ps->a[ps->top] = x; 
	//上面以a为头开辟连续的空间,
	//所以a可以看作一个数组名使用(?)
	//通过数组下标放入值
	
	//再调整栈顶值"下标"位置:
	ps->top++;
}



//出栈函数 -- 进行出栈
//接收栈类型指针
void STPop(ST* ps)
{
	//assert断言栈类型指针不为空:
	assert(ps != NULL);
	//assert断言栈顶top到了栈底就不能继续出栈了
	assert(ps->top > 0); //栈不为空

	//出栈只要栈顶top--
	//把栈顶向下移动一个单位即可实现"删除"(出栈):
	--ps->top;
}



//栈顶元素函数 -- 获取栈顶元素
//接收栈类型指针
STDataType STTop(ST* ps)
{
	//assert断言栈类型指针不为空:
	assert(ps != NULL);
	//assert断言栈为空的话就不能找栈顶元素了
	assert(ps->top > 0);

	//返回栈顶元素:
	return ps->a[ps->top - 1];
	//top即栈中元素个数:
	//top从0开始,压栈后top++,先赋值再++
	//top永远在栈顶元素的下一个位置
	//所以要获得栈顶元素就要top-1到栈顶元素位置
}



//栈中元素函数 --计算栈中元素个数
//接收栈类型指针
int STSize(ST* ps)
{
	//assert断言栈类型指针不为空:
	assert(ps != NULL);

	//top即栈中元素个数:
	//top从0开始,压栈后top++,先赋值再++
	//top永远在栈顶元素的下一个位置
	return ps->top;
}



//判空函数 -- 判断栈是否为空
//接收栈类型指针
bool STEmpty(ST* ps)
{
	//assert断言栈类型指针不为空:
	assert(ps != NULL);

	//如果top为0
	//说明栈中没有元素
	return ps->top == 0; 
	//top为0 -> 栈为空 -> 返回true
	//top不为0 -> 栈不为空 -> 返回false
}

---------------------------------------------------------------------------------------------

Test.c -- 栈测试文件

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

//包含栈头文件:
#include "Stack.h"

//测试函数:
void TestStack()
{
	//创建一个栈类型:
	ST st;
	//对其进行初始化:
	STInit(&st);
	//使用压栈往栈中增加元素:
	STPush(&st, 1);
	STPush(&st, 2);
	STPush(&st, 3);
	STPush(&st, 4);
	STPush(&st, 5);
	//元素大于4个再次进行扩容

	//打印当前栈中元素个数:
	printf("目前栈内元素个数为:%d\n", STSize(&st));
	//换行:
	printf("\n");

	//使用while循环:
	//打印栈顶元素再出栈,循环此操作:
	//证明栈的后进先出原则
	while (!STEmpty(&st))
		//链表不为空就继续操作:
	{
		//打印当前栈顶元素:
		printf("出栈前栈顶元素:%d\n", STTop(&st));

		//进行出栈:
		STPop(&st);
	}

	//换行:
	printf("\n");
	//打印当前栈中元素个数:
	printf("目前栈内元素个数为:%d", STSize(&st));

	//进行销毁:
	STDestroy(&st);
}

//主函数
int main()
{
	TestStack();

	return 0;
}
相关推荐
XiaoLeisj8 分钟前
【递归,搜索与回溯算法 & 综合练习】深入理解暴搜决策树:递归,搜索与回溯算法综合小专题(二)
数据结构·算法·leetcode·决策树·深度优先·剪枝
Jasmine_llq28 分钟前
《 火星人 》
算法·青少年编程·c#
闻缺陷则喜何志丹39 分钟前
【C++动态规划 图论】3243. 新增道路查询后的最短距离 I|1567
c++·算法·动态规划·力扣·图论·最短路·路径
Lenyiin1 小时前
01.02、判定是否互为字符重排
算法·leetcode
小林熬夜学编程1 小时前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
鸽鸽程序猿1 小时前
【算法】【优选算法】宽搜(BFS)中队列的使用
算法·宽度优先·队列
Jackey_Song_Odd1 小时前
C语言 单向链表反转问题
c语言·数据结构·算法·链表
Watermelo6171 小时前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
乐之者v1 小时前
leetCode43.字符串相乘
java·数据结构·算法