考研数据结构1 | 使用 C++ 实现链栈 | 栈的基本应用之括号匹配

文章目录

  • 一、链栈简介
  • 二、链栈的代码实现
  • 三、栈的应用之括号匹配
    • 3.1 思路描述
    • 3.2 完整代码(链栈)
    • 3.3 LeetCode 提交代码(链栈)
    • 3.4 完整代码(stack库)
    • 3.5 LeetCode 提交代码(stack库)

一、链栈简介


栈是一种 操作受限 的特殊的线性表,属于逻辑结构。

操作受限是指只允许在一端(栈顶)进行插入或删除的操作。

栈的特点有:

  1. 后进先出,Last In First Out ,简写为 LIFO。
  2. 不同元素进栈的出栈排列符合Catalan数。

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 上的题目只需要实现特定的函数,为了练习,可以先实现一个程序,然后把核心的运算放到函数里。

3.1 思路描述

题目只需要让我们实现一个程序去匹配左右括号是否匹配,就跟写数学公式一样,一个标准的数学式子如果有左右括号,那么一定就是匹配的。

括号匹配一共分为两大类,N小类(N取决于括号的种类,题目中是3种)

(两大类是指左括号和右括号)

为方便描述,接下来暂时用 ( 泛指所有左括号,) 泛指所有右括号

这里使用类似于消消乐的思想,遍历字符串然后逐一地将匹配的括号消去。

所以可以根据这两大类进行分类,( 直接存储,在遇到) 时在与(进行对比,匹配时则消除(

例如 [()] ,当 [] 匹配结束后,] 匹配的括号变成了 [

我们可以发现,每次) 都是找最近的( 进行匹配(就近原则),

) 没有存储。所以可以用栈来存储( ,每次遇到 ) 就弹出栈顶进行匹配。

不过这里需要注意,有两种特殊情况:

  1. 遇到 ) 时,栈为空,这说明前面的( 不足以匹配,直接就是不合理的。
  2. 遍历结束后,栈非空(即还存在(),这说明) 并不足以匹配,同样是不合理的。

PS:可能描述的不是很清晰,这里是以我个人的想法出发的,如果还是看不懂的话可以去看看 力扣官方的题解

3.2 完整代码(链栈)

完整代码实现

#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
程序结束.

3.3 LeetCode 提交代码(链栈)

#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库,本身就实现了许多数据结构的相关类,引入头文件后可以直接调用其方法,之后的文章也会以这种方式,先手写实现一遍数据结构(未重复的前提下),然后在放一段调用库的实现方式,对比一下可以加深记忆。

3.4 完整代码(stack库)

#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;
}

3.5 LeetCode 提交代码(stack库)

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;
    }
};

你可能感兴趣的:(#,数据结构(C++),考研,数据结构,c++)