栈是一种 操作受限 的特殊的线性表,属于逻辑结构。
操作受限是指只允许在一端(栈顶)进行插入或删除的操作。
栈的特点有:
Catalan 数 即 卡特兰数,公式 1 n + 1 C 2 n n \frac{1}{n+1}C^{n}_{2n} n+11C2nn ,这个在选择题中经常出现。
栈通常有两种实现的存储结构:顺序存储和链式存储。
顺序存储:使用数组(地址连续的存储单元)+一个top指针来表示。
链式存储:使用多个带数据域和指针域的结点来表示。
而链栈则是指用 链式 的存储结构实现的栈结构。
以上两种存储方式在实现的过程中,有带头节点和不带头节点之分,这里实现的是带头结点的链栈。
// 带头结点的链栈
typedef struct LinkNode{
char data;
struct LinkNode *next;
} *LinkStack;
// 初始化
bool InitStack(LinkStack &S){
S = (LinkNode *) malloc(sizeof(LinkNode));
if(S == NULL) return false; // 内存空间不足
S->next = NULL;
return S;
}
// 创建链栈
LinkStack createLinkStack(){
// 创建头节点
LinkNode * head = (LinkNode *) malloc(sizeof(LinkNode));
InitStack(head);
return head;
}
// 判断是否为空
bool StackEmpty(LinkStack &S){
return S == NULL || S->next == NULL;
}
// 入栈
bool Push(LinkStack &S, char x){
LinkNode *node = (LinkNode*) malloc(sizeof(LinkNode));
if(node == NULL) return false; // 内存空间不足
node -> data = x;
node -> next = S -> next;
S->next = node;
return true;
}
// 出栈
bool Pop(LinkStack &S, char &x){
if(StackEmpty(S)) return false; // 空栈
LinkNode *p = S -> next;
x = p -> data;
S -> next = p -> next;
free(p);
return true;
}
// 取栈顶元素
bool GetTop(LinkStack &s, char &x){
if(StackEmpty(s)) return false;
x = s->next->data;
return true;
}
LeetCode 20. 括号匹配
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
Leetcode 上的题目只需要实现特定的函数,为了练习,可以先实现一个程序,然后把核心的运算放到函数里。
题目只需要让我们实现一个程序去匹配左右括号是否匹配,就跟写数学公式一样,一个标准的数学式子如果有左右括号,那么一定就是匹配的。
括号匹配一共分为两大类,N小类(N取决于括号的种类,题目中是3种)
(两大类是指左括号和右括号)
为方便描述,接下来暂时用 (
泛指所有左括号,)
泛指所有右括号
这里使用类似于消消乐的思想,遍历字符串然后逐一地将匹配的括号消去。
所以可以根据这两大类进行分类,(
直接存储,在遇到)
时在与(
进行对比,匹配时则消除(
。
例如 [()]
,当 []
匹配结束后,]
匹配的括号变成了 [
。
我们可以发现,每次)
都是找最近的(
进行匹配(就近原则),
而)
没有存储。所以可以用栈来存储(
,每次遇到 )
就弹出栈顶进行匹配。
不过这里需要注意,有两种特殊情况:
)
时,栈为空,这说明前面的(
不足以匹配,直接就是不合理的。(
),这说明)
并不足以匹配,同样是不合理的。PS:可能描述的不是很清晰,这里是以我个人的想法出发的,如果还是看不懂的话可以去看看 力扣官方的题解
完整代码实现
#include
#include
#include
/
// LeetCode 20. [括号匹配]
// https://leetcode.cn/problems/valid-parentheses/
/
// 带头结点的链栈
typedef struct LinkNode{
char data;
struct LinkNode *next;
} *LinkStack;
// 初始化
bool InitStack(LinkStack &S){
S = (LinkNode *) malloc(sizeof(LinkNode));
if(S == NULL) return false; // 内存空间不足
S->next = NULL;
return S;
}
// 创建链栈
LinkStack createLinkStack(){
// 创建头节点
LinkNode * head = (LinkNode *) malloc(sizeof(LinkNode));
InitStack(head);
return head;
}
// 判断是否为空
bool StackEmpty(LinkStack &S){
return S == NULL || S->next == NULL;
}
// 入栈
bool Push(LinkStack &S, char x){
LinkNode *node = (LinkNode*) malloc(sizeof(LinkNode));
if(node == NULL) return false; // 内存空间不足
node -> data = x;
node -> next = S -> next;
S->next = node;
return true;
}
// 出栈
bool Pop(LinkStack &S, char &x){
if(StackEmpty(S)) return false; // 空栈
LinkNode *p = S -> next;
x = p -> data;
S -> next = p -> next;
free(p);
return true;
}
// 取栈顶元素
bool GetTop(LinkStack &s, char &x){
if(StackEmpty(s)) return false;
x = s->next->data;
return true;
}
int main() {
printf("程序开始\n");
// 获取输入
printf("请输入待匹配的括号字符串:\n");
char str[1024];
gets(str);
// 输出输入(防读取错误)
printf("获取到的输入数据为:%s\n", str);
// 创建链栈
LinkStack S = createLinkStack();
// 遍历输入
int i = 0;
char top;
bool flag = true; // 标记是否为合法的括号匹配
while(str[i] != '\0'){
// 算法核心 //
// 遇到左括号直接入栈
if(str[i] == '(' || str[i] == '[' || str[i] == '{'){
Push(S,str[i]);
}
// 遇到右括号, 匹配后进的左括号
else{
// 判断栈是否为空(为空则缺少括号进行匹配,直接结束循环)
if(StackEmpty(S)){
flag = false;
break;
} else{ // 判断括号是否匹配
Pop(S, top);
if(!((top == '(' && str[i] == ')' ) ||
(top == '[' && str[i] ==']')
|| (top == '{' && str[i]=='}'))){
flag = false;
break;
}
}
}
i ++ ;
}
// 最后栈应该不为空, 否则说明匹配失败
if(!StackEmpty(S)) flag = false;
printf(flag ? "true\n" : "false\n");
printf("程序结束.");
return 0;
}
运行测试结果:
程序开始
请输入待匹配的括号字符串:
()[]
获取到的输入数据为:()[]
true
程序结束.
#include
#include
#include
// 带头结点的链栈
typedef struct LinkNode{
char data;
struct LinkNode *next;
} *LinkStack;
// 初始化
bool InitStack(LinkStack &S){
S = (LinkNode *) malloc(sizeof(LinkNode));
if(S == NULL) return false; // 内存空间不足
S->next = NULL;
return S;
}
// 创建链栈
LinkStack createLinkStack(){
// 创建头节点
LinkNode * head = (LinkNode *) malloc(sizeof(LinkNode));
InitStack(head);
return head;
}
// 判断是否为空
bool StackEmpty(LinkStack &S){
return S == NULL || S->next == NULL;
}
// 入栈
bool Push(LinkStack &S, char x){
LinkNode *node = (LinkNode*) malloc(sizeof(LinkNode));
if(node == NULL) return false; // 内存空间不足
node -> data = x;
node -> next = S -> next;
S->next = node;
return true;
}
// 出栈
bool Pop(LinkStack &S, char &x){
if(StackEmpty(S)) return false; // 空栈
LinkNode *p = S -> next;
x = p -> data;
S -> next = p -> next;
free(p);
return true;
}
// 取栈顶元素
bool GetTop(LinkStack &s, char &x){
if(StackEmpty(s)) return false;
x = s->next->data;
return true;
}
class Solution {
public:
bool isValid(string str) {
// 创建链栈
LinkStack S = createLinkStack();
// 遍历输入
int i = 0;
char top;
bool flag = true; // 标记是否为合法的括号匹配
while(str[i] != '\0'){
// 算法核心 //
// 遇到左括号直接入栈
if(str[i] == '(' || str[i] == '[' || str[i] == '{'){
Push(S,str[i]);
}
// 遇到右括号, 匹配后进的左括号
else{
// 判断栈是否为空(为空则缺少括号进行匹配,直接结束循环)
if(StackEmpty(S)){
flag = false;
break;
} else{ // 判断括号是否匹配
Pop(S, top);
if(!((top == '(' && str[i] == ')' ) ||
(top == '[' && str[i] ==']')
|| (top == '{' && str[i]=='}'))){
flag = false;
break;
}
}
}
i ++ ;
}
// 最后栈应该不为空, 否则说明匹配失败
if(!StackEmpty(S)) flag = false;
return flag;
}
};
以上方法主要是为了准备考研,然而C++提供了现成的STL库,本身就实现了许多数据结构的相关类,引入头文件后可以直接调用其方法,之后的文章也会以这种方式,先手写实现一遍数据结构(未重复的前提下),然后在放一段调用库的实现方式,对比一下可以加深记忆。
#include
#include
using namespace std;
int main() {
printf("程序开始\n");
// 获取输入
printf("请输入待匹配的括号字符串:\n");
char str[1024];
gets(str);
// 输出输入(防读取错误)
printf("获取到的输入数据为:%s\n", str);
// 创建链栈
stack<char> S;
// 遍历输入
int i = 0;
char top;
bool flag = true; // 标记是否为合法的括号匹配
while(str[i] != '\0'){
// 算法核心 //
// 遇到左括号直接入栈
if(str[i] == '(' || str[i] == '[' || str[i] == '{'){
S.push(str[i]);
}
// 遇到右括号, 匹配后进的左括号
else{
// 判断栈是否为空(为空则缺少括号进行匹配,直接结束循环)
if(S.empty()){
flag = false;
break;
} else{ // 判断括号是否匹配
top = S.top();
S.pop();
if(!((top == '(' && str[i] == ')' ) ||
(top == '[' && str[i] ==']')
|| (top == '{' && str[i]=='}'))){
flag = false;
break;
}
}
}
i ++ ;
}
// 最后栈应该不为空, 否则说明匹配失败
if(!S.empty()) flag = false;
printf(flag ? "true\n" : "false\n");
printf("程序结束.");
return 0;
}
class Solution {
public:
bool isValid(string str) {
// 创建链栈
stack<char> S;
// 遍历输入
int i = 0;
char top;
bool flag = true; // 标记是否为合法的括号匹配
while(str[i] != '\0'){
// 算法核心 //
// 遇到左括号直接入栈
if(str[i] == '(' || str[i] == '[' || str[i] == '{'){
S.push(str[i]);
}
// 遇到右括号, 匹配后进的左括号
else{
// 判断栈是否为空(为空则缺少括号进行匹配,直接结束循环)
if(S.empty()){
flag = false;
break;
} else{ // 判断括号是否匹配
top = S.top();
S.pop();
if(!((top == '(' && str[i] == ')' ) ||
(top == '[' && str[i] ==']')
|| (top == '{' && str[i]=='}'))){
flag = false;
break;
}
}
}
i ++ ;
}
// 最后栈应该不为空, 否则说明匹配失败
if(!S.empty()) flag = false;
return flag;
}
};