【数据结构初阶】手把手带你实现栈

news2024/11/14 3:33:47

前言

在进入数据结构初阶的学习之后,我们学习了顺序表和链表,当然栈也是一种特殊的数据结构,他的特点是后进先出。


栈的概念及结构

栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

我们切记,此处的栈是数据结构中的栈,与我们操作系统处学习的栈是不同的,它是一个数据存储的位置,而这里的栈是数据存储时的结构,是两个学科中不同的术语。

栈的入数据叫做进栈,压栈或者入栈,出数据时叫做出栈,栈的压栈和出栈都是在栈顶操作的,所以符合后进先出或者先进后出。

我们的生活中就有很多关于栈的示例,比如我们在坐电梯的时候,后上来的人到达目的地之后先出电梯,先上来的人后出电梯。

栈的接口实现

我们在学习完顺序表和链表之后,我们就要考虑顺序表和链表的优劣点来实现栈,我们知道栈的入栈和出栈都是一端,所以可以使用顺序表的尾插和尾删,也可以使用链表的头插头删以及尾插尾删,但是在都可以实现的情况下,我们就要选择消耗最小的顺序表。

(1)定义结构体

typedef int datetype;

typedef struct Stack
{
	datetype* a;
	int capacity;
	int top;
}stack;

我们使用typedef定义栈中存储数据的类型,这样方便我们在日后的使用中进行修改,我们将栈的结构定义为一个动态的顺序表,通过a指针来控制,同时记录栈顶的位置,以及最大容量。 

(2)栈的初始化

void stackInit(stack* p)
{
	assert(p);
	p->a = NULL;
	p->capacity = 0;
	p->top = 0;
}

对p进行断言,能够防止不小心传入NULL后解引用程序崩溃,初始化各个数据。

(3)压栈

void stackPush(stack* p,datetype x)
{
	assert(p);
	if (p->capacity == p->top)
	{
		int newCapacity = p->capacity == 0 ? 4 : 2 * p->capacity;
		datetype* tmp = (datetype*)realloc(p->a, newCapacity * sizeof(datetype));
		if (tmp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		p->a = tmp;
        p->capacity = newCapacity;
	}
	
	p->a[p->top] = x;
	p->top++;
}

同理,对指针p进行断言,判断栈顶是否达到最大容量处,如果没有到达直接将数据插入到top处,并且top自加,如果到了最大容量,我们就对顺序表进行扩容,首先判断容量是否为0,为0时将容量扩到4个数据的字节数,否则容量乘2。

此处的压栈与顺序表的尾插相同。

(4)出栈

void stackPop(stack* p)
{
	assert(p);
	assert(!stackEmpty(p));
	p->top--;
}

 同理对指针进行断言,还需要注意的时需要判断顺序表是否为NULL,如果为空,解引用后程序会崩溃,所以我们选择粗暴的方式进行断言,然后对栈顶减1,不用去处理本来的数据。

(5)判空

bool stackEmpty(stack* p)
{
    assert(p);

    return p->top==0;
}

此处我们只想要判断栈顶处是否为0,如果为0则说明顺序表为空,否则顺序表不为空,我们使用了bool类型,C语言中并没有布尔类型,所以我们要引头文件<stdbool.h>。

(6)求栈顶元素

datetype stackTop(stack* p)
{
	assert(p);
	assert(!stackEmpty(p));
	return p->a[p->top-1];
}

断言p以及断言顺序表不为空,返回栈顶的值即可。

(7)求数据个数 

int stackSize(stack* p)
{
	assert(p);

	return p->top;
}

(8)销毁栈

void stackDestroy(stack* p)
{
	assert(p);
	free(p->a);
	p->a = NULL;
	p->capacity = 0;
	p->top = 0;
}

我们使用malloc,calloc,realloc申请的空间,我们都必须使用free进行销毁。


源码 

stack.h

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

typedef int datetype;

typedef struct Stack
{
	datetype* a;
	int capacity;
	int top;
}stack;
//初始化
void stackInit(stack* p);
//销毁
void stackDestroy(stack* p);
//入栈
void stackPush(stack* p, datetype x);
//出栈
void stackPop(stack* p);
//取栈顶数据
datetype stackTop(stack* p);
//数据个数
int stackSize(stack* p);
//判断是否为空
bool stackEmpty(stack* p);

stack.c 

#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
void stackInit(stack* p)
{
	assert(p);
	p->a = NULL;
	p->capacity = 0;
	p->top = 0;
}
void stackPush(stack* p,datetype x)
{
	assert(p);
	if (p->capacity == p->top)
	{
		int newCapacity = p->capacity == 0 ? 4 : 2 * p->capacity;
		datetype* tmp = (datetype*)realloc(p->a, newCapacity * sizeof(datetype));
		if (tmp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		p->a = tmp;
        p->capacity = newCapacity;
	}
	
	p->a[p->top] = x;
	p->top++;
}

void stackPop(stack* p)
{
	assert(p);
	assert(!stackEmpty(p));
	p->top--;
}

void stackDestroy(stack* p)
{
	assert(p);
	free(p->a);
	p->a = NULL;
	p->capacity = 0;
	p->top = 0;
}

datetype stackTop(stack* p)
{
	assert(p);
	assert(!stackEmpty(p));
	return p->a[p->top-1];
}

bool stackEmpty(stack* p)
{
	assert(p);

	return p->top==0;
}
int stackSize(stack* p)
{
	assert(p);

	return p->top;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
void test()
{
	stack st;
	stackInit(&st);

	stackPush(&st, 1);
	stackPush(&st, 2);
	stackPush(&st, 3);
	stackPush(&st, 4);

	printf("%d ", stackTop(&st));
	stackPop(&st);
	printf("%d ", stackTop(&st));
	stackPop(&st);
	stackPush(&st, 5);
	stackPush(&st, 6);

    //int ret = stackTop(&st);
	//printf("%d", ret);

	while (!stackEmpty(&st))
	{
		printf("%d ",stackTop(&st));
		stackPop(&st);
	}


	stackDestroy(&st);

}

int main()
{
	test();
	return 0;
}

通过测试,我们发现无论什么时候出栈,出几个数据,都符合栈的特点,先进后出,后进先出。


总结 

今天我通过顺序表的结构来实现了栈,向展示并讲解了栈的概念、结构与各接口的实现过程与作用原理,当然也可以通过链表的方式来实现,希望大家可以多多尝试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/383030.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

iptables的介绍

iptables简介 1、 什么是iptables&#xff1f; iptables是linux防火墙工作在用户空间的管理工具&#xff0c;是 netfilter/iptables IP信息包过滤系统的一部分&#xff0c;用来设置、维护和检查Linux内核的IP数据包过滤规则 2、 iptables特点 iptables是基于内核的防火墙&…

【pytorch 入门系列】02 手把手多分类从0到1

温故而知新&#xff0c;通过手把手写一个多分类任务来复习之前所学过的知识。 前置知识 factorize的妙用&#xff1a;把文本数据枚举化 labels, uniques pd.factorize([b, b, a, c, b]) labels,uniques(array([0, 0, 1, 2, 0]), array([‘b’, ‘a’, ‘c’], dtypeobject))…

【C++】-- 特殊类设计

对于类的思维境界提升&#xff0c;没有太大的实际意义&#xff0c;但是锻炼思想。 目录 单例模式 饿汉模式 懒汉模式 #&#xff1a;请设计一个类&#xff0c;不能被拷贝。 拷贝只会发生在两个场景中&#xff1a;拷贝构造函数赋值运算符重载因此想要让一个类禁止拷贝&#xf…

对称锥规划:对称锥的增广拉格朗日乘子法(Semi-Smooth Newton Method解无约束优化子问题)

文章目录对称锥规划&#xff1a;对称锥的增广拉格朗日乘子法&#xff08;Semi-Smooth Newton Method解无约束优化子问题&#xff09;对称锥的增广拉格朗日函数Semi-Smooth Newton Method半光滑牛顿法广义雅可比半光滑性半光滑牛顿算法参考文献对称锥规划&#xff1a;对称锥的增…

2023年最新阿里云服务器价格表出炉(精准收费标准及配置价格表)

阿里云在全球率先宣布了基于 Intel Ice Lake 处理器的第七代云服务器ECS&#xff0c;性能提升的同时降低了报价&#xff0c;性价比更高了。进入2023年阿里云服务器价格依然是大家关心的问题&#xff0c;事实上阿里云服务器租用价格和最新收费标准都可以通过官方云服务器计算器来…

【IoT】智能烟雾报警器

设计简介 硬件设计由AT89C51单片机、DS18B20温度传感器、4位共阳数码管、电源模块、报警模块、按键模块、MQ-2烟雾检测模块和ADC0832模数转换模块组成。 烟雾传感器MQ-2检测空气中的烟雾气体&#xff0c;通过ADC0832进行数据转换&#xff0c;经过单片机的运算处理后在数码管上…

【WEB前端进阶之路】 HTML 全路线学习知识点梳理(上)

前言 HTML 是一切Web开发的基础&#xff0c;本文专门为小白整理&#xff0c;针对前端零基础的朋友们&#xff0c;手把手教你学习HTML&#xff0c;让你轻松迈入WEB开发的行列。 首先&#xff0c;感谢 橙子_ 在HTML学习以及本文编写过程中对我的帮助。 文章目录前言一.HTML简介1.…

Java使用不同方式获取两个集合List的交集、补集、并集(相加)、差集(相减)

1 明确概念首先知道几个单词的意思&#xff1a;并集 union交集 intersection补集 complement析取 disjunction减去 subtract1.1 并集对于两个给定集合A、B&#xff0c;由两个集合所有元素构成的集合&#xff0c;叫做A和B的并集。记作&#xff1a;AUB 读作“A并B”例&#…

微纳制造技术——基础知识

1.什么是直接带隙半导体和间接带隙半导体 导带底和价带顶处以同一K值&#xff0c;称为直接带隙半导体 导带底和价带顶不处在同一K值&#xff0c;称为间接带隙半导体 2.扩散和漂移的公式 3.三五族半导体的性质 1.high mobility 2.wide bandgap 3.direct bandgap 4.三五族…

SWM181 串口功能使用介绍

SWM181 串口功能使用介绍&#x1f4cc;SDK固件包&#xff1a;https://www.synwit.cn/kuhanshu_amp_licheng/✨注意新手谨慎选择作为入门单片机学习。&#x1f33c;开发板如下图&#xff1a; &#x1f4cb;SWM181描述上写了有4个串口&#xff0c;在数据手册上&#xff0c;将引脚…

教你如何搭建设备-巡检管理系统,demo可分享

1、简介1.1、案例简介本文将介绍&#xff0c;如何搭建设备-巡检管理。1.2、应用场景设备管理员进行制定设备巡检时间/内容计划、记录设备巡检信息、可以查看今日待巡检设备。2、设置方法2.1、表单搭建1&#xff09;新建表单【设备档案-履历表】&#xff0c;字段设置如下&#x…

activiti7执行流程详解

什么是工作流&#xff1f; 官方定义&#xff1a;工作流是将一组任务组织起来以完成某个经营过程&#xff1a;定义了任务的触发顺序和触发条件&#xff0c;每个任务可以由一个或多个软件系统完成&#xff0c;也可以由一个或一组人完成&#xff0c;还可以由一个或多个人与软件系统…

阿赵的MaxScript学习笔记分享八《文件操作》

大家好&#xff0c;我是阿赵。继续分享MaxScript学习笔记第八篇 。这一篇主要讲文件操作&#xff0c;包括文件的I/O和导入导出。 1、获得3DsMax指定的一些目录路径 如果在电脑上安装了3DsMax软件&#xff0c;那么在文档里面会有一个3dsMax的文件夹&#xff0c;里面有一些3dsMa…

《C++ Primer Plus》(第6版)第8章编程练习

《C Primer Plus》&#xff08;第6版&#xff09;第8章编程练习《C Primer Plus》&#xff08;第6版&#xff09;第8章编程练习1. 打印字符串2. CandyBar3. 将string对象的内容转换为大写4. 设置并打印字符串5. max5()6. maxn()7. SumArray()《C Primer Plus》&#xff08;第6版…

【C++】C++11 异常

目录 1. C语言传统的处理错误的方式 2. C异常概念 3. 异常的使用 3.1. 异常的抛出和捕获 3.2. 在函数调用链中异常栈展开匹配原则 3.3. 异常的重新抛出 3.4. 异常安全 3.5. 异常规范 4.自定义异常体系 5. C标准库的异常体系 6. 异常的优缺点 6.1. C异常的优点&…

Spark性能优化二 Shuffle机制分析

&#xff08;一&#xff09; 什么情况下发生shuffle 在MapReduce框架中&#xff0c;Shuffle是连接Map和Reduce之间的桥梁&#xff0c;Map阶段通过shuffle读取数据并输出到对应的Reduce&#xff1b;而Reduce阶段负责从Map端拉取数据并进行计算。在整个shuffle过程中&#xff0c…

Linux 学习整理(使用 iftop 查看网络带宽使用情况 《端口显示》)

一、命令简介 iftop 是实时流量监控工具&#xff0c;可以用来监控网卡的实时流量&#xff08;可以指定网段&#xff09;、反向解析IP、显示端口信息等。 二、命令安装 yum install -y iftop 三、命令相关参数及说明 3.1、相关参数说明 -i&#xff1a;设定监测的网卡&#…

python未来应用前景怎么样

Python近段时间一直涨势迅猛&#xff0c;在各大编程排行榜中崭露头角&#xff0c;得益于它多功能性和简单易上手的特性&#xff0c;让它可以在很多不同的工作中发挥重大作用。 正因如此&#xff0c;目前几乎所有大中型互联网企业都在使用 Python 完成各种各样的工作&#xff0…

CAD中如何将图形对象转换为三维实体?

有些小伙伴在CAD绘制完图纸后&#xff0c;想要将图纸中的某些图形对象转换成三维实体&#xff0c;但却不知道该如何操作&#xff0c;其实很简单&#xff0c;本节CAD绘图教程就和小编一起来了解一下浩辰CAD软件中将符合条件的对象转换为三维实体的相关操作步骤吧&#xff01; 将…

HID协议详解 - Report Descriptor报告描述符构建与解析

USB相关基础知识简述 报告描述符是HID协议里比较复杂的一部分&#xff0c;在理解报告描述符之前&#xff0c;可以对USB协议数据传输的一些基础知识做一些了解&#xff0c;更方便理解后续内容。 报告是USB协议里数据传输&#xff08;Data Transfer&#xff09;的一种&#xff…