LeetCode20.有效的括号(以及对代码优越性的思考)

力扣20这道题在2020年非常火,B站、小米、搜狐等大厂都在面试中考察了这道题,由此可见此题的重要性。

此题虽然是简单题,但是我在做的时候,并没有像其他简单题一样,根据题目表述逻辑顺利地写出来,而是花了不少时间来思考才慢慢找到逻辑。在考虑什么样的括号才算是合适的括号时,想到一个很关键的点,就是在遍历字符串时,我们后遇到的左括号要先闭合。敏感的同学可能已经想到了,这不就是栈的特性,后进先出吗?于是,在我把逻辑基础往【栈】这个数据结构上引后,才慢慢理清思路。

基于python,本文使用两个方法解决此题,并在文末讨论代码的优越性。

解法一:【栈】的解法

  1. 创建一个空的栈stack。
  2. 遍历字符串。
  3. 遇到左括号,就把这个字符串放在stack里。
  4. 遇到右括号,就把stack里顶层的元素拿出来,看看和这个右括号是否为同一类型的括号。是则继续,不是则返回False。
  5. 直到遍历完字符串的所有元素,看stack是否还剩着左括号没被取出来。有则返回False,没有则说明一切顺利,返回True。
class Solution:
    def isValid(self, s: str) -> bool:
        stack = [] # 创建一个空的栈stack
        try:
            for i in range(len(s)): # 遍历字符串
                # 遇到左括号,就把这个字符串放在stack里
                if s[i] == '(' or s[i] == '{' or s[i] == '[':
                    stack.append(s[i])
                # 遇到右括号,就把stack里顶层的元素拿出来,看看和这个右括号是否为同一类型的括号。是则继续,不是则返回False
                elif s[i] == ')' or s[i] == '}' or s[i] == ']':
                    temp = stack.pop()
                    if temp == '(' and s[i] != ')':
                            return False
                    elif temp == '[' and s[i] != ']':
                            return False
                    elif temp == '{' and s[i] != '}':
                            return False
            # 直到遍历完字符串的所有元素,看stack是否还剩着左括号没被取出来。有则返回False,没有则说明一切顺利,返回True
            if len(stack) == 0:
                return True
            else:
                return False
        except:
            return False

注意,这里用到了python的异常处理结构try和except,是因为如果数组的第一个括号就是右括号,则temp = stack.pop()会报错,因为stack此时是空的。所以这种情况报错的话直接返回False,这不是有效的括号。

复杂度分析:

  • 时间复杂度:O(N),其中N为字符串s的长度。
  • 空间复杂度:O(n),其中n为字符串s的长度。
  • 执行用时:28ms, 99.0%
  • 内存消耗:14.9MB, 33.5%

解法二:

此方法来自LeetCode评论区的wings大神。有效的括号总有一个特点,那就是总有一对相邻的括号存在。他利用这一特点,仅用3行replace代码把括号由内到外层层拨开,来验证括号的有效性。思路巧妙至极,我看完只好再次大呼内行...

class Solution:
    def isValid(self, s):
        while '{}' in s or '()' in s or '[]' in s:
            s = s.replace('{}', '')
            s = s.replace('[]', '')
            s = s.replace('()', '')
        return s == ''

复杂度分析:

  • 时间复杂度:O(N^2),其中N为字符串s的长度。
  • 空间复杂度:O(n),其中n为字符串s的长度。
  • 执行用时:64ms, 6.91%
  • 内存消耗:14.9MB, 33.5%

关于代码优越性的思考

对比一下上面的两个解法。从思路上讲,解法一就是利用栈的常规思路,解法二更加精妙讨巧;从代码长度上讲,解法一的主体部分是19行,而解法二是5行,几乎是解法一的四分之一,对程序员来说写得更快;从执行效率上讲,解法二由于在一个while循环中用来3次replace方法,相当于每次循环都遍历字符串3次,所以解法二的实际执行效率要明显弱于解法一。

其实最开始我执行解法二的时候,看到执行时间明显下降,第一反应有些疑惑,因为这不符合我的直观印象。我们总觉得,越短的代码,设计越精妙的代码,理所当然有着更高的执行效率。但是,其实我们都忽略了一个问题,尤其是在python这种比较高级的语言中,这些看似简单的代码背后其实是有其他人已经写好了很多代码,打包成了这么一个方法。比如这里的replace看似简单,实际上这也是一个遍历字符串的循环。

所以,短而巧不能代表更好的执行效率。不否认这些花式解法要比暴力解法优美得多,有更高的艺术性。但是在实际应用中,可能低复杂度、可读性好的代码会有更好的实用性。

感谢各路大佬对每一道LeetCode题提供的各种思路,我们永远在追求兼具艺术性和实用性的代码的路上!

(如果两者一定要trade off,还是选择执行效率高的写法吧嘿嘿)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值