c语言实现栈


前言

在学习完链表之后,接下来就要了解另外的两个常用的线性数据结构,栈和队列。

一、栈的特征

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

出栈:栈的删除操作叫做出栈。出数据也在栈顶。
在这里插入图片描述

二、栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。

1、栈的设计

我们可以直接使用一个定长数组来设计栈,然后每次插入和删除数据都在数组的末尾进行尾插和尾删。该设计和顺序表中的静态顺序表的设计一致,就是栈只允许在栈顶进行元素的删除和插入。

#define N 10
typedef int STDataType;
typedef struct Stack
{
	STDataType a[N];
	int top;  //栈顶
}ST;

但是使用定长数组设计时,如果进入栈的数据太多还需要扩容,进入栈的数据太少时又浪费空间,所以还可以使用和动态顺序表一致的设计,即使用动态开辟的数组。

typedef int STDataType;
typedef struct Stack
{
	STDataType* data;
	int top;  //栈顶
	int capacity;  //用来记录动态数组的大小,当栈满时再开辟空间。
}ST;

2、栈的初始化和销毁

栈的初始化就是将创建的ST结构体中用来存数据的动态数组的指针先置为NULL,避免其为野指针,将动态数组的大小capacity置为0。然后将top先置为0或-1。
当将top置为0时,当要入栈时,就先将数组中下标为top的元素中放入数据,然后top再++,此时下标为top-1的元素就为栈顶元素,top表示了栈中的元素。
在这里插入图片描述
当将top置为-1时,当有元素要入栈时,就需要先将top++,然后再将数组中下标为top的元素中放入数据。因为top为-1时指向的是栈底的下面,需要先将top++,然后才能在栈底放入第一个元素。此时top指向的就是栈顶元素,当要返回栈顶元素时直接将数组中下标为top的元素返回即可,但是要计算栈是否为空时,就需要判断top==-1。
在这里插入图片描述
下面实现栈的操作是按照top刚开始设为0而实现的。即真正的栈顶是top下面的那个元素,此时top指向的元素数据为空。所以当返回栈顶元素时,需要将下标为top-1的元素返回。top的值刚好就是栈中元素的值,所以判断栈空时就用top==0来判断。
在这里插入图片描述

栈的初始化。

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

栈的销毁就是将动态申请的数组的内存释放。

void StackDestory(ST* ps)
{
	assert(ps);
	//将动态数组申请的空间都释放。
	free(ps->data);
	ps->data = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

3、元素的入栈和出栈

元素入栈就相当于在数组的尾部插入元素。但是元素在入栈前要判断一下此时栈是否满了,输入满了就需要再将栈扩容。

void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	//当栈的容量不够时
	if (ps->top == ps->capacity)
	{
		//当动态数组的大小为0时,就申请4个空间;当动态数组大小不为0时,就说明栈满了,需要扩容。
		int newCapacity = (ps->capacity == 0) ? 4 : (ps->capacity * 2);
		//给动态数组申请空间,用来存放数据
		STDataType* tmp = (STDataType*)realloc(ps->data, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->data = tmp;
		ps->capacity = newCapacity;
	}
	//将数据存到数组的末尾,然后栈顶向后移。
	ps->data[ps->top] = x;
	ps->top++;
}

元素出栈就相当于将数组末尾的数据删除,但是在出栈前要判断此时栈是否为空,如果为空就没有元素可以出栈。
判断栈空的函数。

bool StackEmpty(ST* ps)
{
	assert(ps);
	/*if (ps->top <= 0)
	{
		return true;
	}
	else
	{
		return false;
	}*/

	return ps->top == 0;
}

出栈的函数。

void StackPop(ST* ps)
{
	assert(ps);
	//出栈时要检查栈中有没有元素,有元素才可以出栈,没有元素就不可以出栈
	assert(!StackEmpty(ps));
	ps->top--;
}

4、返回栈顶元素

返回栈顶元素就是将此时栈顶的元素返回,因为此时top指向的是下一个要存数据的位置,此位置当前还没有数据,所以当返回栈顶元素时,实际返回的是数组下标为top-1的元素的值。

STDataType StackTop(ST* ps)
{
	assert(ps);
	//出栈时要检查栈中有没有元素,有元素才可以出栈,没有元素就不可以出栈
	assert(!StackEmpty(ps));
	return ps->data[(ps->top)-1];
}

三、栈的应用

经过上面的介绍我们发现栈的实现与顺序表的实现类似,差别就是栈只允许在末尾进行插入和删除。那么栈这个数据结构怎么应用呢?我们可以通过一个经典题来体会栈的特点。
题目
在这里插入图片描述
题目链接

题目分析
该问题就可以使用栈来解决,遍历字符串,当遇到左括号 ( [ { 时,就将这些左括号进行压栈,当遇到右括号 ) ] }时,就取出栈顶元素来与当前括号相匹配,如果两个括号类型一致就将该左括号出栈,并且访问字符串的下一个括号。如果两个括号匹配不一致,则返回false。当匹配到字符串末尾时,此时栈也为空,说明这些左括号和右括号都匹配,此时返回true。
代码
这个题中用到了我们上面写的一些栈的操作,需要将这些对应的函数都放到代码里面,这样提交时才不会报错。


#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
//静态数组实现栈
//#define N 10
//typedef int STDataType;
//typedef struct Stack
//{
//	STDataType a[N];
//	int top;
//}ST;

//动态数组实现栈
typedef char STDataType;
typedef struct Stack
{
	STDataType* data;
	int top;
	int capacity;
}ST;

//栈的初始化
void StackInit(ST* ps);

//检查栈空
bool StackEmpty(ST* ps);

//栈的销毁
void StackDestory(ST* ps);

//元素入栈
void StackPush(ST* ps, STDataType x);

//元素出栈
void StackPop(ST* ps);

//返回栈顶元素
STDataType StackTop(ST* ps);

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

bool StackEmpty(ST* ps)
{
	assert(ps);
	/*if (ps->top <= 0)
	{
		return true;
	}
	else
	{
		return false;
	}*/

	return ps->top == 0;
}

void StackDestory(ST* ps)
{
	assert(ps);
	//将动态数组申请的空间都释放。
	free(ps->data);
	ps->data = NULL;
	ps->top = 0;
	ps->capacity = 0;
}


void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	//当栈的容量不够时
	if (ps->top == ps->capacity)
	{
		//当动态数组的大小为0时,就申请4个空间;当动态数组大小不为0时,就说明栈满了,需要扩容。
		int newCapacity = (ps->capacity == 0) ? 4 : (ps->capacity * 2);
		//给动态数组申请空间,用来存放数据
		STDataType* tmp = (STDataType*)realloc(ps->data, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->data = tmp;
		ps->capacity = newCapacity;
	}
	//将数据存到数组的末尾,然后栈顶向后移。
	ps->data[ps->top] = x;
	ps->top++;
}

void StackPop(ST* ps)
{
	assert(ps);
	//出栈时要检查栈中有没有元素,有元素才可以出栈,没有元素就不可以出栈
	assert(!StackEmpty(ps));
	ps->top--;
}

STDataType StackTop(ST* ps)
{
	assert(ps);
	//出栈时要检查栈中有没有元素,有元素才可以出栈,没有元素就不可以出栈
	assert(!StackEmpty(ps));
	return ps->data[(ps->top)-1];
}

bool isValid(char * s){
    ST st; 
		//创建一个栈并且初始化。
    StackInit(&st);

		//遍历括号字符串
    while(*s!='\0')
    {
			//当为左括号时,就进行压栈
        if((*s=='(') || (*s=='[') || (*s=='{'))
        {
            StackPush(&st,*s);
            s++;
        }
        else
        {
					//如果为右括号,并且此时栈已空,就说明括号不匹配,
						if(StackEmpty(&st))
            {
								StackDestory(&st);
                return false;
            }
						//取出栈顶元素
            char top = StackTop(&st);
						//并且将栈顶元素进行出栈
            StackPop(&st);  
						//如果两个括号匹配就访问下一个字符
            if((top=='(' && *s==')') || (top=='[' && *s==']') || (top=='{' && *s=='}'))
            {
                s++;
            }
            else
            {
							//如果两个括号不匹配就返回false
							StackDestory(&st);
                return false;
            }
        }
    }

		//如果字符串访问完后,此时栈中还有左括号,说明括号不匹配
    if(!StackEmpty(&st))
    {
			StackDestory(&st);
        return false;
    }
		StackDestory(&st);
		//当字符串访问完,且此时栈为空,说明括号都匹配,
    return true;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值