꧁ 各位大佬们好!很荣幸能够得到您的访问,让我们一起在编程道路上任重道远!꧂
☙ 博客专栏:【数据结构初阶】❧
⛅ 本篇内容简介:数据结构初阶中的栈的实现以及练习题【括号匹配问题】!
⭐ 了解作者:励志成为一名编程大牛的学子,目前正是大二的编程小白。
✍励志术语:编程道路的乏味,让我们一起学习变得有趣!
文章目录
✯ 顺序表与链表的总结
✡ 顺序表和链表的区别
不同点 | 顺序表 | 链表(双向循环带头) |
存储空间上 | 物理上一定连续 | 逻辑上连续,但物理上不一定连续 |
随机访问 | 支持O(1) 下标 | 不支持:为O(N) 遍历 |
任意位置插入或者删除元素 | 可能需要挪动元素,效率低,为O(N) | 只需要修改指针指向 |
插入 | 动态顺序表,空间不够时需要扩容 | 没有扩容的概念 |
应用场景 | 元素高效存储+频繁访问 | 任意位置插入与删除频繁 |
缓存利用率 | 高 | 低 |
✡ 顺序表和链表的优缺点
⛦ 顺序表的优缺点
优点:
· 尾插尾删效率很高
· 随机访问(用下标访问)
· CPU高速缓冲命中率更高(相比于链表)
缺点:
· 头部和中部的插入与删除效率很低(为O(N))
· 扩容时 —— 性能消耗+空间浪费
⛦ 链表的优缺点
优点:
· 任意位置插入与删除效率很高(为O(1))
· 按需申请与释放空间
缺点:
· 不支持随机访问
CPU执行指令,不会直接访问内存
1. 先看数据在不在三级缓存,如果在(命中),直接访问
2. 如果不在(不命中),先加载到缓存,再访问
✯ 栈
✡ 栈的概念及结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入与删除元素操作,进行数据的插入和删除操作的一端称为栈顶,另外一端为栈底。栈中的数据元素遵守后进先出(Last In First out)的原则。
压栈:栈的插入操作叫做 进栈 / 压栈 / 入栈,入数据是在栈顶
出栈:栈的删除操作叫做出栈,出数据也在栈顶。
✡ 栈的实现
栈的实现一般可以使用数组或者链表实现,但是相对而言数组实现结构更加优一些,因为数组在尾上插入数据的代价比较小。
⛦ 栈的结构
看到上面栈的物理结构,可以看到栈是一个数组,有top值和容量值(capacity)。
栈的结构分为静态版本以及动态增长版本,但是静态版本的栈一般不实用,这里我们不做过多的介绍了,直接介绍动态增长版本的栈。
代码实现:
//动态栈
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;//栈顶
int capacity;//容量
}Stack;
⛦ 栈的初始化
//初始化栈
void StackInit(Stack* ps);
栈的初始化有两种形式:
①. top初始的值为 0
②. top初始的值为 -1
这里我们介绍top初始化为0的值,因为top值的位置就栈中元素个数。
代码实现:
//初始化栈
void StackInit(Stack* ps)
{
assert(ps);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
⛦ 栈的入栈(压栈)
//入栈
void StackPush(Stack* ps, STDataType x);
在入栈的过程中,需要注意的点有:
①. 需要考虑扩容问题,如果需要扩容将数组成2倍增长。
②. 需要在数组的top位置插入元素,最后top++。
代码实现:
//入栈
void StackPush(Stack* ps, STDataType x)
{
//考虑扩容
assert(ps);
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
//入栈
ps->a[ps->top] = x;
ps->top++;
}
⛦ 栈的出栈
//出栈
void StackPop(Stack* ps);
出栈需要注意的点:
①. 需要判断栈(Stack)是否为空。
②. 出栈就比较简单了,直接top - - 就行了。
代码实现:
//出栈
void StackPop(Stack* ps)
{
assert(ps);
//判空
assert(!StackEmpty(ps));
//直接 --
ps->top--;
}
⛦ 获取栈顶的元素
//获取栈顶元素
STDataType StackTop(Stack* ps);
top位置的数据是元素的个数,在数组中数组末的数据就是元素个数 -1 的位置,即:top -1。
代码实现:
//获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
⛦ 获取栈的有效元素个数
//栈的有效个数
int StackSize(Stack* ps);
元素个数就是top的值,在之前我们就已经解释过了!!!
//栈的有效个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
⛦ 栈的判空
//栈的判空 为空返回非0,不为空返回0
bool StackEmpty(Stack* ps);
在初始化时我们将top初始化为0,如果top一直是0,就说明栈是空的,返回非0,反之。
代码实现:
//栈的判空 为空返回非0,不为空返回0
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
⛦ 栈的销毁
//栈的销毁
void StackDestroy(Stack* ps);
//栈的销毁
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->a);
//置空
ps->a = NULL;
ps->top = ps->capacity = 0;
}
⛦ 测试栈的接口功能
我们先将几个数据入栈,但栈不为空时,就获取栈顶的元素,并且要将栈顶的元素Pop,也就是出栈。
✡ 栈的源代码
⛦ Stack.h文件
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
//typedef int STDataType;
//#define N 100
静态栈
//typedef struct Stack
//{
// STDataType a[N];
// int top;//栈顶
//};
//动态栈
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;//栈顶
int capacity;//容量
}Stack;
//初始化栈
void StackInit(Stack* ps);
//入栈
void StackPush(Stack* ps, STDataType x);
//出栈
void StackPop(Stack* ps);
//获取栈顶元素
STDataType StackTop(Stack* ps);
//栈的有效个数
int StackSize(Stack* ps);
//栈的判空 为空返回非0,不为空返回0
bool StackEmpty(Stack* ps);
//栈的销毁
void StackDestroy(Stack* ps);
⛦ Stack.c文件
#include"Stack.h"
//初始化栈
void StackInit(Stack* ps)
{
assert(ps);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
//入栈
void StackPush(Stack* ps, STDataType x)
{
//考虑扩容
assert(ps);
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
//入栈
ps->a[ps->top] = x;
ps->top++;
}
//出栈
void StackPop(Stack* ps)
{
assert(ps);
//判空
assert(!StackEmpty(ps));
//直接 --
ps->top--;
}
//获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
//栈的有效个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
//栈的判空 为空返回非0,不为空返回0
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
//栈的销毁
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->a);
//置空
ps->a = NULL;
ps->top = ps->capacity = 0;
}
⛦ test.c文件
#include"Stack.h"
void test()
{
Stack st;
//初始化
StackInit(&st);
//入栈
StackPush(&st, 1);
StackPush(&st, 2);
StackPush(&st, 3);
StackPush(&st, 4);
StackPush(&st, 5);
while (!StackEmpty(&st))
{
//获取栈顶元素
printf("%d ",StackTop(&st));
//出栈
StackPop(&st);
}
//销毁
StackDestroy(&st);
}
int main()
{
test();
return 0;
}
✯【LeetCode】— 括号匹配问题
✡ 题目描述
20. 有效的括号. OJ链接
✡ 解题思路
①. 我们在这里使用的是C语言实现这个题目,因为C语言没有库,所以我们需要自己写一个栈,但是我们之前已经写好了一个栈了,可以直接使用。
②. 如果字符是左括号 '(' || ' [ ' || ' { ' ,我们就入栈,如果是右括号我们就取栈顶的元素,与字符串进行对比,如果左括号与相应的右括号不匹配时,就 return false。
考虑边界情况:
③. 当栈中只有左括号时,就需要对栈进行判空处理,如果栈不为空,就 返回它的 bool 值。
④. 当没有左括号时,就说明没有括号入栈,也对栈进行判空,如果栈为空,就 return false。
注意:养成好习惯,在每次 return 时,对栈进行销毁。
✡ 解题代码
typedef char STDataType;
typedef struct Stack
{
STDataType* a;
int top;//栈顶
int capacity;//容量
}Stack;
//初始化栈
void StackInit(Stack* ps);
//入栈
void StackPush(Stack* ps, STDataType x);
//出栈
void StackPop(Stack* ps);
//获取栈顶元素
STDataType StackTop(Stack* ps);
//栈的有效个数
int StackSize(Stack* ps);
//栈的判空 为空返回非0,不为空返回0
bool StackEmpty(Stack* ps);
//栈的销毁
void StackDestroy(Stack* ps);
//初始化栈
void StackInit(Stack* ps)
{
assert(ps);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
//入栈
void StackPush(Stack* ps, STDataType x)
{
//考虑扩容
assert(ps);
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
//入栈
ps->a[ps->top] = x;
ps->top++;
}
//出栈
void StackPop(Stack* ps)
{
assert(ps);
//判空
assert(!StackEmpty(ps));
//直接 --
ps->top--;
}
//获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
//栈的有效个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
//栈的判空 为空返回非0,不为空返回0
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
//栈的销毁
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->a);
//置空
ps->a = NULL;
ps->top = ps->capacity = 0;
}
bool isValid(char * s){
//使用栈实现
Stack st;
//初始化
StackInit(&st);
while(*s!='\0')
{
//左括号就入栈
if(*s=='('||*s=='['||*s=='{')
{
StackPush(&st,*s);
}
else
{
//右括号 出栈 对比
// 栈为空时
if(StackEmpty(&st))
{
StackDestroy(&st);
return false;
}
char top=StackTop(&st);
StackPop(&st);
if((*s==')'&&top!='(')||
(*s==']'&&top!='[')||
(*s=='}'&&top!='{'))
{
StackDestroy(&st);
return false;
}
}
s++;
}
//栈不为空,为false
bool flag=StackEmpty(&st);
StackDestroy(&st);
return flag;
}
✯ 结束语
【写在最后·想告诉你】
在互联网这个行业里
任何时候都要学好技术
永远都是 技术为王