数据结构:栈和队列详解(上)
一.栈
1.概念与结构:
栈:一种特殊的线性表,只允许在顺序表的一段插入和删除数据,进行插入和删除的一端叫做栈顶,另外一端则叫做栈底,而我们将在栈顶插入数据叫做压栈(入栈或进栈),在栈底删除数据则叫做出栈
栈的实现可以通过链表和数组实现,但数组在尾插时相对于需要遍历链表才能尾插的链表结构而言拥有更小的时间复杂度,所以栈的实现一般使用数组实现
2.栈的实现:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//定义栈的结构
typedef int STDataType;
typedef struct Stack
{
STDataType* arr;
int top; //指向栈顶的位置
int capacity;//栈的容量
}ST;
//栈的初始化
void STInit(ST* ps);
//栈的销毁
void STDestory(ST* ps);
//入栈
void StackPush(ST* ps, STDataType x);
//判断栈是否为空(配合确定top--至0(栈底)的限制条件)
bool StackEmpty(ST* ps);
//出栈
void StackPop(ST* ps);
//取栈顶数据
STDataType StackTop(ST* ps);
//获取栈中有效元素个数
STDataType STsize(ST* ps);
//栈的初始化
void STInit(ST* ps)
{
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
//栈的销毁
void STDestory(ST*ps)
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
//入栈
void StackPush(ST* ps, STDataType x)
{
assert(ps);
//空间不够
if (ps->capacity == ps->top)
{
//扩容
STDataType newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->arr, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc failed");
exit(1);
}
ps->arr = tmp;
ps->capacity = newcapacity;
}
//空间足够
ps->arr[ps->top++] = x;
ps->capacity++;
}
//判断栈是否为空
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
//出栈
void StackPop(ST* ps)
{
assert(!StackEmpty(ps));
--ps->top;
--ps->capacity;
}
//获取栈顶元素
STDataType StackTop(ST* ps)
{
assert(!StackEmpty(ps));
return ps->arr[ps->top-1];
}
//获取栈中元素个数
STDataType STsize(ST* ps)
{
assert(ps);
return ps->top;
}
以上是栈的实现中所有涉及的函数及函数实现
3.与栈有关的算法题(有效的括号):
原题来源:https://leetcode.cn/problems/valid-parentheses/description/
这题的主要思路就是通过创建一个栈,然后在对输入的一串字符串进行遍历入栈,首先是使其中的一个栈的入栈条件为”是左括号“,如果遍历出的下一个是与之对应的右括号,则使先前入栈的左括号出栈,以此类推,直至字符串遍历完成,如果此时栈为空,则说明字符串符符合题意并且返回true,若不为空,则返回false,整体来看,代码总体上会以字符串的遍历以及栈的输入输出为整体架构,并且伴随着对特殊情况1:”空栈“和特殊情况2:”字符串遍历完后栈中是否还有元素残留“的讨论,前者应当置于字符串遍历过程中字符元素与栈元素的对比之前,因为对比就意味着要从栈里读取元素,而空栈是无法读取的,后者则应当置于尾部,在对比机制完整之后以防止”【】{“(类似这种情况)的产生,同时还有一点值得注意的就是每次排除一种错误情况时都不要忘记销毁栈(因为栈的插入涉及到对空间的申请,而不及时释放空间就很容易产生内存泄漏问题)
//栈的初始化
void STInit(ST* ps)
{
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
//栈的销毁
void STDestory(ST* ps)
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
//入栈
void StackPush(ST* ps, STDataType x)
{
assert(ps);
//空间不够
if (ps->capacity == ps->top)
{
//扩容
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->arr, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc failed");
exit(1);
}
ps->arr = tmp;
ps->capacity = newcapacity;
}
//空间足够
ps->arr[ps->top++] = x;
ps->capacity++;
}
//判断栈是否为空
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
//出栈
void StackPop(ST* ps)
{
assert(!StackEmpty(ps));
--ps->top;
--ps->capacity;
}
//获取栈顶元素
char StackTop(ST* ps)
{
assert(!StackEmpty(ps));
return ps->arr[ps->top - 1];
}
//获取栈中元素个数
int STsize(ST* ps)
{
assert(ps);
return ps->top;
}
bool isValid(char* s) {
ST st;
STInit(&st);
char* pi = s;
//遍历字符串
while (*pi != '\0')
{
//左括号入栈
if (*pi == '{' || *pi == '[' || *pi == '(')
{
StackPush(&st, *pi);
}
else
{//右括号取栈顶并匹配
//先判断是否为空
if (StackEmpty(&st))
{
STDestory(&st);
return false;
}
char tmp = StackTop(&st);
if (tmp == '{' && *pi! = '}' || tmp == '(' && *pi! = ')' || tmp == '[' && *pi! = ']')
{
STDestory(&st);
return false;
}
StackPop(&st);
}
pi++;
}
//如果栈为空,说明所有的括号都匹配完了,反之为非有效字符串
bool ret = StackEmpty(&st) ? true : false;
STDestory(&st);
return ret;
}