数据结构初阶:栈

本篇博客主要介绍栈的概念与结构等内容。

1.栈

1.1 概念与结构

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

**压栈:**栈的插入操作叫作进栈/压栈/入栈,入数据在栈顶。

出栈: 栈的删除操作叫作出栈。出数据也在栈顶。

栈底层结构选型

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较少。

1.2 栈的实现

Stack.h

复制代码
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>


//定义栈的结构
typedef int STDataType;
typedef struct Stack
{
	STDataType* arr;
	int top;      //指向栈顶的位置
	int capacity; //容量
}ST;

void STInit(ST* ps);
void STDestroy(ST* ps);


//入栈---栈顶
void StackPush(ST* ps, STDataType x);
//出栈---栈顶
void StackPop(ST* ps);
//取栈顶数据
STDataType StackTop(ST* ps);

bool StackEmpty(ST* ps);
//获取栈中有效元素个数
int STSize(ST* ps);
1.3 定义栈的结构
复制代码
//定义栈的结构
typedef int STDataType;
typedef struct Stack
{
	STDataType* arr;
	int top;      //指向栈顶的位置
	int capacity; //容量
}ST;

代码逻辑:

首先,通过typedef定义了一个整数类型STDataType ,这里实际上是将int类型取了一个别名STDataType

然后,定义了一个结构体Stack,这个结构体包含了三个成员:

  • STDataType* arr:一个指向整数类型的指针,用于存储栈的元素。
  • int top:用于指向栈顶的位置。
  • int capacity:表示栈的容量。
1.4 栈的初始化
复制代码
void STInit(ST* ps)
{
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}

代码逻辑:

函数的参数是一个指向ST结构体的指针ps。在函数内部,将结构体中的成员进行了如下初始化:

  • ps->arr被初始化为NULL,表示栈的元素数组为空。
  • ps->topps->capacity都被初始化为0,分别表示栈顶位置和栈的容量为0

这样,通过调用这个函数,可以将一个栈结构体初始化为一个空栈的状态。

1.5 栈的销毁
复制代码
void STDestroy(ST* ps)
{
	if (ps->arr)
		free(ps->arr);
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}

代码逻辑:

函数的参数是一个指向ST结构体的指针ps。在函数内部,进行了以下操作:

首先,检查ps->arr是否不为NULL。如果不为NULL,则使用free函数释放ps->arr所指向的内存空间,以避免内存泄漏。

然后,将ps->arr再次设置为NULL,并将ps->topps->capacity都设置为0,将栈结构体的相关成员恢复到初始状态。

这样,通过调用这个函数,可以释放栈所占用的内存资源,并将栈结构体的成员进行清理和重置。

1.6 入栈
复制代码
//入栈----栈顶
void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		//空间不够---增容
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(1);
		}
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
	//空间足够
	ps->arr[ps->top++] = x;
}

代码逻辑:

用于实现向栈中入栈的操作。

函数的参数包括一个指向栈结构体的指针ps和一个要入栈的元素x

在函数内部,首先使用assert函数检查ps是否有效 。然后,通过判断栈顶位置ps->top是否等于栈的容量ps->capacity来确定是否需要进行扩容操作。

如果需要扩容,会根据当前容量的情况计算新的容量(如果当前容量为0,则新容量为4;否则新容量为当前容量的2倍),然后使用realloc函数 对栈的元素数组进行扩容。如果扩容失败(tmpNULL),则会输出错误信息并退出程序。

如果空间足够,直接将元素x放入栈顶位置(ps->arr[ps->top] = x),然后将栈顶位置加1ps->top++)。

总的来说,这个函数实现了在栈空间不足时进行自动扩容,并将元素成功入栈的功能。

1.7 判空和出栈
复制代码
bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}

//出栈----栈顶
void StackPop(ST* ps)
{
	assert(!StackEmpty(ps));
	--ps->top;
}

代码逻辑:

  • bool StackEmpty(ST* ps) 这个函数用于判断栈是否为空。它首先使用assert函数检查ps是否有效,然后通过判断栈顶位置ps->top是否为0来确定栈是否为空。如果ps->top0,则表示栈为空,函数返回true;否则,函数返回false
  • void StackPop(ST* ps) 这个函数用于实现出栈操作。它首先使用assert函数和StackEmpty函数来检查栈是否为空,如果栈为空则会产生断言错误。如果栈不为空,那么将栈顶位置ps->top1,实现出栈操作。
1.8 取栈顶数据
复制代码
//取栈顶数据
STDataType StackTop(ST* ps)
{
	assert(!StackEmpty(ps));
	return ps->arr[ps->top - 1];
}
1.9 获取栈中数据有效个数
复制代码
//获取栈中有效元素个数
int STSize(ST* ps)
{
	assert(ps);
	return ps->top;
}

代码逻辑:

函数的参数是一个指向栈结构体的指针 ps 。在函数内部,首先使用 assert 函数检查 ps 是否有效。然后,直接返回栈顶位置 ps->top 的值,将其作为栈中有效元素的个数。因为栈顶位置的值就表示了栈中当前元素的数量。

2.源代码(Stack.c)

复制代码
#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"

void STInit(ST* ps)
{
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}
void STDestroy(ST* ps)
{
	if (ps->arr)
		free(ps->arr);
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}
//入栈----栈顶
void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		//空间不够---增容
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(1);
		}
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
	//空间足够
	ps->arr[ps->top++] = x;
}


bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}

//出栈----栈顶
void StackPop(ST* ps)
{
	assert(!StackEmpty(ps));
	--ps->top;
}
//取栈顶数据
STDataType StackTop(ST* ps)
{
	assert(!StackEmpty(ps));
	return ps->arr[ps->top - 1];
}
//获取栈中有效元素个数
int STSize(ST* ps)
{
	assert(ps);
	return ps->top;
}

3.小结

以上便是本篇博客的所有内容,如果诸君学到知识的话,还请诸君点点赞,支持博主。

相关推荐
PHASELESS41125 分钟前
Java二叉树深度解析:结构、算法与应用实践指南
java·开发语言·数据结构·算法
牧木江26 分钟前
【从C到C++的算法竞赛迁移指南】第二篇:动态数组与字符串完全攻略 —— 写给C程序员的全新世界
c语言·c++·经验分享·笔记·算法
祁同伟.29 分钟前
【数据结构 · 初阶】- 带头双向循环链表
数据结构·链表
学习2年半2 小时前
回溯算法:List 还是 ArrayList?一个深拷贝引发的思考
数据结构·算法·list
烁3472 小时前
每日一题(小白)暴力娱乐篇30
java·数据结构·算法·娱乐
Y.O.U..5 小时前
力扣HOT100——无重复字符的最长子字符串
数据结构·c++·算法·leetcode
虔城散人10 小时前
C语言 |位域结构体
c语言
Hanson Huang10 小时前
【数据结构】堆排序详细图解
java·数据结构·排序算法·堆排序
Susea&10 小时前
数据结构初阶:队列
c语言·开发语言·数据结构
序属秋秋秋11 小时前
算法基础_数据结构【单链表 + 双链表 + 栈 + 队列 + 单调栈 + 单调队列】
c语言·数据结构·c++·算法