栈的应用1:括号匹配

先看简单的例题1:如果序列中只有小括号‘(’和‘)’,那么怎么判断序列是否合法?

解法1:

step1:用一个指针遍历这个序列。

已知的结论:若序列是合法的,它需要满足下面2个条件。

1)指针遍历在序列的任何一个位置上,已遍历的部分,左括号数量>=右括号数量。

2)在指针遍历到序列的最后一个位置上,就是说整个序列,左括号数量==右括号数量。

示例:((()可能是合法的。     ((())))一定不合法。

step2:程序运行中只需要记录左括号的数量和右括号的数量即可。

代码:

bool isValid(char* s){ //bool返回序列s是否合法,序列s其实是一个字符串。
    int lnum = 0,rnum = 0; //lnum是左括号的数量,rnum是右括号的数量。
    int len = strlen(s); //len记录字符串s序列有多长。
    for(int i=0; i<len; i++){ //step1:用i这个索引来遍历序列。索引i就相当于指针。
        switch(s[i]){ //判断索引i指向的字符s[i]是不是左括号或右括号。
            case '(': ++lnum; break; //若s[i]是左括号,则左括号计数+1。
            case ')': ++rnum; break; //若s[i]是右括号,则右括号计数+1。
            default : return false; //这个序列s,除了小括号以外还有别的字符,不合法。
        }
        if(lnum >= rnum) continue; //step2:在序列s已遍历的部分,左括号数量>=右括号数量,才合法
        else return false; //左括号数量<右括号数量,如序列(())),不合法的。
    }
    //程序走到这里,意味着已经遍历完了序列s。这里已经剔除掉所有左括号数量<右括号数量的情况。
    //遍历完序列s的所有元素,若左括号数量==右括号数量,就合法,返回true。
    //若左括号数量>右括号数量,依然不合法。
    return lnum == rnum; 
}

上面的程序还有一点点优化的余地,就是可以不用lnum和rnum这两个变量去分别记录左右括号的数量,而是可以只设置一个变量num来记录左右括号数量的差值即可。

若 左括号数量>=右括号数量,则左右括号的插值 num = lnum-rnum >= 0

遍历序列,遇到左括号,num+1,遇到右括号,num-1。

中途如果num>=0就合理,最后num==0就合理。

bool isValid(char* s){ //bool返回序列s是否合法,序列s其实是一个字符串。
    int num = 0; //num是左括号数量和右括号数量的差值。num = 左括号数量-右括号数量
    int len = strlen(s); //len记录字符串s序列有多长。
    for(int i=0; i<len; i++){ //step1:用i这个索引来遍历序列。索引i就相当于指针。
        switch(s[i]){ //判断索引i指向的字符s[i]是不是左括号或右括号。
            case '(': ++num; break; //若s[i]是左括号,则num+1。
            case ')': --num; break; //若s[i]是右括号,则num-1。
            default : return false; //这个序列s,除了小括号以外还有别的字符,不合法。
        }
        if(num >= 0) continue;
        //step2:在序列s已遍历的部分,左括号数量>=右括号数量(差值 num>=0),才合法。
        else return false;
        //左括号数量<右括号数量(差值 num<0),如序列(())),不合法的。
    }
    //遍历完序列s的所有元素,若左括号数量==右括号数量,即差值num==0,就合法,返回true。
    //若左括号数量>右括号数量,即差值>0,依然不合法,返回false。
    return num == 0; 
}

接下来要利用来解决上面的问题。

解法2:

遍历序列的时候,遇到了左括号‘(’,将其入栈。

若遇到右括号‘)’,这个右括号‘)’能和栈中的一个左括号‘(’凑成一对‘()’。则将栈中的一个左括号出栈与此右括号抵消。

正确的思维:

将一对括号‘()‘看成是一个完整的事件。(())看作是一个大事件嵌套了一个小事件。

这里的23和45就是小事件,16是大事件。想解决16这个大事件必须得先把23和45解决了。


把一对括号‘()’,这一个完整的事件,看成是一个函数funcA()。

则上面序列“(()())”可写成下面代码:

最外层的funcA就是大事件16,里面的funcA是需要解决小事件23和45。

这个数据结构,正好适合用来处理这种大事件里面套着小事件之类的问题。

这里比较特殊,这还是一个递归问题。故栈还可以用来处理递归问题


一般情况:把序列"([]{()}())"可理解成下面代码:

 把"()"作为funcA,"[]"作为funcB,"{}"作为funcC。

上面代码就是大事件嵌套小事件,而并非递归。这适用于用栈来解决问题。上面的递归只是特殊情况,当然因为是特殊情况,栈也能够解决递归问题了。

括号匹配:leetcode 20

请看题目:

 题目的中文版(力扣官网原文):

简要概括题目:给你一个只有括号的序列,看看它合不合法。

解题方法,利用栈即可。

这道题具体代码实现,详情见:https://mp.csdn.net/mp_blog/creation/editor/13073726

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值