数据结构学习之路--深入探索栈的核心要点(附C源码)

   哈喽~大家!今天我们来学习栈的特别节目,精彩马上开始~


目录

前言

一、栈 

1 栈的概念

2 栈的结构

3 栈的实现 

3.1 栈的定义

3.2 栈的初始化 

3.3 入栈 

3.4 出栈 

3.5 取栈顶元素 

 3.6 判断栈是否为空

3.7 栈的大小

3.8 栈的销毁 

 二、源代码


前言

   栈和队列均是常见的数据结构。栈的特点是后进先出(即 Last In First Out(LIFO) ),其增删查元素都在栈顶实现。而队列的特点是先进先出,增加元素在队尾实现,删除和查看元素都在队首实现。本期我们来详细学习实现栈的结构。

一、栈 

我们先根据下图直观地了解栈:

1 栈的概念

   栈是一种特殊的线性表,特点是后进先出,它仅允许在固定一端进行插入和删除元素操作,最先加入的元素最后取出,最后加入的元素最先取出。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。文字描述难免过于死板,为了更好的帮助大家理解,附以下图解: 

将数字 1 到 7 依次入栈之后,此时栈顶元素是 7 ,第一个出栈的元素是 7。

2 栈的结构

3 栈的实现 

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

一般栈的实现需要完成几种函数的操作: 

  • 栈的初始化
  • 入栈
  • 出栈
  • 取出栈顶元素
  • 判断栈是否为空
  • 栈的销毁 
//初始化
void StackInit(ST* ps);
//入栈
void StackPush(ST* ps, STDateType x);
//出栈
void StackPop(ST* ps);
//取栈顶元素
STDateType GetTop(ST* ps);
//判空
bool StackEmpty(ST* ps);
//栈大小
int StackSize(ST* ps)
//栈的销毁
void StackDestory(ST* ps);

3.1 栈的定义

//这里我们实现的是动态的栈

typedef int STDateType;    //方便数据类型的替换

typedef struct Stack        
{
	STDateType* a;         //动态开辟数组
	int top;               //栈顶
	int capacity;          //容量,方便扩容
}ST;

首先,定义动态数组 a,采用动态数组的方式主要是便于后期容量的扩充;然后,定义一个变量 top 用于标识栈顶位置;最后,定义一个变量 capacity 用于统计栈可以容纳的数据个数。 

3.2 栈的初始化 

//初始化
void StackInit(ST* ps)
{
	//判空
	assert(ps);

	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

首先,将指向栈中数据内存空间的指针 a 初始化为NULL;然后,将指向栈顶元素的变量 top初始化为0;最后,再将标识栈当前容量的 capacity 初始化为0即可。 

3.3 入栈 

//入栈
void StackPush(ST* ps, STDataType x)
{
	//判空
	assert(ps);
 
	//判断容量是否已满
	//top标识的是最后一个数据的下一个位置,如果想要指向最后一个数据,初始时top=-1
	if (ps->top == ps->capacity)
	{
        //为空就开辟四个空间,不为空,就扩容至二倍
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;

		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
 
		if (tmp == NULL)
		{
			printf("malloc fail\n");
			exit(-1);
		}
 
		//将新开辟的内存空间的首地址tmp赋值给a
		ps->a = tmp;
		//更新capacity
		ps->capacity = newCapacity;
	}
 
    //入栈
	ps->a[ps->top] = x;
	//top指向栈顶元素的下一个位置
	ps->top++;
}

在入栈之前,首先需要对顺序栈的空间容量进行检查,这里使用三目运算符进行判断:若当前容量 capacity 为空,则开辟4个数据的内存空间;若当前容量非空但已满,则将 capacity 的大小扩容至 2*capacity。然后调用 realloc 函数开辟新的内存空间,并返回新的内存空间的起始地址 tmp。接着将新开辟的内存空间的起始地址 tmp 赋值给 a,使 a 指向这片新开辟的空间。最后,将 x 插入栈顶位置,并让 top 继续指向栈顶元素的下一个位置。 

3.4 出栈 

//出栈
void StackPop(ST* ps)
{
	//判空
	assert(ps);
 
	//判断栈是否为空
	assert(!StackEmpty(ps));
	
	//出栈
	ps->top--;
}

在出栈之前,首先需要调用 StackEmpty(ps) 函数来判断栈是否为空,若不为空,则可以出栈。出栈操作就是将 top--,这里需要注意的是:出栈的数据还残留在内存中,只是逻辑上被删除了。 

3.5 取栈顶元素 

//取栈顶元素
STDataType StackTop(ST* ps)
{
	//判空
	assert(ps);
 
	//判断栈是否为空
	assert(!StackEmpty(ps));
 
	//取栈顶元素
	return ps->a[ps->top - 1];
}

在取栈顶元素之前,首先需要调用函数 StackEmpty(ps) 判断栈是否为空,若栈不为空则可以取栈顶元素。因为 top 指向栈顶元素的下一个位置,所以 top 需要先进行--,再取栈顶元素。 

 3.6 判断栈是否为空

//判空
bool StackEmpty(ST* ps)
{
	assert(ps);

	return ps->top==0;      //返回栈的top,若为0则为空,非0则不为空;
}

判断一个栈是否为空,若为空则返回 true,否则返回 false。这里需要特别说明的一点是,我们将 top 指向栈顶元素的下一个位置,而非指向栈顶元素本身。 

 注意:

  • 当top初始化为:top=0;此时top指向栈顶元素的下一个位置;
  • 当top初始化为:top=-1;此时top指向栈顶元素。

3.7 栈的大小

//大小
int StackSize(ST* ps)
{
	//判空
	assert(ps);
 
	return ps->top;
}

因为 top 指向栈顶元素的下一个位置,而 top 的下标又是从0开始的,所以 top 所在位置的下标就是所求栈的大小,也就是栈中元素个数。 

3.8 栈的销毁 

//销毁
void StackDestory(ST* ps)
{
	//判空
	assert(ps);
 
	//realloc开辟的空间,需要调用free释放
	free(ps->a);
	ps->a = NULL;
 
	ps->top = ps->capacity = 0;
}

对于栈的销毁,首先需要释放由 realloc 动态申请开辟的内存空间,使用 free 函数进行释放。然后将 top 和 capacity 初始化为0。 

 二、源代码

Stack.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
 
​
//这里我们实现的是动态的栈

typedef int STDateType;    //方便数据类型的替换

typedef struct Stack        
{
	STDateType* a;         //动态开辟数组
	int top;               //栈顶
	int capacity;          //容量,方便扩容
}ST;

 
//初始化
void StackInit(ST* ps);
 
//入栈
void StackPush(ST* ps, STDataType x);

//出栈
void StackPop(ST* ps);

//取栈顶元素
STDataType StackTop(ST* ps);

//判空
bool StackEmpty(ST* ps);

//大小
int StackSize(ST* ps);
 
//销毁
void StackDestory(ST* ps);
Stack.c

#include"Stack.h"

​//初始化
void StackInit(ST* ps)
{
	//判空
	assert(ps);

	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

​​//入栈
void StackPush(ST* ps, STDataType x)
{
	//判空
	assert(ps);
 
	//判断容量是否已满
	//top标识的是最后一个数据的下一个位置,如果想要指向最后一个数据,初始时top=-1
	if (ps->top == ps->capacity)
	{
        //为空就开辟四个空间,不为空,就扩容至二倍
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;

		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
 
		if (tmp == NULL)
		{
			printf("malloc fail\n");
			exit(-1);
		}
 
		//将新开辟的内存空间的首地址tmp赋值给a
		ps->a = tmp;
		//更新capacity
		ps->capacity = newCapacity;
	}
 
    //入栈
	ps->a[ps->top] = x;
	//top指向栈顶元素的下一个位置
	ps->top++;
}

​​//出栈
void StackPop(ST* ps)
{
	//判空
	assert(ps);
 
	//判断栈是否为空
	assert(!StackEmpty(ps));
	
	//出栈
	ps->top--;
}

​​//取栈顶元素
STDataType StackTop(ST* ps)
{
	//判空
	assert(ps);
 
	//判断栈是否为空
	assert(!StackEmpty(ps));
 
	//取栈顶元素
	return ps->a[ps->top - 1];
}

​​//判空
bool StackEmpty(ST* ps)
{
	assert(ps);

	return ps->top==0;      //返回栈的top,若为0则为空,非0则不为空;
}

​​//大小
int StackSize(ST* ps)
{
	//判空
	assert(ps);
 
	return ps->top;
}

​​//销毁
void StackDestory(ST* ps)
{
	//判空
	assert(ps);
 
	//realloc开辟的空间,需要调用free释放
	free(ps->a);
	ps->a = NULL;
 
	ps->top = ps->capacity = 0;
}
​
test.c

 
#include"Stack.h"
 
void TestStack()
{
	ST st;
 
	//初始化
	StackInit(&st);
 
	//入栈
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);
	StackPush(&st, 5);
    StackPush(&st, 6);
    StackPush(&st, 7);

	//出栈
	while (!StackEmpty(&st))
	{
		//读取栈顶元素
		printf("%d ",StackTop(&st));
		
		//出栈
		StackPop(&st);
	}
	printf("\n");
 
	//销毁
	StackDestory(&st);
}
 
int main()
{
	TestStack();
 
	return 0;
}

   本期的内容不多,相信大家学起来也相对容易,提前透露一下下:我们下期来玩队列哈~如果你们觉得本篇文章对自己有所帮助,给博主留下三连支持噢,你的支持是我创作的最大动力!那我们下期再会啦~

  • 14
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值