前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >【数据结构与算法】使用数组实现栈:原理、步骤与应用

【数据结构与算法】使用数组实现栈:原理、步骤与应用

作者头像
用户11396077
发布2024-12-06 19:01:18
发布2024-12-06 19:01:18
9400
代码可运行
举报
文章被收录于专栏:C/C++指南C/C++指南
运行总次数:0
代码可运行

一、引言

🎄栈(Stack)是什么?
  • 栈是一种后进先出(LIFO, Last In First Out)的数据结构。
  • 栈是一种只能在一端进行插入和删除操作的线性表。
  • 允许进行插入和删除操作的一端称为栈顶(top),另一端称为栈底(bottom)。
  • 栈中没有元素时,称为空栈。
  • 栈的基本操作包括:push(入栈)、pop(出栈)、peek(查看栈顶元素)和isEmpty(判断栈是否为空)等。

🎄为什么使用数组实现栈?

  • 数组是一种线性数据结构,能够连续存储数据,且通过索引可以方便地访问任意位置的元素。
  • 因为栈只在栈顶增删,所以基于数组实现,既避免了插入需要移动数据的劣势,又保持了数组访问数据的优势,可以实现高效的栈操作。

二、定义栈结构

🎄栈的结构
  • 指向数组的指针(动态开辟的空间)
  • 标记栈顶位置的变量 top
  • 标记栈的大小的变量 capacity
代码语言:javascript
代码运行次数:0
运行
复制
// 支持动态增长的栈
typedef int STDataType;//对数据类型重命名,方便后期修改类型
typedef struct Stack
{
	STDataType* a;
	int top;		// 栈顶
	int capacity;  // 容量 
}Stack;//定义结构同时重命名
🎄栈顶位置的指向

需要注意的是:top的指向应该始终保持一致性

1.如果top指向栈顶元素,初始不能为0,应该指向-1

2.如果top初始为0,其应该指向栈顶元素的下一个元素

对应的判定栈满和栈空有所不同

三、实现栈的基本操作

🍃初始化
  • 对形参判空
  • 数组指针初始指向空
  • top和capacity初始化为0(这里top指向的是栈顶元素的下一个位置)
代码语言:javascript
代码运行次数:0
运行
复制
// 初始化栈 
void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}
🍃销毁
  • 对形参判空
  • 释放数组空间
  • 数组指针指向空
  • top和capacity改为0
代码语言:javascript
代码运行次数:0
运行
复制
// 销毁栈 
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}
🍃入栈

判空 判断是否需要扩容(top和capacity相等) 扩容步骤: 空间二倍增长 ,更新数组指针和容量 数据插入到top位置,top位置++

代码语言:javascript
代码运行次数:0
运行
复制
// 入栈 
void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	//判断是否需要扩容
	if (ps->top == ps->capacity)
	{
		int newcapa = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapa);
		if (tmp == NULL)
		{
			perror("realloc\n");
			exit(1);
		}
		ps->a = tmp;
		ps->capacity = newcapa;
	}
	//确定空间足够之后再插入数据
	ps->a[ps->top] = data;
	ps->top++;
}
🍃出栈
  • 对形参判空
  • 对栈判空
  • top--

(该方法对于栈只存在一个元素的情况也可以正确处理)

代码语言:javascript
代码运行次数:0
运行
复制
// 出栈 
void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->top);

	ps->top--;
}

注意: 即使函数只有一两条语句也还是建议封装成函数,这样可以提高程序的可维护性和可读性

🍃查看栈顶元素
  • 对形参判空
  • 对栈判空
  • 返回top前一个位置的元素
代码语言:javascript
代码运行次数:0
运行
复制
// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->top);
	return ps->a[ps->top-1];
}
🍃对栈判空
  • 对形参判空
  • 返回top==0的结果(因为这里top指向的是栈顶元素的下一个元素,所以栈空时top==0)
代码语言:javascript
代码运行次数:0
运行
复制
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{
	assert(ps);

	return ps->top == 0;
}
🍃获取有效数据个数
  • 对形参判空
  • 返回top (top对应的下标是栈顶的下一个元素,top就是元素的个数)
代码语言:javascript
代码运行次数:0
运行
复制
// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{
	assert(ps);

	return ps->top;
}

四、使用数组实现栈的C语言代码

stack.h 栈的头文件
代码语言:javascript
代码运行次数:0
运行
复制
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

// 支持动态增长的栈
typedef int STDataType;//对数据类型重命名,方便后期修改类型
typedef struct Stack
{
	STDataType* a;
	int top;		// 栈顶
	int capacity;  // 容量 
}Stack;//定义结构同时重命名

// 初始化栈 
void StackInit(Stack* ps);
// 入栈 
void StackPush(Stack* ps, STDataType data);
// 出栈 
void StackPop(Stack* ps);
// 获取栈顶元素 
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数 
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);
stack.c 栈的实现源文件
代码语言:javascript
代码运行次数:0
运行
复制
#include"stack.h"

// 初始化栈 
void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}

// 入栈 
void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	//判断是否需要扩容
	if (ps->top == ps->capacity)
	{
		int newcapa = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapa);
		if (tmp == NULL)
		{
			perror("realloc\n");
			exit(1);
		}
		ps->a = tmp;
		ps->capacity = newcapa;
	}
	//确定空间足够之后再插入数据
	ps->a[ps->top] = data;
	ps->top++;
}

// 出栈 
void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->top);

	ps->top--;
}

// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->top);
	return ps->a[ps->top-1];
}

// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{
	assert(ps);

	return ps->top;
}

// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{
	assert(ps);

	return ps->top == 0;
}

// 销毁栈 
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}
test.c 主函数测试文件
代码语言:javascript
代码运行次数:0
运行
复制
#include"stack.h"

void test1()
{
	Stack st ;
	StackInit(&st);
	if (StackEmpty(&st))
	{
		printf("栈空\n");
	}
	else
	{
		printf("栈非空\n");
	}
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);
	if (StackEmpty(&st))
	{
		printf("栈空\n");
	}
	else
	{
		printf("栈非空\n");
	}
	printf("栈中元素个数:%d\n", StackSize(&st));

	printf("%d\n", StackTop(&st));
	StackPop(&st);
	printf("%d\n", StackTop(&st));
	StackPop(&st);
	printf("%d\n", StackTop(&st));
	StackPop(&st);
	printf("%d\n", StackTop(&st));
	StackPop(&st);
	if (StackEmpty(&st))
	{
		printf("栈空\n");
	}
	else
	{
		printf("栈非空\n");
	}

	StackDestroy(&st);

}

int main()
{
	test1();

	return 0;
}
测试结果

五、栈的应用

  1. 函数调用栈:在程序执行过程中,函数调用是通过栈来实现的。每个函数调用时,其返回地址、局部变量和参数等信息都会被压入栈中,当函数返回时,这些信息会被弹出栈。
  2. 表达式求值:在编译器中,表达式求值通常使用栈来实现。例如,在解析算术表达式时,可以使用两个栈:一个用于存储操作数,另一个用于存储操作符。
  3. 浏览器历史记录:浏览器的“前进”和“后退”功能通常使用栈来实现。用户浏览的网页会被压入栈中,当用户点击“后退”按钮时,会从栈中弹出并显示上一个网页。
  4. 撤销操作:在许多图形编辑器和文本编辑器中,撤销操作通常使用栈来实现。每次编辑操作(如剪切、复制、粘贴等)都会被压入一个撤销栈中,当用户点击“撤销”按钮时,会从栈中弹出并执行相反的操作以撤销上一次编辑。

六、总结

  1. 使用数组实现栈是一种简单且高效的方法,能够充分利用数组的特性来实现栈的基本操作。
  2. 在实际应用中,栈具有广泛的应用场景,如函数调用栈、浏览器的前进后退功能以及表达式求值等。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、引言
    • 🎄栈(Stack)是什么?
    • 🎄为什么使用数组实现栈?
  • 二、定义栈结构
    • 🎄栈的结构
    • 🎄栈顶位置的指向
  • 三、实现栈的基本操作
    • 🍃初始化
    • 🍃销毁
    • 🍃入栈
    • 🍃出栈
    • 🍃查看栈顶元素
    • 🍃对栈判空
    • 🍃获取有效数据个数
  • 四、使用数组实现栈的C语言代码
    • stack.h 栈的头文件
    • stack.c 栈的实现源文件
    • test.c 主函数测试文件
    • 测试结果
  • 五、栈的应用
  • 六、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档