4. 栈
4.1 栈的概念及结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
4.2 栈的实现
1 正式的栈
基本信息
用于动态申请内存的指针。
栈顶。一般由整型变量担任。
容量。一般由整型变量担任。
功能
初始化栈(Init)
指针置空。
栈顶指向栈底。这里有两种处理方式:
假设
int top;
为表示栈顶的变量,int *a;
为动态数组,int t;
为即将入栈的元素。
- 若栈顶元素初始化为
top = 0
,则尽量使用a[top++] = t;
表示入栈。此时栈顶元素为a[top-1]
。- 若栈顶元素初始化为
top = -1
,则尽量使用a[++top] = t;
表示入栈。此时栈顶元素为a[top]
。两种处理方式不能同时用,最好坚持一种风格。
栈容量置空。
销毁栈(destroy)
释放alloc系列函数(malloc,calloc,realloc以及c++关键字new)在堆区申请的空间并将指针置空。
栈顶元素和容量恢复初始值。
入栈(push)
假设
int top;
为表示栈顶的变量,int *a;
为动态数组,int t;
为即将入栈的元素。
- 若栈顶元素初始化为
top = 0
,则尽量使用a[top++] = t;
表示入栈。此时栈顶元素为a[top-1]
。- 若栈顶元素初始化为
top = -1
,则尽量使用a[++top] = t;
表示入栈。此时栈顶元素为a[top]
。这里以第1种风格为例。
首先判断栈顶元素和容量是否相等(即判断栈是否已满)。若满了则要进行有限的扩容。
后按照风格1入栈。
出栈(pop)
栈顶元素自减1即可。
返回栈顶元素(top)
假设
int top;
为表示栈顶的变量,int *a;
为动态数组,int t;
为即将入栈的元素。
- 若栈顶元素初始化为
top = 0
,则尽量使用a[top++] = t;
表示入栈。此时栈顶元素为a[top-1]
。- 若栈顶元素初始化为
top = -1
,则尽量使用a[++top] = t;
表示入栈。此时栈顶元素为a[top]
。若使用风格1,则返回
a[top-1]
。若使用风格2,则返回
a[top]
。
判断栈是否为空(empty)
假设
int top;
为表示栈顶的变量,int *a;
为动态数组,int t;
为即将入栈的元素。
- 若栈顶元素初始化为
top = 0
,则尽量使用a[top++] = t;
表示入栈。此时栈顶元素为a[top-1]
。- 若栈顶元素初始化为
top = -1
,则尽量使用a[++top] = t;
表示入栈。此时栈顶元素为a[top]
。若使用风格1,则当
top == 0
时栈为空。若使用风格2,则当
top == -1
时栈为空。
返回栈中元素(size)
假设
int top;
为表示栈顶的变量,int *a;
为动态数组,int t;
为即将入栈的元素。
- 若栈顶元素初始化为
top = 0
,则尽量使用a[top++] = t;
表示入栈。则栈中元素数为top-1
。- 若栈顶元素初始化为
top = -1
,则尽量使用a[++top] = t;
表示入栈。则栈中元素数为top
。
2 栈的参考程序
Stack.h
:
#pragma once
#include<stdlib.h>
#include<assert.h>
#include<stdbool.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);//返回栈的元素个数
Stack.c
#include "Stack.h"
void STInit(ST* pst) {//初始化栈
assert(pst);//真实存在的栈才能被初始化
pst->a = NULL;
//pst->top = -1; // top 指向栈顶数据
pst->top = 0; // top 指向栈顶数据的下一个位置
pst->capacity = 0;//栈中元素数初始化为0
}
void STDestroy(ST* pst) {//销毁栈
assert(pst);
free(pst->a);//销毁alloc开辟在堆区的内存
pst->a = NULL;
pst->top = pst->capacity = 0;
}
void STPush(ST* pst, STDataType x) {//入栈
if (pst->top == pst->capacity) {
int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;//线性表式扩容,因为栈也是特殊的线性表
STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));
if (tmp == NULL) {
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newCapacity;
}
pst->a[pst->top] = x;
pst->top++;//因为栈顶要指向下一个元素
}
void STPop(ST* pst) {//出栈
assert(pst);
assert(!STEmpty(pst));//栈不应该为空
pst->top--;//top指向栈顶元素的下一个位置,则退一格即可
}
STDataType STTop(ST* pst) {//返回栈顶元素
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top - 1];
//之前top指向栈顶元素的下一个位置
}
bool STEmpty(ST* pst) {//判断栈是否为空
assert(pst);
/*if (pst->top == 0) {
return true;
}
else {
return false;
}*/
return pst->top == 0;
}
int STSize(ST* pst) {//返回栈中元素数量
assert(pst);
return pst->top;
}
3 简化的栈
根据上方栈的描述,我们完全可以省略大部分功能,弄一个临时的栈:
int a[1000]={0};//静态数组表示栈,但容量固定。可根据实际情况开辟。类型可更换。
int top=0;//栈顶元素,这里采用风格1,则栈顶元素即为a[top-1]。
//之后写几个函数作为栈的接口即可
void push(int x){//风格1入栈
a[top]=x;
top++;
}
void pop(){//出栈
top--;
}
int size(){//返回栈的元素数
return top;
}
//其他功能可自行补充
4.3 栈的应用
这里简单举个例子。
原题地址:http://ybt.ssoier.cn:8088/problem_show.php?pid=1354
1354:括弧匹配检验
时间限制: 1000 ms 内存限制: 65536 KB
【题目描述】
假设表达式中允许包含两种括号:圆括号和方括号,其嵌套的顺序随意,如([]())
或[([ ][ ])]
等为正确的匹配,[( ])
或([ ]( )
或 ( ( ) ) )
均为错误的匹配。
现在的问题是,要求检验一个给定表达式中的括弧是否正确匹配?
输入一个只包含圆括号和方括号的字符串,判断字符串中的括号是否匹配,匹配就输出 “OK
” ,不匹配就输出“Wrong
”。输入一个字符串:[([][])]
,输出:OK
。
【输入】
输入仅一行字符(字符个数小于255)。
【输出】
匹配就输出 “OK
” ,不匹配就输出“Wrong
”。
【输入样例】
[(])
【输出样例】
Wrong
分析:遍历题解给的字符串,是左括号则入栈,是右括号则判断栈顶元素是否与该括号匹配,若匹配则出栈,不匹配则整个字符串不匹配。
若遍历完所有的字符串,栈中还有剩余的左括号,则整个字符串不匹配。
参考程序:
#include<stdio.h>
#include<string.h>
int main () {
char str[256];
scanf("%[^\n]",str);
int len=strlen(str);
//一个数组和一个整型变量就能模拟一个栈的基本功能
char str2[256]={0}; //栈本体
int po=0; //栈顶
for(int i=0;i<len;i++) {
if((str[i]=='[')||(str[i]=='(')) {//左括号入栈
str2[po]=str[i]; //风格1入栈
po++;
continue;
}
else if(str[i]==')') {//右括号则观察是否和栈顶元素匹配
if(str2[po-1]=='(') {
str2[po-1]=0;
po--; //风格1出栈
continue;
}
printf("Wrong");
return 0;
}
else if(str[i]==']') {
if(str2[po-1]=='[') {
str2[po-1]=0;
po--;
continue;
}
printf("Wrong");
return 0;
}
}
if(po==0)//判断栈是否为空
printf("OK");
else
printf("Wrong");
return 0;
}