C语言实现动态栈以及栈的实践——括号匹配问题

        栈也是基础数据结构线性表一种,它和之前说过的顺序表和链表的最大区别在于,它只能在一段进行存入数据或者取出数据,我们把数据进出的这一段称为栈顶,另外一段则称为栈底,由于栈只能在一段存入或者移出数据,所以它有一个很重要的特性——后进先出 LIFO(Last In First Out)。

目录

        一、动态栈的实现

        二、括号匹配问题


        一、动态栈的实现

        首先我们要先写一个栈的主体,其中有三个成员,一个是我们要存储的元素(这里我们用int来举例),一个是我们的容量大小,最后一个就是我们栈顶的位置。

//使用这种方法可以在修改栈内成员类型的时候,只需要修改这一个地方即可
typedef int STDataType;

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

        我们实现的是一个动态的栈,栈的大小是随着我们插入数据而扩大的,因此我们还需要将有两个函数,一个是初始化栈,另外一个则是销毁栈。

        因为我们要改变形参内容,所以我们在传参的时候需要传递结构体指针。

        初始化栈的时候我们可以给定一个初始的大小,这里我就把初始化的大小设置为0,因为此时容量为0,所以存储数据的指针也是指向空的;此时的top也为0。

        这里的top就可以分为两种情况,第一种是把top的初始值设置为0,因为此时栈内并没有元素,但top已经指向了0的位置,所以此时的top表示的是栈顶元素的下一个位置而不是栈顶元素的位置。

        如果要表示栈顶元素的位置,应当把top的初始值设置为-1,当第一个元素入栈以后,top变为0,数组中的第0个元素就是我们存入的第一个数据,换个方式说,如果我们把top的初始值设置为-1,此时top表示的是存储元素数组的小标。

void STInit(ST* st)
{
    //这个栈不存在的时候报错
	assert(st);

	st->a = NULL;
	st->capacity = 0;
    //这里我选择了用top表示栈顶数据下一个位置的表示方式
	st->top = 0;
}

void STDestory(ST* st)
{
	assert(st);
    //先释放动态申请的空间
	free(st->a);
    //再把指向这块空间的指针指为空
	st->a = NULL;
	st->capacity = 0;
	st->top = 0;
}

        接下来就是最重要的把数据放入栈中,首先我们可以考虑简单的情况,当这个栈内还有空间时,也就是栈不满时的情况。因为我们top表示的是栈顶数据的下一个位置,所以此时数据a的top位置也就是空闲的状态,我们只需要把数据放到这个位置上就可以了,插入数据以后再++top就行了。

        当top的值和capacity的值相等的时候,也就是栈满的时候,这个时候我们就要申请额外的空间来存放我们的数据了,此时realloc函数正好能满足我们的需求。当空间大小允许时,realloc函数会在原地将空间扩展成我们需要的大小,当空间大小不足时,realloc函数会自己选择另外一块大小合适空间开辟我们需要的大小,同时将原来空间内的数据复制到新的空间内,再将原来的空间给释放掉;当realloc函数的第一个参数为空指针时,realloc函数的功能与malloc函数一致。

        当空间不足的时候,我们就可以把容量扩展为原来的2倍,但是并不能直接用简单的把capacity*2得到新的capacity,因为我们给capacity的初值是0,这样的话我们可以用一个三目操作符来解决这个问题,newcapacity = st->capacity == 0 ? 4 : st->capacity * 2,当capacity的值为0时,我们的newcapacity就赋值为4,当capacity的值不为0时,就把newcapa的值赋值为capacity的二倍。

void STPush(ST* st, STDataType x)
{
	assert(st);

	if (st->top == st->capacity)
	{
		int newcapacity = st->capacity == 0 ? 4 : st->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(st->a, sizeof(STDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail!");
			return;
		}
		st->a = tmp;
		st->capacity = newcapacity;
	}
	st->a[st->top] = x;
	st->top++;
}

        栈顶元素出栈我们只需要把top的--就可以做到栈顶元素出栈了,并不需要管栈顶元素的数据,因为我们是通过top来管理我们的栈顶的位置的,所以我们是取不到这个数据的,就算不把这个数据置0,下次再到这个位置入栈时,新的元素就会把它覆盖掉。

        同时我们在出栈的时候一定要保障栈内是有元素的才能进行出栈操作,也就是top一定要大于0时,我们才能进行出栈操作。

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

	st->top -- ;
}

        因为top表示的是栈顶元素的下一个位置,所以要取得栈顶数据的实际位置是top-1。

//取栈顶元素
STDataType STTop(ST* st)
{
	assert(st);
	assert(st->top > 0);

	return st->a[st->top - 1];
}

         因为数组的下标是从0开始的,top表示的是栈顶元素的下一个位置,所以此时top表示的正好是栈内数据的个数。

//栈内元素个数
int STSize(ST* st)
{
	assert(st);

	return st->top;
}

        判空

        因为top表示的是栈内元素的个数,所以当top为0时,此时栈内就是空的。此时我们就可以用一个等式来判断,当top为0时,此时等式的结果为真,函数的返回值为true;若此时top不为0,等式的结果为假,函数的返回值为false。

bool STEmpty(ST* st)
{
	assert(st);

	return st->top == 0;
}

        二、括号匹配问题

        括号匹配问题就是经典的需要用栈解决的问题。

        

        从题目我们可以清楚的知道,不仅每个左括号要和相应的右括号相匹配,而且匹配的顺序还要相同,并且不能有多余的括号。

        因此我们可以有一个思路,我们可以一个字符一个字符的遍历这个字符串s,当遍历到左括号时,我们就把这个括号入栈;当当前字符不是左括号时,因为在这个题目中,只有左括号和右括号,所以如果不是左括号的时候,那就一定是右括号,我们就先取出栈顶元素,再把栈顶元素出栈;然后再看取出的这个栈顶元素和我们的当前遍历到的这个元素是否匹配,如果是匹配的,我们不需要采取多余的动作,继续遍历当前字符串即可;若这个元素与栈顶元素不匹配,说明,出现了不匹配的括号,此时就可以知道这个括号不匹配的了,就可以不用继续向后比较了。当比较完所有的括号时,也并不一定就是匹配的了,因为可能会有多余的左括号,所以在遍历完字符s以后,我们要看看栈是不是为空,如果不为空,那说明存在多余的左括号;如果栈为空,此时才能说明这些括号是匹配的。最后要把我们动态申请的栈要销毁掉,当然不止在最后要销毁,因为我们可能在没有遍历完这个字符串s的时候函数就会提前返回,所以在每次可能返回之前,都要先销毁栈,才能防止内存泄漏的发生。

        具体代码如下:

bool isValid(char* s) {
    ST st;
    STInit(&st);
    while(*s)
    {
        if((*s == '(')
        || (*s == '{')
        ||(*s == '['))
        {
            STPush(&st,*s);
        }
        else
        {
            if(STEmpty(&st))
            {
                STDestory(&st);
                return false;
            }

            char top =STTop(&st);
            STPop(&st);
            if((top == '(' && *s!=')')
            ||(top == '[' && *s!=']')
            ||(top == '{' && *s!='}'))
            {
                STDestory(&st);
                return false;
            }
        }
        s++;
    }
    bool ret=STEmpty(&st);
    STDestory(&st);
    return ret;
}

  • 35
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
是一种常用的数据结构,可以用来实现括号匹配。下面是一个使用C语言代码实现来进行括号匹配的示例: ```c #include <stdio.h> #include <stdbool.h> #define MAX_SIZE 100 // 定义结构 typedef struct { char data[MAX_SIZE]; int top; } Stack; // 初始化 void initStack(Stack *s) { s->top = -1; } // 判断是否为空 bool isEmpty(Stack *s) { return s->top == -1; } // 判断是否已满 bool isFull(Stack *s) { return s->top == MAX_SIZE - 1; } // 入 void push(Stack *s, char c) { if (isFull(s)) { printf("Stack is full.\n"); return; } s->data[++(s->top)] = c; } // 出 char pop(Stack *s) { if (isEmpty(s)) { printf("Stack is empty.\n"); return '\0'; } return s->data[(s->top)--]; } // 括号匹配函数 bool isParenthesesMatch(char *str) { Stack s; initStack(&s); for (int i = 0; str[i] != '\0'; i++) { if (str[i] == '(' || str[i] == '[' || str[i] == '{') { push(&s, str[i]); } else if (str[i] == ')' || str[i] == ']' || str[i] == '}') { if (isEmpty(&s)) { return false; } char top = pop(&s); if ((str[i] == ')' && top != '(') || (str[i] == ']' && top != '[') || (str[i] == '}' && top != '{')) { return false; } } } return isEmpty(&s); } int main() { char str[MAX_SIZE]; printf("请输入括号序列:"); scanf("%s", str); if (isParenthesesMatch(str)) { printf("括号匹配成功!\n"); } else { printf("括号匹配失败!\n"); } return 0; } ``` 这段代码中,我们使用一个字符数组来存储输入的括号序列,然后通过遍历序列中的每个字符,将左括号入,遇到右括号时与顶元素进行匹配。如果匹配成功,则继续遍历;如果匹配失败或者为空,则括号匹配失败。最后判断是否为空,如果为空则括号匹配成功,否则匹配失败。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值