【数据结构】栈

我们接着链表来讲,今天来聊聊栈和队列


栈的概念以及结构

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

注意:我们这里讲的栈是数据结构的栈,与操作系统中函数栈帧的栈是两个完全不同的概念

下面引用一张图片:
在这里插入图片描述
这张图片很好的向我们诠释了栈的基本流程
入栈数据可以一次入一个,也可以一次入多个,一定要具体分析,大家不要踩坑了

栈的实现

Stack.h头文件

1、需要包含的头文件

值得一提的是布尔值是c99中新加的C语言语法,使用布尔值要引用头文件stdbool.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//C语言使用布尔值要引用该头文件

2、结构体的定义

老规矩,还是将int类型重定义,这样在进行其他数据类型操作的时候只需要修改int即可
与顺序表一样,栈也有静态和动态的区别,这里我们采用动态实现

//#define max 100
//typedef struct Stack
//{
//	TDataType data[max];
//	int top;//有效数据个数
//}ST;   静态栈
typedef int TDataType;

typedef struct Stack
{
	TDataType* data;
	int top;//有效数据个数
	int capacity;//容量
}ST;

3、函数的声明

void StackInit(ST* ps);//初始化
void StackDestory(ST* ps);//释放空间
void StackPush(ST* ps, TDataType x);//入栈
void StackPop(ST* ps);//出栈
TDataType StackTop(ST* ps);//栈顶元素
bool StacEmpty(ST* ps);//判断栈是否为空
size_t StackSize(ST* ps);//查看栈里面有剩多少个元素

这里小编就实现了这么些接口,大家可以试着写一些新接口

Stack.c源文件

接下来就是每个函数的定义了

1、初始化

void StackInit(ST* ps)//初始化
{
	assert(ps);
	ps->data = NULL;//将data置空
	ps->top = ps->capacity = 0;//让有效数据个数和容量都为0,让top从0开始
}

这里top初始化为0是有讲究的,具体有什么作用我们等下就知道了

2、释放空间

void StackDestory(ST* ps)//释放空间
{
	assert(ps);
	free(ps->data);//数组是连续开辟的,释放应该一次性释放完
	ps->data = NULL;
	ps->top = ps->capacity = 0;
}

3、入栈数据

这里就要提到为什么上面要初始化top为0了
在这里插入图片描述
所以我们需要考虑是初始化为0还是-1了
各有各的好,这里小编初始化的是0,大家可以试一试初始化为-1的情况

同时,入栈是需要判断空间是否扩容的,栈也只有入栈数据要不要考虑扩容,其他地方不需要。
所以扩容不用像前面的链表,顺序表写一个单独函数出来

void StackPush(ST* ps, TDataType x)//入栈
{
	assert(ps);
	if (ps->top == ps->capacity)//如果入栈数据个数等于容量就扩容
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//第一次直接扩容出4个数据空间,往后每次扩容原来容量的2倍
		TDataType* newnode = (TDataType*)realloc(ps->data, newcapacity*sizeof(TDataType));//进行扩容
		if (newnode == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->data = newnode;//将扩容地址交给原来内存指针维护
		ps->capacity = newcapacity;//将新容量赋值给capacity
	}
	ps->data[ps->top] = x;//入栈我们需要的数据
	ps->top++;//top的位置向后面移动一步
}

4、出栈数据(删除)

直接弹出栈顶的数据,因为栈是后进先出的

void StackPop(ST* ps)//出栈
{
	assert(ps);
	assert(!StacEmpty(ps));//如果栈为空,断言生效
	ps->top--;//直接减减,弹出栈顶的数据
}

5、取出栈顶数据

TDataType StackTop(ST* ps)//栈顶数据
{
	assert(ps);
	return ps->data[ps->top - 1];//因为初始化为0,所以top处于栈顶位置的下一个位置,减1才是栈顶数据
}

6、判断栈是否为空

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

7、查看栈有多少个数据



size_t StackSize(ST* ps)//栈还剩下多少元素
{
	assert(ps);
	return ps->top;//top是多少就有多少个数据
}

完整代码

Stack.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//C语言使用布尔值要引用该头文件

//#define max 100
//typedef struct Stack
//{
//	TDataType data[max];
//	int top;//有效数据个数
//}ST;   静态栈

typedef int TDataType;

typedef struct Stack
{
	TDataType* data;
	int top;//有效数据个数
	int capacity;//容量
}ST;

void StackInit(ST* ps);//初始化
void StackDestory(ST* ps);//释放空间
void StackPush(ST* ps, TDataType x);//入栈
void StackPop(ST* ps);//出栈
TDataType StackTop(ST* ps);//栈顶元素
bool StacEmpty(ST* ps);//判断栈是否为空
size_t StackSize(ST* ps);//查看栈里面有剩多少个元素

Stack.c

#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"
void StackInit(ST* ps)//初始化
{
	assert(ps);
	ps->data = NULL;//将data置空
	ps->top = ps->capacity = 0;//让有效数据个数和容量都为0,让top从0开始
}


void StackDestory(ST* ps)//释放空间
{
	assert(ps);
	free(ps->data);//数组是连续开辟的,释放应该一次性释放完
	ps->data = NULL;
	ps->top = ps->capacity = 0;
}


void StackPush(ST* ps, TDataType x)//入栈
{
	assert(ps);
	if (ps->top == ps->capacity)//如果入栈数据个数等于容量就扩容
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//第一次直接扩容出4个数据空间,往后每次扩容原来容量的2倍
		TDataType* newnode = (TDataType*)realloc(ps->data, newcapacity*sizeof(TDataType));//进行扩容
		if (newnode == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->data = newnode;//将扩容地址交给原来内存指针维护
		ps->capacity = newcapacity;//将新容量赋值给capacity
	}
	ps->data[ps->top] = x;//入栈我们需要的数据
	ps->top++;//top的位置向后面移动一步
}


void StackPop(ST* ps)//出栈
{
	assert(ps);
	assert(!StacEmpty(ps));//如果栈为空,断言生效
	ps->top--;//直接减减,弹出栈顶的数据
}


TDataType StackTop(ST* ps)//栈顶数据
{
	assert(ps);
	return ps->data[ps->top - 1];//因为初始化为0,所以top处于栈顶位置的下一个位置,减1才是栈顶数据
}


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


size_t StackSize(ST* ps)//栈还剩下多少元素
{
	assert(ps);
	return ps->top;//top是多少就有多少个数据
}

test.c

#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"
//void Test1()
//{
//	ST p;
//	StackInit(&p);
//
//	StackPush(&p, 1);
//	StackPush(&p, 2);
//	StackPush(&p, 3);
//	StackPush(&p, 4);
//	StackPush(&p, 5);
//	while (!StacEmpty(&p))
//	{
//		printf("%d ", StackTop(&p));
//		StackPop(&p);
//	}
//	printf("\n");
//	printf("%zd\n", StackSize(&p));
//	StackDestory(&p);
//}
void menu()
{
	printf("-------------------------------\n");
	printf("***1、入栈数据   2、出栈数据***\n");
	printf("*******3、查看栈数据个数*******\n");
	printf("*******4、判断栈是否为空*******\n");
	printf("*******5、打印栈顶处数据*******\n");
	printf("******6、出栈打印所有数据******\n");
	printf("*******************************\n");
	printf("-------------------------------\n");
}
int main()
{
	//Test1();
	ST p;
	StackInit(&p);
	int n = 0;
	int x = 0;
	int y = 0;
	do
	{
		menu();
		printf("请选择操作:");
		scanf("%d", &n);
		switch (n)
		{
		case 1:
			printf("请输入入栈数据,以-1截止\n");
			do
			{
				scanf("%d", &x);
				//StackPush(&p, x);//入栈函数接口在上面会将-1一起入栈
				if (x == -1)
				{
					break;
				}
				StackPush(&p, x);//入栈函数接口在下面不会将-1一起入栈
			} while (x != -1);
			break;
		case 2:
			StackPop(&p);
			break;
		case 3:
			printf("%zd\n", StackSize(&p));
			break;
		case 4:
			if (StacEmpty(&p))
				printf("没有数据,栈为空\n");
			else
				printf("有数据,栈不为空\n");
			break;
		case 5:
			printf("%d\n", StackTop(&p));
			break;
		case 6:
			while (StackSize(&p))
			{
				printf("%d ", StackTop(&p));
				StackPop(&p);
			}
			printf("\n");
			break;
		case 0:
			StackDestory(&p);
			printf("退出程序\n");
			break;
		default :
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (n);
	return 0;
}

栈的OJ题

OJ练习题

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

1、 左括号必须用相同类型的右括号闭合。
2、左括号必须以正确的顺序闭合。

在这里插入图片描述
我已经将这个题注意点标出来了,括号就3种,括号顺序很重要,不能直接数括号

接下来我们来解决:

typedef int TDataType;
typedef struct Stack
{
	TDataType* data;
	int top;//有效数据个数
	int capacity;//容量
}ST;
void StackInit(ST* ps);//初始化
void StackDestory(ST* ps);//释放空间
void StackPush(ST* ps, TDataType x);//入栈
void StackPop(ST* ps);//出栈
TDataType StackTop(ST* ps);//栈顶元素
bool StacEmpty(ST* ps);//判断栈是否为空
size_t StackSize(ST* ps);//查看栈里面有剩多少个元素

void StackInit(ST* ps)//初始化
{
	assert(ps);
	ps->data = NULL;//将data置空
	ps->top = ps->capacity = 0;//让有效数据个数和容量都为0,让top从0开始
}
void StackDestory(ST* ps)//释放空间
{
	assert(ps);
	free(ps->data);//数组是连续开辟的,释放应该一次性释放完
	ps->data = NULL;
	ps->top = ps->capacity = 0;
}
void StackPush(ST* ps, TDataType x)//入栈
{
	assert(ps);
	if (ps->top == ps->capacity)//如果入栈数据个数等于容量就扩容
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//第一次直接扩容出4个数据空间,往后每次扩容原来容量的2倍
		TDataType* newnode = (TDataType*)realloc(ps->data, newcapacity*sizeof(TDataType));//进行扩容
		if (newnode == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->data = newnode;//将扩容地址交给原来内存指针维护
		ps->capacity = newcapacity;//将新容量赋值给capacity
	}
	ps->data[ps->top] = x;//入栈我们需要的数据
	ps->top++;//top的位置向后面移动一步
}
void StackPop(ST* ps)//出栈
{
	assert(ps);
	assert(!StacEmpty(ps));//如果栈为空,断言生效
	ps->top--;//直接减减,弹出栈顶的数据
}
TDataType StackTop(ST* ps)//栈顶数据
{
	assert(ps);
	return ps->data[ps->top - 1];//因为初始化为0,所以top处于栈顶位置的下一个位置,减1才是栈顶数据
}
bool StacEmpty(ST* ps)//判断栈是否为空
{
	assert(ps);
	return ps->top == 0;
}
size_t StackSize(ST* ps)//栈还剩下多少元素
{
	assert(ps);
	return ps->top;//top是多少就有多少个数据
}

bool isValid(char * s){
    ST p;
    StackInit(&p);//初始化
    while(*s!='\0')
    {
        if(*s=='(' || *s=='[' || *s=='{')
        {
            StackPush(&p,*s);//如果是左括号都入栈
        }
        else
        {
            if(StacEmpty(&p))//如果全是右括号,那么没有数据入栈,栈就为空,直接返回false即可
            {
                StackDestory(&p);//返回记得释放动态内存
                return false;
            }
            char ch=StackTop(&p);//拿到栈顶数据
            StackPop(&p);//删除/弹出栈顶数据
            if((*s==')' && ch!='(')
             ||(*s==']' && ch!='[')
             ||(*s=='}' && ch!='{'))//当来到这里时,指针s已经走到第一个右括号了,此时栈里面放的都是左括号,ch就是栈顶的符号(也就是最后最后一个左括号),这里拿到之后弹出了栈顶符号(就是下一次的ch变成了倒数第二个左括号,以此类推)。然后进行匹配,如果左右括号对应上了就进行下一步++s,如果不对应就说明字符串左右括号不匹配,直接false
             {
                 StackDestory(&p);//返回记得释放动态内存
                 return false;
             }
        }
        ++s;//指针向后移动一位
    }
    bool flag=StacEmpty(&p);//这里之所以用一个布尔型的flag是因为如果入的都是左括号,直接会跳出循环来到这里,那么就直接打印结果了。这里判断一下如果都是左括号那么栈不为空,就是一个假值,flag的值就为就是false
    StackDestory(&p);//返回记得释放动态内存
    return flag;//返回flag,不能直接返回true,因为都是左括号直接跳出循环来这返回了
}

在这里插入图片描述

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值