深入了解栈(走进栈的世界)

目录

栈的概念及其结构

栈的实现

为何使用数组?

从Push来看:

从Pop来看:

栈的结构:

静态栈:

动态栈:

头文件(stack.h):

实现栈的功能:

初始化栈:

销毁栈:

入栈:

出栈:

这里为什么只需要top--呢?

获取栈顶元素

获取栈中有效元素个数  

检测栈是否为空 


欢迎来到栈的领域,想必你也看了很多的数据结构吧!希望这篇将会给你带来不一样的感想!!!

本文将会涉及到顺序表的内容:

顺序表的实现(迈入数据结构的大门)(1)-CSDN博客

顺序表的实现(迈入数据结构的大门)(2)-CSDN博客


栈的概念及其结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFOLast In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。

栈:先进后出,后进先出(Last In First Out);

栈的实现

栈的实现一般使用数组和链表实现,相对而言数组更加方便,因为数组插入数据的代价更小;

结构的选择:为何使用数组?

从Push来看:

对数组来讲:我们只需在栈顶(数组尾部)插入即可

对链表来讲:我们需要每次pcur=head,循环找尾节点

从Pop来看:

与Push原因类似;

栈的结构:

与顺序表类似分为两种:

静态栈:

// 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
typedef int STDataType;
//利于变换类型
#define N 10
typedef struct Stack
{
 STDataType _a[N];
 int _top; // 栈顶
}Stack;

动态栈:

// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
 STDataType* _a;
 int _top; // 栈顶
 int _capacity; // 容量
}Stack;

头文件(stack.h):

#pragma once

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.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);

实现栈的功能:

// 初始化栈
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);

初始化栈:

大多数据结构的开门必是初始化

// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
    STDataType* a;
    int top;        // 栈顶大小
    int capacity;  // 容量 
}Stack;

栈中需要初始化数据的有三个,数组(置NULL)、栈顶大小(置0)、容量(置0);

// 初始化栈 
void StackInit(Stack* ps) {
	//对于传址来讲,第一步的判空是尤为重要的
	assert(ps);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

栈的初始化很快就完成了;

销毁栈:

从这里就可以看出,在栈的销毁中,数组更为便捷

void StackDestroy(Stack* ps) {
	assert(ps);
	//自内而外进行释放摧毁
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
	free(ps);
	ps = NULL;
}

入栈:

对于入栈,我们想要存放数据,我们需要判断是否还有空间,所以我么需要开辟空间和扩容

当面对需要开辟空间和扩容时,可以使用realloc;

// 入栈 
void StackPush(Stack* ps, STDataType data) {
	assert(ps);
	//扩容
	if (ps->top == ps->capacity) {
		//三目操作符
		//ps->capacity是否为0;为0执行4,不为0,执行ps->capacity * 2
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		//这里的realloc不仅仅具有realloc自身的功能,还具有malloc的功能;
		//仔细看这里并没有直接让ps->a对接realloc,以防开辟失败
		STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
		if (tmp == NULL) {
			perror("realloc fail!");
			//直接退出程序
			exit(1);
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = data;
	ps->top++;
}

//这里的realloc不仅仅具有realloc自身的功能,还具有malloc的功能;
//仔细看这里并没有直接让ps->a对接realloc,以防开辟失败

出栈:

// 出栈 
void StackPop(Stack* ps) {
	assert(ps);
	//因为要出栈,所以为了防止top为NULL,对NULL进行操作的危险
	assert(ps->top);
	ps->top--;
}
这里为什么只需要top--呢?

当我们下一次插入时,会将原先pop的位置覆盖掉(也是可以将top的值删去的,在这里只是为了便洁);

获取栈顶元素

// 获取栈顶元素 
STDataType StackTop(Stack* ps) {
	assert(ps);
	//得现存有数据
	assert(ps->top);
	//因为top初始为0,当插入一个数据时,数组下标为0,而top++,为1;
	return ps->a[ps->top - 1];
}

    //因为top初始为0,当插入一个数据时,数组下标为0,而top++,为1;

获取栈中有效元素个数  

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

	return ps->top;
}

检测栈是否为空 

// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps) {
	assert(ps);
	return ps->top == 0 ? -1 : 0;
}

这样关于栈的内容就完成了


以下为完整代码的展示:

 stack.h:

#pragma once

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.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:

#define _CRT_SECURE_NO_WARNINGS
#include"stack.h"
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
// 初始化栈 
void StackInit(Stack* ps) {
	//对于传址来讲,第一步的判空是尤为重要的
	assert(ps);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

// 入栈 
void StackPush(Stack* ps, STDataType data) {
	assert(ps);
	//扩容
	if (ps->top == ps->capacity) {
		//三目操作符
		//ps->capacity是否为0;为0执行4,不为0,执行ps->capacity * 2
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		//这里的realloc不仅仅具有realloc自身的功能,还具有malloc的功能;
		//仔细看这里并没有直接让ps->a对接realloc,以防开辟失败
		STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
		if (tmp == NULL) {
			perror("realloc fail!");
			//直接退出程序
			exit(1);
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}

	ps->a[ps->top] = data;
	ps->top++;

}

void StackDestroy(Stack* ps) {
	assert(ps);
	//自内而外进行释放摧毁
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
	free(ps);
	ps = NULL;
}


// 出栈 
void StackPop(Stack* ps) {
	assert(ps);
	//因为要出栈,所以为了防止top为NULL,对NULL进行操作的危险
	assert(ps->top);
	ps->top--;
}

// 获取栈顶元素 
STDataType StackTop(Stack* ps) {
	assert(ps);
	//得现存有数据
	assert(ps->top);
	//因为top初始为0,当插入一个数据时,数组下标为0,而top++,为1;
	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 ? -1 : 0;
}

你也可以试一试以下测试代码或手搓一个,以防错误!!!

test.c

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

//#include"stack.h"
//int main() {
//	Stack ST;
//
//	StackInit(&ST);
//	StackPush(&ST, 1);
//	StackPush(&ST, 2);
//	StackPush(&ST, 3);
//	StackPush(&ST, 4);
//	StackPush(&ST, 5);
//	while (!StackEmpty(&ST)) {
//		printf("%d ", StackTop(&ST));
//		STpop(&ST);
//	}
//	StackDestroy(&ST);
//	return 0;
//}

  • 28
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值