首页 > 其他分享 >数据结构:栈

数据结构:栈

时间:2024-07-20 09:26:55浏览次数:13  
标签:ps top 栈顶 空间 数据结构 数据 Stack

数据结构:栈

满足栈的特性,只有先进后出的特性,不能遍历,也就不能遍历打印,也不能随机访问。

栈:栈是在处理数据时是先进后出、就是先进栈的数据最后一个出栈、最后一个进栈的数据第一个出栈、栈就类似于给一把手枪弹夹压子弹,给弹夹压子弹的顺序就如同数据进栈的顺序,第一颗子弹在弹夹的最低端,最后一颗子弹在弹夹的最上端,发射子弹时,总是发射最上端的子弹,手枪不可能逆着顺序发射子弹吧~,这样的给手枪压子弹,就如同进栈、发射子弹就好比出栈。

总的来说栈是一个先进后出的数据结构,只能在栈顶取出数据和入数据,这就类型给手枪压子弹和发射子弹。

允许插入数据和删除数据的一端叫做栈顶 ,另一端叫做栈底

栈是属于线性表的一种,既然是线性表的一种,那它可以有两种存储结构来实现,一是顺序存储结构、二是链式存储结构。其中以顺序存储结构实现最佳

以顺序结构来实现的栈,由于数组的特性在进行数据的存储取出时,非常方便,而不足是使用的空间大小是有限的,若空间不够需要重新开辟空间,不免找出空间的浪费。

以链式结构来实现的栈,它并不会对空间的存储上造成浪费,在空间上它可以的无限的,每个数据块之间是通过指针链接,但这样会增加内存的开销。

若数据范围是可控的,使用顺序结构实现,若是不可控的,数据很多的使用链式结构实现。

typedef int StackDatdType;
typedef struct Stack
{
	StackDatdType* arr;
	int capacity;//空间大小
	int top;//栈顶
}Stack;

使用顺序结构实现栈时,它的底层是以数组实现的,其中以数组下标为零的位置为栈尾,它的变化更小更适合做栈尾,用数组最后一个位置做栈顶,它涉及元素的增添删除。

功能实现

//初始化
void StackInit(Stack* ps);
//销毁
void StackDestory(Stack* ps);
//入栈、压栈
void StackPush(Stack* ps, StackDatdType x);
//出栈
void StackPuop(Stack* ps);
//判断栈是否为空
bool StackEmpty(Stack* ps);
bool StackEmpty2(Stack* ps);

//获取栈顶元素
StackDatdType StackTop(Stack* ps);
//获取栈的元素个数
int StackSize(Stack* ps);

栈使用顺序存储结构来实现,它的底层使用数组来实现的,若学习过顺序表,实现栈将会得心应手,在代码的理解上并没有过多区别。

初始化

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

初始化:

初始化栈,将指向栈的变量的地址传递过来使用一级指针接收,实现形参的改变影响到实参。初始化栈很简单,只需对其指针置空、空间大小和栈顶置0即可。

出栈、入栈

//入栈、压栈
void StackPush(Stack* ps, StackDatdType x)
{
	assert(ps);
	if (ps->capacity == ps->top)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		StackDatdType* new = (StackDatdType*)realloc(ps->arr, sizeof(StackDatdType) * newcapacity);
		if (new == NULL)
		{
			perror("realloc");
			exit(1);
		}
		ps->arr = new;
		ps->capacity = newcapacity;
	}
	ps->arr[ps->top++] = x;
}

入栈,只能从栈顶位置入栈,所以在栈里插入数据时只有这一种操作,入栈操作很简单,只需要在栈顶放置一个数据 ps->arr[ps->top] = x;,即可,入了一个数据别忘了对top加1。需要注意的是,对空间大小的判断,是该开辟空间还是不开辟,又该开辟多大的空间。

在函数里,需要对有效数据个数(栈顶)和空间大小是否相等进行判断,所以使用if语句,在if语句里,使用realloc函数开辟,为啥不使用malloc函数呢~,我们需要实现的动态开辟,空间越开越大,可能会在新的位置开辟一块空间,malloc函数做不到。增容到原空间的2倍或3倍可以减少扩容操作的频率。如果每次只增加少量空间,那么在元素数量增长时,需要频繁进行扩容操作,这会降低性能。

​ 我们对栈进行初始化时空间大小为0,直接乘2还是0,这里使用了三目操作符巧妙地解决的这种问题,还完成对空间的倍数增长。

ps->capacity == 0 ? 4 : ps->capacity * 2;,将表达式的结果赋给 int newcapacity,使用它开辟一个StackDatdType的空间,大小为 sizeof(StackDatdType) * newcapacity,由于在开辟空间是可能会开辟失败,导致数据丢失,这里使用了new来接收新开辟的空间*,然后对其判空,若为空则说明开辟是否结束函数,打印错误信息,若开辟成功则将new赋给ps->arr,以及对空间大小的更新

//判断栈是否为空
bool StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->top == 0;
	//return ps->top;
}
//出栈
void StackPuop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	ps->top--;
}

出栈不过是让栈顶位置和大小发生变化,栈顶位置数据并不需要发生变化这是多余操作。当栈顶位置减1后原栈顶的数据就被舍弃认为是一块无效数据。所以完成出栈只需有将top减1即可。

重点是对栈判空,若栈是空的那就不可以出栈,这里 StackEmpty函数来完成判空,当top为零时说明栈里没有数据,返回真,有数据返回假,在出栈函数里使用 assert断言对上述两种情况取反 !StackEmpty(ps),若top为零,属于空栈,试图对空栈出栈程序会报错。

获取栈的数据个数

//获取栈的元素个数
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->top;
}

栈顶是数组有效数据个数的最后一个位置,每压一次栈,top都会加1,保存它栈顶的位置,也统计了栈里数据的个数。所以在获取栈的数据个数是将其top返回即可。

取栈顶元素

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

栈顶top指的是栈尾,数组有效数据里的最后一个位置,将栈顶元素返回时返回数组的top位置的数据即可。需要注意的是,使用top调用数组元素是需要减1,数组总是从零开始的,top栈顶是从1开始的。

销毁

//销毁
void StackDestory(Stack* ps)
{
	assert(ps);
	if(ps->arr)
		free(ps->arr);
	ps->capacity = ps->top = 0;
}

栈的空间是使用realloc函数开辟的,那他得使用对应得free对空间进行释放,让后将栈的空间大小和,栈顶置0即可。

需要注意的是若数组本身是空的那就不能对齐进行释放,在使用free释放前还需要使用if语句进行判断。是否为空。

标签:ps,top,栈顶,空间,数据结构,数据,Stack
From: https://blog.csdn.net/sparrowfart/article/details/140550253

相关文章

  • 数据结构(队列)
    文章目录一、概念与结构二、队列的实现QueueNode.hQueue.c初始化判断队列为空队尾,入队列队头,出队列取队头数据取队尾数据取队列有效元素个数销毁队列test.c一、概念与结构......
  • 数据结构_排序
    目录一、排序二、插入排序2.1直接插入排序2.2希尔排序三、选择排序3.1直接选择排序3.2堆排序四、交换排序4.1冒泡排序4.2快速排序五、归并排序六、排序算法分析总结一、排序排序:就是使一串记录序列,按照其中某个或某些关键字的大小,进行递增或递减的排列......
  • 【数据结构】学习day 1.1线性表中的顺序表
    1 "&"区别(c++)#include<stdio.h>voidtest(intx){   x=2024;   printf("intestx=%d\n",x);}intmain(){   intx=1;   printf("beforetestx=%d\n",x);   test(x);   printf("aftertestx=%d\n"......
  • 数据结构——哈希
    前言顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(logN),搜索的效率取决于搜索过程中元素的比价次数。理想的搜索方法:可以不经过任何比较,一次直接从表中得......
  • 数据结构—双向链表
    文章目录1.双向链表的概念和结构1.1双向链表的概念1.2双向链表与单链表的对比1.3双向链表的结构2.双向链表的接口实现2.1申请一个新结点2.2初始化2.3打印2.4尾插2.5头插2.6判断链表是否为空2.7尾删2.8头删2.9查找2.10在指定位置之后插入数据2.11在指定位......
  • 数据结构与算法 数组篇之长度最小的子数组
    问题描述:给定一个含有 n 个正整数的数组和一个正整数 target 。找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl,numsl+1,...,numsr-1,numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。题目链接:.-力扣(LeetCode)解题思路:双指针,......
  • 基础数据结构——初级——链表
    链表的特点是一组位于任意位置的存储单元存储线性表的数据元素。一般分为单向链表和双向链表(如下图所示)。 使用链表时,可以用STL的list,也可以自己写链表。如果自己写代码实现链表,有两种编码实现方法:动态链表和静态链表。在算法竞赛中为加快编码速度,一般用静态链表或者STL的l......
  • 【数据结构】- 线段树
    前言线段树用于维护区间上的值,可以在$O(\logn)$的时间复杂度内实现单点,区间修改及查询。并且所有满足结合律的运算都可以用线段树进行加速。基本操作建树给你长度为$n$的正整数序列$A$,要你实现单点修改与区间查询。(加和)这就是线段树的基本题目。线段树,字面上来看就是把......
  • 基于Python语言的入门算法和数据结构(持续更新中,求关注一波)[链表 栈 队列 复杂度 操作]
    这篇文章主要是讲的Python语言的算法,本人还在不断记笔记中,文章也会持续更新,内容比较浅薄,请大家指教另外推荐一个比较好用的记笔记的软件Typora,自己已经使用很久了,感觉不错。。。虽然但是还是有欠缺。目录第一章算法概述1.1什么是数据结构?01数据结构都有哪些组成方式02......
  • Java中的并发数据结构与多线程优化技术
    Java中的并发数据结构与多线程优化技术大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在多线程编程中,并发数据结构和优化技术是提高系统性能和可靠性的关键。Java提供了丰富的并发数据结构和多线程优化技术,本文将详细介绍常用的并发数据结构及其使用方法......