栈(附完整代码实现)
性质
- 栈是一种特殊的线性结构,与线性表不同,栈只能在一端进行操作,另外一端锁死,是操作受限的线性表
- 只能在栈顶进行数据的插入和删除
- 遵从FILO原则,先进后出,最先进栈的元素在最底部,最后的在栈顶(空箱子放物品)
- 常用操作
- 初始化栈
- 压栈(入栈),添加一个元素
- 弹栈(出栈),删除一个元素
- 获取栈顶元素
- 判断栈是否为空,是否满栈
- &在操作的时候,避免上溢和下溢&
上溢:指栈已满,若继续存数据,则会上溢,出现报错(栈满再存出现上溢)
下溢:指栈已空,若继续取数据,则会下溢,出现报错(栈空再取出现下溢)
以下以线性表为基础,图解说明各个操作
&注意:栈满,无法进行入栈,栈空,无法进行出栈以及获取栈顶元素(这是显而易见的)
代码:
- 定义栈的结构
//定义栈的结点结构
typedef struct StackNode {
int value;
struct StackNode* next;
}StackNode;
//定义栈的结构
typedef struct Stack {
//定义此时栈顶元素位置(指针对应地址),每次入栈pos自增,再将相应元素入栈
StackNode* top;
int size;
}Stack;
- 栈的初始化
/
/初始化栈
Stack* initStack() {
Stack* stack = (Stack*)malloc(sizeof(Stack));
stack->top = NULL;
stack->size = 0;
return stack;
}
//使用malloc函数分配大小为Stack的结构体大小的内存空间,将其转换为Stack类型的指针,将之赋值给变量stack,将首地址给stack->top,表示栈顶为空,最后将大小初始化为0,表示栈目前为空栈
创建一个空的栈结构,并且返回该栈的指针
- 判断此时栈是否为空
//判断栈是否为空
int isEmpty(Stack* stack) {
return stack->top = NULL;
}
(非0为真,0为假)
- 入栈
/
//判断栈是否为空
int isEmpty(Stack* stack) {
return stack->top = NULL;
}
//出栈
int pop(Stack* stack) {
if (stack->top != NULL) {
StackNode* node = stack->top;
int value = node->value;
stack->top = node->next;
free(node);
stack->size--;
return value;
}
return -1;
}
入栈过程,图解:
- 出栈
考虑栈的大小,空栈不能出栈
//判断栈是否为空
int isEmpty(Stack* stack) {
return stack->top = NULL;
}
//出栈
int pop(Stack* stack) {
if (stack->top != NULL) {
StackNode* node = stack->top;
int value = node->value;
stack->top = node->next;
free(node);
stack->size--;
return value;
}
return -1;
}
出栈,图解:
- 获取栈顶元素
//获取栈顶元素
int topStack(Stack* stack) {
//判断是否为空
if (stack->top != NULL) {
return stack->top->value;
}
//表示无法取栈顶元素
return -1;
}
完整代码示例:(附代码运行效果)
#include<stdio.h>
#include<stdlib.h>
//栈的最大容量
#define STACK_SZ 100
//定义栈的结点结构
typedef struct StackNode {
int value;
struct StackNode* next;
}StackNode;
//定义栈的结构
typedef struct Stack {
StackNode* top;
int size;
}Stack;
//初始化栈
Stack* initStack() {
Stack* stack = (Stack*)malloc(sizeof(Stack));
stack->top = NULL;
stack->size = 0;
return stack;
}
//销毁栈
void destroy(Stack* stack) {
while (stack->top != NULL) {
StackNode* node = stack->top;
stack->top = node->next;
free(node);
}
free(stack);
}
//入栈
void push(Stack* stack, int value) {
StackNode *node = (StackNode*)malloc(sizeof(StackNode));
node->value = value;
node->next = stack->top;
stack->top = node;
stack->size++;
}
//出栈
int pop(Stack* stack) {
if (stack->top != NULL) {
StackNode* node = stack->top;
int value = node->value;
stack->top = node->next;
free(node);
stack->size--;
return value;
}
return -1;
}
//获取栈顶元素
int topStack(Stack* stack) {
if (stack->top != NULL) {
return stack->top->value;
}
return -1;
}
//判断栈是否为空
int isEmpty(Stack* stack) {
return stack->top = NULL;
}
//判断栈是否满栈
int isFull(Stack* stack) {
return stack->size = STACK_SZ;
}
//打印栈内容
void Print(Stack* stack) {
StackNode* node = stack->top;
while (node != NULL) {
printf("%d", node->value);
node = node->next;
}
printf("\n");
}
int main()
{
Stack* stack = initStack();
push(stack, 1);
push(stack, 2);
push(stack, 3);
printf("栈顶元素:%d\n", topStack(stack));
printf("出栈元素:%d\n", pop(stack));
printf("栈顶元素:%d\n", topStack(stack));
printf("是否为空:%d\n", isEmpty(stack));
printf("是否满栈:%d\n", isFull(stack));
Print(stack);
destroy(stack);
return 0;
}
代码运行效果:
代码注意:在 `main` 函数中,首先创建了一个栈,并向其中依次压入了 1、2、3 三个整数。然后,打印了栈顶元素、出栈元素、栈顶元素,并判断了栈是否为空和满。最后,打印了栈的内容,并销毁了栈。
需要注意的是,这个栈的实现是基于动态内存分配的,因此在使用完毕后需要调用 destroy 函数释放内存。
- 习题练习
题源:leetcode题库20题 有效的括号
思路:我们遍历给定的字符串 sss。当我们遇到一个左括号时,我们会期望在后续的遍历中,有一个相同类型的右括号将其闭合。由于后遇到的左括号要先闭合,因此我们可以将这个左括号放入栈顶。
当我们遇到一个右括号时,我们需要将一个相同类型的左括号闭合。此时,我们可以取出栈顶的左括号并判断它们是否是相同类型的括号。如果不是相同的类型,或者栈中并没有左括号,那么字符串 无效,返回 False
解决方法:
char pairs(char a) {
if (a == '}') return '{';
if (a == ']') return '[';
if (a == ')') return '(';
return 0;
}
bool isValid(char* s) {
int len = strlen(s);
//检查字符串长度,奇数直接return false
if (len % 2 == 1) {
return false;
}
int stk[len + 1], top = 0;
//遍历字符串,然后压栈(左括号)
for (int i = 0; i < len; i++) {
char ch = pairs(s[i]);
if (ch) {
if (top == 0 || stk[top - 1] != ch) {
return false;
}
top--;
} else {
stk[top++] = s[i];
}
}
return top == 0;
}
测试用例:
时间复杂度:O(n),n是字符串s的长度
空间复杂度:O(n+6),6种括号,字符数量为O(n)