数据结构——栈和队列(上)

目录

一,栈的介绍

二,栈及其操作的实现

1. 初始化空间

2. 进栈和出栈

3. 栈的其余操作

三,整体代码


一,栈的介绍

        我们都知道一个杀伤力极大的武器——枪。枪击案、枪击事件屡见不鲜,即便没有见过,也会有各色各样的玩具枪,模型枪。我们今天要带来的线性表之一——栈,它类似于子弹放入弹夹一样:先进去的子弹后出来,后进去的子弹先出来。

        在我们软件应用中,栈这种先进后出的数据结构的应用是非常广的。比如我们使用的浏览器

 在我们浏览网页上网时,总会有一个“ 后退 ”键是以供我们使用的,假如你正在轻松的刷着视频,突然看到一个让自己很感兴趣的链接,当你迫不及待地打开它观看时却发现是假的,于是你就可以点击“后退”键返回到你的视频页面。即便你点击了十几个链接进行跳转,当你又连续单击”后退“键的时,就i可以回到之前浏览的某个页面。

对于栈的定义是:栈是限定仅在表尾进行插入和删除操作的线性表。

我们把允许插入和删除的一端称为栈顶,另一端称为栈底,不含任何数据元素的栈称为空栈。

栈又称为后进先出的线性表,简称LIFO(Last in First out) 结构。

而定义中的表尾指的不是栈底,而是栈顶,这点要牢记。

对于栈的插入操作——即给栈中放入数据,我们称之为进栈,也叫压栈。好比子弹入弹夹

对于栈的删除操作——即删除栈中的数据,我们称之为出栈,也叫弹栈。好比子弹出弹夹

其两种操作图如下展示:(因为我们以顶部底部来描绘栈,所以我们竖直其模型方便理解)

 那么是不是说最先进栈的元素只能最后出栈呢?答案是不一定的,比如当1,2,3三个元素依次进栈时,我们可以1进栈接着2接着3,出栈顺序就是3、2、1。但是我们也可以1进栈后直接出,然后2进栈,2出栈,3进栈,3出栈。也可以1进,2进,2出,1出,3进,3出等等。

(注意,这里的栈不是我们空间里的栈区,前者是数据结构,后者是空间内存区)

二,栈及其操作的实现

        1. 初始化空间

        由上图进栈出栈的图示可知,我们需要一个变量top来记录栈顶,由于是线性表,则需要一段连续的空间。

        于是对栈的基本含参实现如下:

typedef int STDatatype;
typedef struct Stack
{
	STDatatype* a;   //开辟连续空间
	int capacity;    //栈的大小
	int top;         //栈内元素个数
}Stack;

我们在此基础上为其开辟四个大小为int的空间。

//初始化空间
void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = (STDatatype*)malloc(sizeof(STDatatype) * 4);
	if (ps->a == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	ps->capacity = 4;
	ps->top = 0;
}

因为要修改栈的大小,我们传入结构体指针。开辟完成后我们便得到了一个空栈,大小为16字节。

        2. 进栈和出栈

我们先来实现前面所讲到的进出栈操作,这里我们push表示进栈,pop表示出栈。在初始化栈空间时,我们将top放置为0表示此时栈内没有元素,所以此时只需将元素进栈并在达到栈满容量时扩容即可,进栈实现如下:

void StackPush(Stack* ps,STDatatype x)
{
	assert(ps);
    //检查容量
	if (ps->top == ps->capacity)
	{
		STDatatype* tmp = (STDatatype*)realloc(ps->a, 2 * ps->capacity * sizeof(STDatatype));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity *= 2;
	}
    //进栈
	ps->a[ps->top] = x;
	ps->top++;
}

 可以观察到我们的栈不再是原来的空栈了,我们放入了1~5五个元素并且容量变为8,此时栈顶元素为Top,也同时表示着栈中有5个元素。

        再来实现出栈效果,当我们为空栈时就不能进行出栈操作,所以我们需要判断栈是否是空栈。之后便是像我们顺序表删除元素类似的操作,实现如下:

void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);
	ps->top--;
}

 可以观察到我们栈中元素只剩一个。

        3. 栈的其余操作

     作为一个拿的上台面的数据结构,它显然不能只有两种相关的操作,返回栈顶元素,返回栈中个数以及判断栈是否为空,销毁栈等四种操作也是极其重要的。接下来我们一一实现。

        判断栈是否为空:

 我们使用布尔值来反映情况,前面提到,top表示的就是栈的元素个数,所以当top为0时就是空栈,top不为零就不是空栈。相关实现如下:

bool StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->top == 0;
}

        返回栈顶元素:

在我们往后的学习中会有涉及到极其重要的知识——用栈来模拟递归。这时我们的返回栈顶元素操作就必不可少。注意,当栈为空栈时,我们就无需返回,所以这里可以复用我们空栈判断函数来对栈是否为空进行断言,实现如下:

STDatatype StackTop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->a[ps->top-1];
}

 在我们入栈五个元素并出栈四个元素后,可知栈内唯一元素就是1,所以此时我们可以提取栈顶元素。

        返回栈的元素个数:

由于返回的数值一定是整数,所以我们返回值设置为int型,返回top即可。

int StackSize(Stack* ps)
{
	assert(ps);
	return ps->top;
}

        销毁栈:

由于我们使用到malloc和realloc函数,它们是在栈空间上开辟内存我们仍然需要主动释放空间

实现如下:

void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

三,整体代码

这里小编只附着Stack.c和Stack.h的文件,测试文件读者可以自己复制两个Stack文件后自行创建使用。

//Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int STDatatype;
typedef struct Stack
{
	STDatatype* a;
	int capacity;
	int top;
}Stack;

void StackInit(Stack* ps);
void StackDestroy(Stack* ps);
void StackPush(Stack* ps, STDatatype x);
void StackPop(Stack* ps);
STDatatype StackTop(Stack* ps);
bool StackEmpty(Stack* ps);
int StackSize(Stack* ps);


//Stack.c
#include"Stack.h"

//初始化空间
void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = (STDatatype*)malloc(sizeof(STDatatype) * 4);
	if (ps->a == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	ps->capacity = 4;
	ps->top = 0;
}

//销毁
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

void StackPush(Stack* ps,STDatatype x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		STDatatype* tmp = (STDatatype*)realloc(ps->a, 2 * ps->capacity * sizeof(STDatatype));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity *= 2;
	}
	ps->a[ps->top] = x;
	ps->top++;
}


void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);
	//assert();
	ps->top--;
}

//返回栈顶元素
STDatatype StackTop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->a[ps->top-1];
}

bool StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->top == 0;
}

int StackSize(Stack* ps)
{
	assert(ps);
	return ps->top;
}

这便是栈的介绍啦,希望大家多多支持。

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值