栈的实现和括号匹配问题

1.什么是栈

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

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶

出栈:栈的删除操作叫做出栈。出数据也在栈顶

后进先出 

2.栈的实现

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


第一步:创建一个头文件和两个源文件

在头文件中进行结构定义和函数声明

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;//栈顶
	int capacity;//容量
}ST;

//初始化和销毁
void STInit(ST* pst);
void STDestroy(ST* pst);

//入栈 出栈
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);

//取栈顶数据
STDataType STTop(ST* pst);

//判空
bool STEmpty(ST* pst);

//获取数据个数
int STSize(ST* pst);

第二步:栈的函数实现

初始化

void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	//top指向栈顶数据的下一个位置
	pst->top = 0;
	//top指向栈顶数据
	//pst->top = -1;
	pst->capacity = 0;
}

注意:top等于-1的时候top指向栈顶,top等于0的时候top指向栈顶的下一个位置,大家可以根据自己的情况选择。


销毁

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

传过来的地址不能为NULL如果为空我们就不能对他进行解引用,所以断言一下


入栈

void STPush(ST* pst, STDataType x)
{
	assert(pst);
	//扩容
	if (pst->top == pst->capacity)
	{
		int newcapcity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newcapcity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapcity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}

初始化的时候我们并没有开辟空间,所以我们需要先申请空间,先开辟4个空间不够再扩容,我们把开辟好的空间用一个临时变量保存起来然后对他进行判断,避免我们空间申请失败导致原地址丢失,然后更改空间容量大小,最后入数据


出栈

void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}

竟然要出数据那么肯定要有数据才能出,所以断言一下,我们直接top--就可以了


取栈顶数据

STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	return pst->a[pst->top - 1];
}

我们要有数据才能取数据所以加个断言,直接返回top-1的下标就是栈顶的数据


判空

bool STEmpty(ST* pst)
{
	assert(pst);
	return pst->top == 0;
}

top为0栈就为空


获取数据个数

int STSize(ST* pst)
{
	assert(pst);
	return pst->top;
}

top表示下标,也就是元素个数


最后一步:测试test.c

int main()
{
	ST s;
	STInit(&s);
	STPush(&s, 1);
	STPush(&s, 2);
	printf("%d\n", STTop(&s));

	STPush(&s, 3);
	STPush(&s, 4);
	while (!STEmpty(&s))
	{
		printf("%d\n", STTop(&s));
		STPop(&s);
	}
	STDestroy(&s);
	return 0;
}

注意:出栈可以边入边出,入栈1 2 3 4,出栈不一定是4 3 2 1


3.括号匹配问题

OJ链接:有效的括号

左括号必须和右括号相匹配必须是成对出现的,如果匹配就返回true否则返回false,这道题乍一看不好判断,其实我们可以用栈来解决,栈是后进先出的原则,如果是左括号就入栈,如果是右括号就出栈顶的左括号进行判断是否匹配,此时的栈里面都是左括号,这里我们的需求是后进先出,我们要让右括号和后进的左括号相匹配,这不就完美的匹配了后进先出。

因为C语言没有自带的栈所以我们需要把写好的栈放进去,C++会有自带的栈。

代码如下:

bool isValid(char* s) {

    ST st;
    STInit(&st);
    while(*s)
    {
        //左括号入栈
        if(*s == '(' || *s == '[' || *s == '{')
        {
            STPush(&st,*s);
        }
        //右括号取栈顶左括号尝试匹配
        else
        {
            if(STEmpty(&st))
            {
                STDestroy(&st);
                return false;
            }
            char top = STTop(&st);
            STPop(&st);
            //不匹配
            if((top == '(' && *s !=')')
            || (top == '[' && *s !=']')
            || (top == '{' && *s !='}'))
            {
                STDestroy(&st);
                return false;
            }
        }
        ++s;
    }
    //栈不为空,说明左括号比右括号多,数量不匹配
    bool ret = STEmpty(&st);
    STDestroy(&st);
    return ret;
}

全部代码如下:

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

//初始化和销毁
void STInit(ST* pst);
void STDestroy(ST* pst);

//入栈 出栈
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);

//取栈顶数据
STDataType STTop(ST* pst);

//判空
bool STEmpty(ST* pst);

//获取数据个数
int STSize(ST* pst);

//初始化和销毁
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	//top指向栈顶数据的下一个位置
	pst->top = 0;
	//top指向栈顶数据
	//pst->top = -1;
	pst->capacity = 0;
}
void STDestroy(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;
}

//入栈 出栈
void STPush(ST* pst, STDataType x)
{
	assert(pst);
	//扩容
	if (pst->top == pst->capacity)
	{
		int newcapcity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newcapcity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapcity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}
void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}

//取栈顶数据
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	return pst->a[pst->top - 1];
}

//判空
bool STEmpty(ST* pst)
{
	assert(pst);
	return pst->top == 0;
}

//获取数据个数
int STSize(ST* pst)
{
	assert(pst);
	return pst->top;
}

bool isValid(char* s) {

    ST st;
    STInit(&st);
    while(*s)
    {
        //左括号入栈
        if(*s == '(' || *s == '[' || *s == '{')
        {
            STPush(&st,*s);
        }
        //右括号取栈顶左括号尝试匹配
        else
        {
            if(STEmpty(&st))
            {
                STDestroy(&st);
                return false;
            }
            char top = STTop(&st);
            STPop(&st);
            //不匹配
            if((top == '(' && *s !=')')
            || (top == '[' && *s !=']')
            || (top == '{' && *s !='}'))
            {
                STDestroy(&st);
                return false;
            }
        }
        ++s;
    }
    //栈不为空,说明左括号比右括号多,数量不匹配
    bool ret = STEmpty(&st);
    STDestroy(&st);
    return ret;
}

 总结

栈是一种特殊的线性数据结构,它遵循后进先出(Last In, First Out,LIFO)的原则。栈的主要操作包括在栈顶插入元素(称为压栈,push)和从栈顶移除元素(称为弹栈,pop)。由于这种操作限制,栈在访问和操作元素时表现出一种特殊的顺序性。

栈的主要特点包括:

  • 1.后进先出(LIFO):栈中的数据元素按照它们进入栈的顺序的逆序进行排列。最后进入栈的元素将最先被移除,而最先进入栈的元素将最后被移除。
  • 2.操作受限:栈只允许在栈顶进行插入和移除操作。这种限制确保了栈的后进先出特性。
  • 3.应用广泛:栈在计算机科学和软件工程中有广泛的应用。它们常用于实现函数调用(函数调用栈)、表达式求值(算术表达式的括号匹配和计算顺序)、内存分配(如自动变量存储)等。栈还被用作一种数据结构和算法设计的辅助工具,例如在深度优先搜索(DFS)中,栈被用来保存待访问的节点。

栈的实现方式有多种,包括基于数组的静态栈和基于链表的动态栈等。这些实现方式各有优缺点,具体选择取决于应用场景和性能需求。在实际应用中,栈的使用通常需要与其他数据结构和算法相结合,以实现复杂的程序逻辑和功能。

栈的一个重要特性是它可以很容易地通过数组或链表来实现,并且具有常数时间的插入和删除操作(在栈顶)。这使得栈成为一种高效且灵活的数据结构,适用于需要按照特定顺序处理元素的情况。

相关推荐

  1. 简单应用:括号匹配

    2024-06-10 09:52:03       8 阅读

最近更新

  1. 地学类期刊最新CiteScore™ 汇总

    2024-06-10 09:52:03       0 阅读
  2. 怎么通过AI构架一个个人简介并且写出来

    2024-06-10 09:52:03       0 阅读
  3. C++240617

    C++240617

    2024-06-10 09:52:03      0 阅读
  4. 数据库修复实例2(副本出入口修复)

    2024-06-10 09:52:03       0 阅读
  5. Leetcode 415. 字符串相加-大数相加

    2024-06-10 09:52:03       0 阅读
  6. Docker使用心得

    2024-06-10 09:52:03       0 阅读
  7. 富格林:细心发现虚假确保安全

    2024-06-10 09:52:03       0 阅读

热门阅读

  1. 1341. 电影评分

    2024-06-10 09:52:03       6 阅读
  2. 如何学好量子计算机技术的两种思路

    2024-06-10 09:52:03       5 阅读
  3. 爬山算法详细介绍

    2024-06-10 09:52:03       9 阅读
  4. 4. 流程控制语句

    2024-06-10 09:52:03       7 阅读
  5. vscode 好用的插件

    2024-06-10 09:52:03       7 阅读
  6. 23种设计模式——创建型模式

    2024-06-10 09:52:03       6 阅读
  7. 2024年6月10日--6月16日(渲染+ue独立游戏)

    2024-06-10 09:52:03       7 阅读
  8. Terminal Multiplexer的使用

    2024-06-10 09:52:03       7 阅读
  9. 什么情况下需要用到动态IP

    2024-06-10 09:52:03       5 阅读
  10. node-mysql中占位符?的使用

    2024-06-10 09:52:03       7 阅读