【数据结构】栈的概念与结构 | 栈的定义 | 栈的初始化和销毁

420 阅读5分钟

​一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

前言:

本章我们将学习 "栈" ,首先介绍栈的概念和结构,然后我们将着重讲解数组栈的实现。我们从零开始写数组栈的接口,并从零开始步步解读。本章旨在筑牢栈知识点的基础,对后续的刷题有着很大的帮助。

一、栈(stack)

0x00 栈的概念

📚 栈的概念:

① 栈是一种特殊的线性表,它只允许在固定的一端进行插入和删除元素的操作。

② 进行数据插入的删除和操作的一端,称为 栈顶 。另一端则称为 栈底

③ 栈中的元素遵守后进先出的原则,即 LIFO原则(Last In First Out)。

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

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

0x01 栈的结构

🔍 栈的结构:

0x02 数组栈和链式栈

📚 实现栈无非就两种结构:数组结构链式结构,两种结构都可以实现。

❓ 数组栈和链式栈哪种结构更好?

💡 相对而言数组的结构实现更优,尾插尾删的效率高,缓存利用率高,它的唯一缺点只是增容,但是增容1次扩2倍对栈来说本身就比较合理,是无伤大雅的。而链式栈虽然不会空间浪费,用一个 malloc 申请一个,但是链式栈存在一个致命的缺点:单链表不好出数据,必须要实现双向链表,否则尾上删除数据将会异常麻烦。

📌 如果硬要使用链式栈:

① 如果用尾做栈顶,尾插尾删,要设计成双向链表,否则删数据效率低。

② 如果用头做栈顶,头插头删,就可以设计成单链表。

🔺 总结:本章栈的实现将采用数组结构!

二、栈的定义

0x00 动态栈

📌 注意:本章将采用动态栈实现!

typedef int StackDataType;

typedef struct Stack {
	StackDataType* array;  //数组
	int top;               //栈顶
	int capacity;          //容量
} Stack;

🔑 解读:顺序表相信大家已经很熟了,这里和顺序表没啥两样。创建结构体,结构体有三个变量,array 是用来存放数据的数组。top 用来表示栈顶,这里相当于顺序表中的 size 变量。capacity 表示栈的容量。

0x01 静态栈

📚 简单介绍下静态栈:

typedef char StackDataType;
#define N 10

typedef struct Stack {
	StackDataType _array[N];  //数组
	int _top;                 //栈顶
} Stack;

🔑 解读:N 给多了浪费给少了又不够用,所以静态栈在实际中是不实用的。静态栈满了就不能扩大了,而动态栈是 malloc 出来的,不够了可以 realloc 扩容。虽然不实用,但是我们也得认识它,知道有这么一个东西,以后见到不至于觉得奇怪。

0x02 接口函数

📚 这是需要实现几个接口函数:

void StackInit(Stack* pst);
void StackDestroy(Stack* pst);
bool StackIsEmpty(Stack* pst);
void StackPush(Stack* pst, StackDataType x);
void StackPop(Stack* pst);
StackDataType StackTop(Stack* pst);
int StackSize(Stack* pst);

🔑 解读:相信细心的小伙伴发现了,StackIsEmpty 函数的类型是布尔。C语言如果想使用布尔值需要引入头文件 #include <stdbool.h> 。


0x00 初始化栈(StackInit)

💬 Stack.h

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int StackDataType;

typedef struct Stack {
	StackDataType* array;  //数组
	int top;               //栈顶
	int capacity;          //容量
} Stack;

void StackInit(Stack* pst);

🔑 解读:大家学到这里想必已然轻车熟路,应该知道需要引入哪些头文件了,动态内存开辟需要引入 stdlib.h,断言需要引入 assert,使用布尔值需要引入 stdbool.h 。

💬 Stack.c

/* 初始化 */
void StackInit(Stack* pst) {
	assert(pst);   // 防止pst为空

	pst->array = NULL;
	pst->top = 0;  // pst->top = -1
	pst->capacity = 0;
}

🔑 解析:初始化和顺序表几乎没有什么区别。首先通过结构体指针(我们定义的Stack) pst 指向 array,将数组为空。因为是初始化,所以将有效数据个数和数组时即能存数据的空间容量一并置为0。

0x01 销毁(StackDestroy)

💬 Stack.h

void StackDestroy(Stack* pst);

💬 Stack.c

/* 销毁 */
void StackDestroy(Stack* pst) {
	assert(pst);   // 防止pst为空
	
	free(pst->array);
	pst->array = NULL;
	pst->capacity = pst->top = 0;
}

🔑 解读:首先把栈 free 掉,为了防止野指针我们手动把它置为空指针(建议养成习惯)。最后再把 capacitysize 全部设为0,做到空着手来,空着手去,销毁部分就实现好了。

参考资料:

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

百度百科[EB/OL]. []. baike.baidu.com/.

📌 笔者:王亦优

📃 更新: 2021.11.17

❌ 勘误: 无

📜 声明: 由于作者水平有限,本文有错误和不准确之处在所难免,本人也很想知道这些错误,恳望读者批评指正!

本篇完。