zoukankan      html  css  js  c++  java
  • 这道LeetCode题究竟有什么坑点,让它的反对是点赞的9倍?

    本文始发于个人公众号:TechFlow,原创不易,求个关注


    今天是LeetCode专题的第38篇文章,我们一起来看看第65题,Valid Number。

    曾经我们聊到过算法当中的一个类别——模拟题。所谓的模拟题就是题面非常简单,也不涉及任何复杂的算法,但是要实现的功能比较麻烦,非常考验人思维的缜密程度,很难写出bug-free的代码来。今天要说的65题可以说是其中的典范,它的题面非常简单,简单到只有一句话,但是要实现非常麻烦,比较锻炼人的耐心,以至于反对的人是点赞的9倍,我们一起来看看。

    题面

    给定一个字符串,判断它是否是一个合法的浮点数。

    从题面来看只有一句话,似乎非常简单。但是实际上如果你仔细研究一下样例和提示,会发现事情可能和你想的不太一样。

    样例

    "0" => true
    " 0.1 " => true
    "abc" => false
    "1 a" => false
    "2e10" => true
    " -90e3   " => true
    " 1e" => false
    "e3" => false
    " 6e-1" => true
    " 99e2.5 " => false
    "53.5e93" => true
    " --6 " => false
    "-+3" => false
    "95a54e53" => false
    

    提示

    我们有意将问题陈述地比较模糊。在实现代码之前,你应当事先思考所有可能的情况。这里给出一份可能存在于有效十进制数字中的字符列表:
    

    数字 0-9
    指数 - "e"
    正/负号 - "+"/"-"
    小数点 - "."
    当然,在输入中,这些字符的上下文也很重要。

    不知道大家感受到了没有,这其中的情况不少,涉及的符号就很多。除了基本的数字之外,还有小数点、空格、正负号、e。这些符号带来的合法和非法的情况都很多,更何况这些符号之间还可以互相组合,又会引申出新的情况。

    更坑爹的一点是,这题简单粗暴,只有这一个解法,我们别无选择,只有覆盖所有的情况才能通过。所以如果你试着去想清楚所有的这些情况,你会发现这是非常困难的一件事,甚至可能会越想情况越多,觉得怎么也理不清楚,即使你理出了很多情况,也不知道是否有遗漏,很容易让人抓狂并且心烦气躁,明明很简单的问题做不出来,感受肯定不好。先别着急,这正是模拟题考验人的地方,它考验的不仅是思维,也是心态。

    所以,先深吸一口气,冷静下来,我们仔细地分析一下这些情况。

    解法

    我们从列举所有的情况入手是非常困难的,因为符号之间互相组合的情况实在是很多,一一列举全并且用代码实现的代价很大。我们先把这些烦人的情况放在一边,我们先来思考一下问题所在。

    这个问题的核心就是判断浮点数是否合法,合法的浮点数的情况也是不少的,但这些情况虽然多,彼此之间是有联系的。最起码我们可以发现符号之间的顺序,如果我们把一个合法的浮点数进行拆分,它大概可以分成以下几个部分。

    首先是符号位,表示这个数是正数还是负数。题目当中没有明说,但是我们可以猜测出来,正数用正号表示也是合法的。

    第二个部分是科学记数法的前半部分,它可以是一个小数。

    第三个部分是e,即科学记数法当中的e。

    最后一个部分是整数部分,表示e的指数,根据科学记数法的定义,必然是一个整数。但是可以是负数。

    当我们把这四个部分列举出来之后,再来进行判断就容易多了。因为这四个部分是有顺序的,我们只需要判断它们顺序的合理性就可以了。根据顺序的合理性,我们可以进一步推测出每一个符号允许出现的位置,所有和预期位置不符的符号都是非法的。

    根据这一点我们可以推导出一些结论:

    1. 空格只能出现在首尾,出现在中间一定是非法的。
    2. 正负号只能出现在两个地方,第一个地方是数字的最前面,表示符号。第二个位置是e后面,表示指数的正负。如果出现在其他的位置一定也是非法的。
    3. 数字,数字没有特别的判断,本题当中没有前导0的问题。
    4. e只能出现一次,并且e之后一定要有数字才是合法的,123e这种也是非法的。
    5. 小数点,由于e之后的指数一定是整数,所以小数点最多只能出现一次,并且一定要在e之前。所以如果之前出现过小数点或者是e,再次出现小数点就是非法的。

    当我们把每一个符号合法的情况梳理清楚之后,会发现其实也没有那么复杂,情况也没有那么多。这其实也是常用套路,我们把互相耦合的一些变量拆分开了,彼此互不影响。这样我们就可以单独考虑这其中的每个零件,而不用面对它们互相耦合的复杂情况了。

    我们把刚才梳理出来的全部用代码实现就可以通过这题了:

    class Solution:
        def isNumber(self, s: str) -> bool:
            s = s.strip()
            numbers = [str(i) for i in range(10)]
            n = len(s)
            
            # 用4个标记记录e和小数点以及数字和e之后的数字有无出现过
            e_show_up, dot_show_up, num_show_up, num_after_e = False, False, False, False
            
            for i in range(n):
                c = s[i]
                # 如果是数字,则将数字和e后出现的数字都标记为true
                # 没有e的浮点数也认为e之后出现过数字
                if c in numbers:
                    num_show_up = True
                    num_after_e = True
                # 如果是正负号,只有出现在首位或者是e后面才是合法
                elif c in ('+', '-'):
                    if i > 0 and s[i-1] != 'e':
                        return False
                # 如果是小数点,那么必须保证e和小数点都没有出现过
                elif c == '.':
                    if dot_show_up or e_show_up:
                        return False
                    dot_show_up = True
                # 如果是e,要保证已经有数字出现,并且e没有出现过
                elif c == 'e':
                    if e_show_up or not num_show_up:
                        return False
                    e_show_up = True
                    num_show_up = False
                # 其他情况都视为非法
                else:
                    return False
                
            return num_show_up and num_after_e
    

    总结

    这题我们看代码好像也不复杂,但是想要把这么多条件都梳理清楚,写出这样简单的代码也不是一件容易的事情。必须建立在我们对问题有了充分的思考的基础上,其实我们的代码还疏漏了一个条件就是前导零的情况。如果0出现在数字最前面其实也是非法的,不过这题当中没有针对这样的case,但实际上我们是应该考虑的,这里也是我偷懒了。

    很多人很讨厌模拟题,包括我在内,原因就是情况太多很恶心人,经常会中招遗漏一些情况。看看这题的评分也能看得出来,点赞的只有678,反对的却又4572,可见一斑。但其实模拟题也是一种对思维的锻炼,需要我们有冷静的思维和理智的分析,这也是一个优秀的选手必不可少的。希望大家都能攻克这道难关。

    今天的文章就到这里,原创不易,扫码关注我,获取更多精彩文章。

  • 相关阅读:
    HDU 3401 Trade
    POJ 1151 Atlantis
    HDU 3415 Max Sum of MaxKsubsequence
    HDU 4234 Moving Points
    HDU 4258 Covered Walkway
    HDU 4391 Paint The Wall
    HDU 1199 Color the Ball
    HDU 4374 One hundred layer
    HDU 3507 Print Article
    GCC特性之__init修饰解析 kasalyn的专栏 博客频道 CSDN.NET
  • 原文地址:https://www.cnblogs.com/techflow/p/12921901.html
Copyright © 2011-2022 走看看