[LeetCode No.65]——什麼是面向測試編程?看看本這道題就知道了!口區

有效數字 困難

在這裏插入圖片描述
沒有get到這道題目的點,這樣的題目很搞心態。尤其是看了作者的解答之後更懵逼了,作者使用了責任鏈的設計模式,從來沒想到解算法題還能用到設計模式。大牛的思維方式就適合我不一樣,解個算法題都能考慮到擴展性和複用性。

思路一:暴力法

按序遍歷字符串,逐位判斷是否合法。注意要去除首尾空格。

這種方法就是比較噁心,很容易有考慮不到的情況。

而且測試用例中有:".1"、".2"、"+.8"、“46.”、"2e0"等等,預期輸出都是true。真的噁心到了。。。

切身感受什麼是面向測試編程!!!!!

public boolean isNumber(String s) {
    if (s==null)return false;
    s = s.trim();
    if (s.length()==0)return false;
    boolean pointSeen = false;//是否出現過 .
    boolean eSeen = false;//是否出現過 e
    boolean numberSeen = false;//是否出現過 0-9
    boolean numberAfterE = true;//e之後是否出現 0-9
    for(int i=0; i<s.length(); i++) {
        if('0' <= s.charAt(i) && s.charAt(i) <= '9') {//當前元素是數字
            numberSeen = true;
            numberAfterE = true;
        } else if(s.charAt(i) == '.') {//當前元素是 .
            if(eSeen || pointSeen) {//已經出現過 e或者.,則非法
                return false;
            }
            pointSeen = true;
        } else if(s.charAt(i) == 'e') {//當前元素 e
            if(eSeen || !numberSeen) {//已經出現過 e或者e之前沒出現過數字,則非法
                return false;
            }
            numberAfterE = false;//注意這點很重要,現在開始記錄e之後是否有數字
            eSeen = true;
        } else if(s.charAt(i) == '-' || s.charAt(i) == '+') {//當前元素是-或+
            if(i != 0 && s.charAt(i-1) != 'e') {//如果-或+不是第一個元素 或者 之前不是 e
                return false;
            }
        } else {//當前元素不是0-9、. 、e 、- 、+,非法
            return false;
        }
    }
    //是否有數字並且e之後也有數字
    return numberSeen && numberAfterE;
}

時間複雜度:O(n)

寫完暴力法,忍不住給下面這個騷操作點了個贊 : )

class Solution:
    def isNumber(self, s: str) -> bool:
        try:
            key=float(s)
            return True
        except:
            return False

思路二:確定有限自動機(DFA)

下面搬運自leetcode社區windliang,我不是大佬,我只是大佬的搬運工。

先畫出狀態轉換圖:
在這裏插入圖片描述
如上圖,從 0 開始總共有 9 個狀態,橙色代表可接受狀態,也就是表示此時是合法數字。總共有四大類輸入,數字,小數點,e 和 正負號。我們只需要將這個圖實現就夠了。

這種方式思路清晰多了,但是之前沒有接觸過這種方法實現起來還是很生疏的。

public boolean isNumber(String s) {
    int state = 0;//當前狀態
    s = s.trim();//去除頭尾的空格
    //遍歷所有字符,當做輸入
    for (int i = 0; i < s.length(); i++) {
        switch (s.charAt(i)) {
                //輸入正負號
            case '+':
            case '-':
                if (state == 0) {
                    state = 1;
                } else if (state == 4) {
                    state = 6;
                } else {
                    return false;
                }
                break;
                //輸入數字
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                //根據當前狀態去跳轉
                switch (state) {
                    case 0:
                    case 1:
                    case 2:
                        state = 2;
                        break;
                    case 3:
                        state = 3;
                        break;
                    case 4:
                    case 5:
                    case 6:
                        state = 5;
                        break;
                    case 7:
                        state = 8;
                        break;
                    case 8:
                        state = 8;
                        break;
                    default:
                        return false;
                }
                break;
                //小數點
            case '.':
                switch (state) {
                    case 0:
                    case 1:
                        state = 7;
                        break;
                    case 2:
                        state = 3;
                        break;
                    default:
                        return false;
                }
                break;
                //e
            case 'e':
                switch (state) {
                    case 2:
                    case 3:
                    case 8:
                        state = 4;
                        break;
                    default:
                        return false;
                }
                break;
            default:
                return false;

        }
    }
    //橙色部分的狀態代表合法數字
    return state == 2 || state == 3 || state == 5 || state == 8;
}

時間複雜度:O(n)

思路三:責任鏈模式

解法二看起來已經很清晰明瞭了,只需要把狀態圖畫出來,然後實現代碼就很簡單了。但是缺點是,如果狀態圖少考慮了東西,再改起來就會很麻煩。

這裏作者提出來,利用責任鏈的設計模式,會使得寫出的算法擴展性以及維護性更高。這裏用到的思想就是,每個類只判斷一種類型。比如判斷是否是正數的類,判斷是否是小數的類,判斷是否是科學計數法的類,這樣每個類只關心自己的部分,出了問題很好排查,而且互不影響。

雖然代碼變多了,但是維護性,擴展性變的很強了。比如,題目新增了一種情況,“0x123” 16 進制也算是合法數字。這樣的話,解法一和解法二就沒什麼用了,完全得重新設計。但對於解法三,我們只需要新增一個類,專門判斷這種情況,然後加到執行者的數組裏就夠了,很牛逼!

//每個類都實現這個接口
interface NumberValidate {
    boolean validate(String s);
}
//定義一個抽象類,用來檢查一些基礎的操作,是否爲空,去掉首尾空格,去掉 +/-
//doValidate 交給子類自己去實現
abstract class  NumberValidateTemplate implements NumberValidate{

    public boolean validate(String s)
    {
        if (checkStringEmpty(s))
        {
            return false;
        }

        s = checkAndProcessHeader(s);

        if (s.length() == 0)
        {
            return false;
        }

        return doValidate(s);
    }

    private boolean checkStringEmpty(String s)
    {
        if (s.equals(""))
        {
            return true;
        }

        return false;
    }

    private String checkAndProcessHeader(String value)
    {
        value = value.trim();

        if (value.startsWith("+") || value.startsWith("-"))
        {
            value = value.substring(1);
        }


        return value;
    }



    protected abstract boolean doValidate(String s);
}

//實現 doValidate 判斷是否是整數
class IntegerValidate extends NumberValidateTemplate{

    protected boolean doValidate(String integer)
    {
        for (int i = 0; i < integer.length(); i++)
        {
            if(Character.isDigit(integer.charAt(i)) == false)
            {
                return false;
            }
        }

        return true;
    }
}

//實現 doValidate 判斷是否是科學計數法
class SienceFormatValidate extends NumberValidateTemplate{

    protected boolean doValidate(String s)
    {
        s = s.toLowerCase();
        int pos = s.indexOf("e");
        if (pos == -1)
        {
            return false;
        }

        if (s.length() == 1)
        {
            return false;
        }

        String first = s.substring(0, pos);
        String second = s.substring(pos+1, s.length());

        if (validatePartBeforeE(first) == false || validatePartAfterE(second) == false)
        {
            return false;
        }


        return true;
    }

    private boolean validatePartBeforeE(String first)
    {
        if (first.equals("") == true)
        {
            return false;
        }

        if (checkHeadAndEndForSpace(first) == false)
        {
            return false;
        }

        NumberValidate integerValidate = new IntegerValidate();
        NumberValidate floatValidate = new FloatValidate();
        if (integerValidate.validate(first) == false && floatValidate.validate(first) == false)
        {
            return false;
        }

        return true;
    }

    private boolean checkHeadAndEndForSpace(String part)
    {

        if (part.startsWith(" ") ||
            part.endsWith(" "))
        {
            return false;
        }

        return true;
    }

    private boolean validatePartAfterE(String second)
    {
        if (second.equals("") == true)
        {
            return false;
        }

        if (checkHeadAndEndForSpace(second) == false)
        {
            return false;
        }

        NumberValidate integerValidate = new IntegerValidate();
        if (integerValidate.validate(second) == false)
        {
            return false;
        }

        return true;
    }
}
//實現 doValidate 判斷是否是小數
class FloatValidate extends NumberValidateTemplate{

    protected boolean doValidate(String floatVal)
    {
        int pos = floatVal.indexOf(".");
        if (pos == -1)
        {
            return false;
        }

        if (floatVal.length() == 1)
        {
            return false;
        }

        NumberValidate nv = new IntegerValidate();
        String first = floatVal.substring(0, pos);
        String second = floatVal.substring(pos + 1, floatVal.length());

        if (checkFirstPart(first) == true && checkFirstPart(second) == true)
        {
            return true;
        }

        return false;
    }

    private boolean checkFirstPart(String first)
    {
        if (first.equals("") == false && checkPart(first) == false)
        {
            return false;
        }

        return true;
    }

    private boolean checkPart(String part)
    {
        if (Character.isDigit(part.charAt(0)) == false ||
            Character.isDigit(part.charAt(part.length() - 1)) == false)
        {
            return false;
        }

        NumberValidate nv = new IntegerValidate();
        if (nv.validate(part) == false)
        {
            return false;
        }

        return true;
    }
}
//定義一個執行者,我們把之前實現的各個類加到一個數組裏,然後依次調用
class NumberValidator implements NumberValidate {

    private ArrayList<NumberValidate> validators = new ArrayList<NumberValidate>();

    public NumberValidator()
    {
        addValidators();
    }

    private  void addValidators()
    {
        NumberValidate nv = new IntegerValidate();
        validators.add(nv);

        nv = new FloatValidate();
        validators.add(nv);

        //nv = new HexValidate();
        //validators.add(nv);

        nv = new SienceFormatValidate();
        validators.add(nv);
    }

    @Override
    public boolean validate(String s)
    {
        for (NumberValidate nv : validators)
        {
            if (nv.validate(s) == true)
            {
                return true;
            }
        }

        return false;
    }


}
public boolean isNumber(String s) {
    NumberValidate nv = new NumberValidator();
    return nv.validate(s);
}

本人菜鳥,有錯誤請告知,感激不盡!

更多題解和學習記錄博客:博客github

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章