数据结构-栈详解(大概)

I 栈的概念

I.I 栈

一种特殊的线性表,其只允许在固定的一段进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的元素遵守后进先出(先进后出也可以)LIFO(Last in First Out)的原则。

I.II 压栈

栈的插入操作叫做进栈/压栈/入栈,入数据再栈顶。

I.III 出栈

栈的说着拿出操作叫做出栈。出数据也在栈顶。

这里是引用

II.栈的实现

II.I图解

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。数组的实现我们可以画个图理解理解,如图所示:

这里是引用

链表指针稍微复杂了些。
进栈出栈的示意图为:

这里是引用

这里是引用

II.II代码的实现

下面我们就用数组的形式来实现栈

II.II.I 大体框架

当我们对于链表的学习已经完成后,我们感受到数组的书写相对简单一些,和顺序表的书写一样,我们先创建三个test.c,Stack.c,Stack.h的文件。再头文件中创建结构体,代码如下:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int capacity;
	int top;
}ST;

接着书写我们要实现的接口函数:

void StackInit(ST* ps);
void StackPush(ST* ps, STDataType x);
void StackDestroy(ST* ps);
void StackPop(ST* ps);
STDataType StackTop(ST* ps);
int StackSize(ST* ps);// size指向栈顶的下一个
bool StackEmpty(ST* ps); //判断栈是否为空 

注意:我们在使用boll这个函数的时候需要引入一个头文件stdbool.h。这样才能正常使用。

II.II.II 栈的初始化

代码如下:

void StackInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}

简洁明了,一目了然。先断言,后变空,最后两个指向0.(顺口溜)然后我们就嘎嘎写完了。
在这里ps->top可以有两种写法。一种是上面代码将其变为0,另外一种就是将其变为-1。

这里是引用

II.II.III 入栈

和上上期我们说的顺序表一样,断言以后。我们首先考虑的是空间是否足够满不满的问题。满了,咱就给它扩容。一顿操作之后。我们通过指针指向了新的空间,最后实现入栈的操作,代码如下:

void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	//考虑空间是否已满的情况
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		else
			ps->a = tmp;
		ps->capacity = newCapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}

II.II.IV 栈的销毁

我们直接将ps->a给free掉,剩下照常,代码如下:

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

II.II.V判断栈是否为空

第一种方法,如果我们ps指向的top为真,那我们就返回真值否则返回假。

 if (ps -> top == 0)
	    {
             return true;
		}
		else
		{
		  return false;
		 }

事实上我们可以只用一行代码搞定这上面的内容

bool StackEmpty(ST* ps) //判断栈是否为空
{
	assert(ps);
	/*
	*    if (ps -> top == 0)
	    {
             return true;
		}
		else
		{
		  return false;
		 }
	*/
	return ps->top == 0;
}

咱直接return 。

II.II.VI出栈

出栈我们首先要用到上面判断栈是否为空,判断后直接top–就完啦。

void StackPop(ST* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	ps->top--;
}

II.II.VII获取栈顶元素

同上判断栈是否为空,判断后我们注意由于我们初始化栈的时候ps->top=0因此我们的top指向的是后面一个元素,获取的时候就要减减来找到。

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

II.II.VIII获取栈中有效元素个数

int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;// size指向栈顶的下一个,
}

额,依然不复杂。写道这里我们所有关于栈的书写就写完了。

III 全部代码

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;
}ST;

void StackInit(ST* ps);// 初始化栈
void StackPush(ST* ps, STDataType x);// 入栈
void StackDestroy(ST* ps);// 销毁栈
void StackPop(ST* ps);// 出栈
STDataType StackTop(ST* ps);// 获取栈顶元素
int StackSize(ST* ps);// 获取栈中有效元素个数
bool StackEmpty(ST* ps); //判断栈是否为空 

Stack.c

#include "Stack.h"

void StackInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}

void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	//考虑空间是否已满的情况
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		else
			ps->a = tmp;
		ps->capacity = newCapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}

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

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];
}

int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;// size指向栈顶的下一个,
}

bool StackEmpty(ST* ps) //判断栈是否为空
{
	assert(ps);
	/*
	*    if (ps -> top == 0)
	    {
             return true;
		}
		else
		{
		  return false;
		 }
	*/
	return ps->top == 0;
}

test.c

#include "Stack.h"

void TestStack1()
{
	ST st;
	StackInit(&st);

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

	StackPop(&st);
	StackPop(&st);
	StackPop(&st);
	StackPop(&st);

	//printf("%d", StackTop(&st));
	StackDestroy(&st);
}

void TestStack2()
{
	ST st;
	StackInit(&st);

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

	printf("%d ", StackTop(&st));
	StackPop(&st);                   // 3 和 4出栈了
	printf("%d ", StackTop(&st));
	StackPop(&st);           

	StackPush(&st, 5);
	StackPush(&st, 6);


	while (!StackEmpty(&st)) // 我们要先取栈顶的数据
	{
		printf("%d ", StackTop(&st));
		StackPop(&st);
	}

	StackDestroy(&st);
}

int main()
{
	TestStack1();
	TestStack2();
	return 0;
 }

运行结果完美的实现了先进后出,后进先出的原则

这里是引用

在main函数中我们已经先将3和4先出栈了,所以他们先打印出来,然后才是6521。

IV 总结

经过开始的顺序表的书写以及链表的学习应用,我们发现栈的书写还是相对容易的。要想学好数据结构还是要多多练习,多刷OJ题,希望我的文章对你能有所帮助,欢迎各位大佬在评论区纠正我的错误。期待的话,请多多给我点赞吧
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值