特点:只能在表尾(栈顶)a1插入和删除,后进先出。表头an是栈底
存储方式:顺序栈,链栈
一、顺序栈
定义:地址连续,依次存储,数组实现
用栈顶指针保存栈顶位置,有两种方法:①指向栈顶元素的实际位置(对应的数组下标,从-1开始)②指向下一次入栈的位置,从0开始。
基本操作:
typedef struct {
int data[MAXSIZE];
int top;//栈顶指针
}SeqStack;
SeqStack s;
//栈顶指针指向实际栈顶的位置
//初始化
s.top = -1;
//入栈
if (s.top < MAXSIZE - 1)
s.data[++s.top] = x;
else printf("overflow");
//出栈
if (s.top >= 0)
s.top--;
//判栈空
s.top == -1;
//判栈满
s.top == MAXSIZE - 1;
//栈顶元素
s.data[s.top];
二、链栈
不带表头的单链表,头指针对应栈顶位置
结构体维护栈:
// 栈顶指针为top,无头结点
typedef struct Snode {
int data;
struct Snode* next;
} *LinkStack;
初始化栈
void InitStack(LinkStack& top) {
top = NULL;
}
出栈
void pop(LinkStack& top) {
if (top != NULL) // 栈非空
{
LinkStack p = top;
top = top->next;
free(p);
}
}
入栈
void push(LinkStack& top, int e) {
LinkStack s = (LinkStack)malloc(sizeof(struct Snode));
s->data = e;
if (StackEmpty(top) == 1) top = s;
else {
s->next = top;
top = s; // 更新栈顶指针
}
}
获取栈顶元素
int GetTop(LinkStack top) {
if (top == NULL) {
return -1;
}
else {
int e = top->data;
return e;
}
}
判断栈是否为空
bool StackEmpty(LinkStack top) {
return (top == NULL); // 若栈为空,返回1
}
主函数
int main() {
LinkStack top;
InitStack(top);
int n, x;
printf("请输入创建栈的元素个数:");
scanf_s("%d", &n);
printf("请输入入栈元素:");
int i;
for (i = 0; i < n; i++) {
int y;
scanf_s("%d", &y);
push(top, y);
}
printf("\n");
printf("请输入入栈元素:");
scanf_s("%d", &x);
push(top, x);
while (StackEmpty(top) != 1) {
printf("获取新的栈顶元素:");
int e = GetTop(top);
printf("%d", e);
printf("-----栈顶元素出栈-----\n");
pop(top);
}
printf("empty!");
return 0;
}
三、双栈共享存储空间
两个栈共用一个一维数组v[M],栈底分别设在数组的两端,两个栈可互补空缺,使得某个栈实际可用空间大于M/2
四、栈的应用
1.括号匹配
这是一道栈的经典题目,也可以见LeetCode20。
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 注意空字符串可被认为是有效字符串。
我们先来分析一下不匹配的场景,有三个。
- 多了左括号
- 多了右括号
- 括号数量匹配,类型不匹配
算法:遇到左括号,就把对应的右括号放进栈里。遇到右括号,与栈顶元素进行比较,如果相同,就匹配成功,弹出栈顶元素。
- 若字符串遍历完了,但栈不为空,则多了左括号。这是情况一。
- 若比较时发现不匹配的括号,是第三种情况。
- 若栈空了,而字符串没有遍历玩完,则多了右括号,是第二种情况。
如果一开始遇到左括号,把左括号放进栈里,那么之后遇到右括号的时候,还要再判断一次栈中的左括号和字符串里的右括号是否匹配,太麻烦了,所以我们采取第一种方法。
2.逆波兰式
首先,我们来认识一下什么是逆波兰式。
逆波兰式是一种后缀表达式,操作符位于操作数的后面,不需要括号来改变运算的优先级。可以想象成二叉树的后序遍历(左右中)。正常的表达式是二叉树的中序遍历,符合书写习惯,但计算机无法加括号,而利用栈和后缀表达式可以免去加括号,直接计算。
那么如何用栈来实现呢?
首先,遍历表达式,遇到数字则入栈,遇到运算符则弹出两个数字,进行运算后,将结果入栈。如此反复。栈里的最后一个数字就是最终结果。
代码实现:
#include<stdlib.h>
#include<string.h>
#include <stdio.h>
// 栈顶指针为top,无头结点
typedef struct Snode {
char data;
struct Snode* next;
} *LinkStack;
void InitStack(LinkStack& top) {
top = NULL;
}
void pop(LinkStack& top) {
if (top != NULL) // 栈非空
{
LinkStack p = top;
top = top->next;
free(p);
}
}
bool StackEmpty(LinkStack top) {
return (top == NULL); // 若栈为空,返回1
}
void push(LinkStack& top, char e) {
LinkStack s = (LinkStack)malloc(sizeof(struct Snode));
s->data = e;
if (StackEmpty(top) == 1) top = s;
else {
s->next = top;
top = s;
}
}
int evalRPN(char* tokens, int tokensSize) {
LinkStack top = NULL;
for (int i = 0; i < tokensSize; i++) {
if (tokens[i] == '+' || tokens[i] == '-' || tokens[i] == '*' || tokens[i] == '/') {
int num1 = top->data - '0';
pop(top);
int num2 = top->data - '0';
pop(top);
int add;
if (tokens[i] == '+') { add = num2 + num1; push(top, add + '0'); }
else if (tokens[i] == '-') { add = num2 - num1; push(top, add + '0'); }
else if (tokens[i] == '*') { add = num2 * num1; push(top, add + '0'); }
else if (tokens[i] == '/') { add = num2 / num1; push(top, add + '0'); }
//比较不能用scrmp我也不知道 为啥
} else {
push(top, tokens[i]);
}
}
int result = top->data - '0';//char转换成int类型
free(top);
return result;
}
int main() {
int tokensSize;
char tokens[100];
printf("请输入数组元素数量:");
scanf("%d", &tokensSize);
printf("请输入后缀表达式:");
for (int i = 0; i < tokensSize; i++) {
scanf(" %c", &tokens[i]);
}
int result = evalRPN(tokens, tokensSize);
printf("计算结果:%d\n", result);
return 0;
}
3.十进制转八进制数
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
typedef struct Snode {
int data;
struct Snode* next;
} *LinkStack;
void InitStack(LinkStack& top) {
top = NULL;
}
int pop(LinkStack& top) {
int e;
if (top != NULL) {
LinkStack p = top;
e = p->data;
top = top->next;
free(p);
}
return e;
}
bool StackEmpty(LinkStack top) {
return (top == NULL);
}
void push(LinkStack& top, int e) {
LinkStack s = (LinkStack)malloc(sizeof(struct Snode));
s->data = e;
if (StackEmpty(top)) {
s->next = NULL;
top = s;
} else {
s->next = top;
top = s;
}
}
void conversion(LinkStack& top, int N) {
int octalNum[100];
int i = 0;
while (N) {
octalNum[i++] = N % 8;
N = N / 8;
}
for (int j = i - 1; j >= 0; j--) {
printf("%d", octalNum[j]);
}
}
int main() {
int N;
LinkStack top;
InitStack(top);
printf("请输入十进制数:");
scanf("%d", &N);
printf("转换后的八进制数为:");
conversion(top, N);
return 0;
}
另一种写法
#include <stdio.h>
#include<math.h>
typedef struct Snode {
int data;
struct Snode* next;
} *LinkStack;
void InitStack(LinkStack& top) {
top = NULL;
}
bool StackEmpty(LinkStack top) {
return (top == NULL);
}
void push(LinkStack& top, int e) {
LinkStack s = new Snode;
s->data = e;
if (StackEmpty(top)) {
s->next = NULL;
top = s;
} else {
s->next = top;
top = s;
}
}
int pop(LinkStack& top) {
int e;
if (top != NULL) {
LinkStack p = top;
e = p->data;
top = top->next;
delete p;
}
return e;
}
int DecimalToOctal(int dec) {
LinkStack top;
InitStack(top);
int octalNum = 0, i = 0;
while (dec != 0) {
push(top, dec % 8);
dec /= 8;
i++;
}
while (!StackEmpty(top)&&i>=1) {
octalNum += pop(top) * pow(10 ,i-1);
i--;
}
return octalNum;
}
int main() {
int N;
printf("请输入十进制数:");
scanf("%d", &N);
printf("转换后的八进制数为:%d\n", DecimalToOctal(N));
return 0;
}