LeetCode初級算法之字符串:8 字符串轉換整數 (atoi)

字符串轉換整數 (atoi)

題目地址:https://leetcode-cn.com/problems/string-to-integer-atoi/java

請你來實現一個 atoi 函數,使其能將字符串轉換成整數。git

首先,該函數會根據須要丟棄無用的開頭空格字符,直到尋找到第一個非空格的字符爲止。接下來的轉化規則以下:正則表達式

  • 若是第一個非空字符爲正或者負號時,則將該符號與以後面儘量多的連續數字字符組合起來,造成一個有符號整數。算法

  • 假如第一個非空字符是數字,則直接將其與以後連續的數字字符組合起來,造成一個整數。數據結構

  • 該字符串在有效的整數部分以後也可能會存在多餘的字符,那麼這些字符能夠被忽略,它們對函數不該該形成影響。函數

注意:假如該字符串中的第一個非空格字符不是一個有效整數字符、字符串爲空或字符串僅包含空白字符時,則你的函數不須要進行轉換,即沒法進行有效轉換。工具

在任何狀況下,若函數不能進行有效的轉換時,請返回 0 。學習

提示:code

本題中的空白字符只包括空格字符 ' ' 。
假設咱們的環境只能存儲 32 位大小的有符號整數,那麼其數值範圍爲 [−231, 231 − 1]。若是數值超過這個範圍,請返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。orm

示例 1:

輸入: "42"
輸出: 42

示例 2:

輸入: " -42"
輸出: -42
解釋: 第一個非空白字符爲 '-', 它是一個負號。
咱們儘量將負號與後面全部連續出現的數字組合起來,最後獲得 -42 。

示例 3:

輸入: "4193 with words"
輸出: 4193
解釋: 轉換截止於數字 '3' ,由於它的下一個字符不爲數字。

示例 4:

輸入: "words and 987"
輸出: 0
解釋: 第一個非空字符是 'w', 但它不是數字或正、負號。所以沒法執行有效的轉換。

示例 5:

輸入: "-91283472332"
輸出: -2147483648
解釋: 數字 "-91283472332" 超過 32 位有符號整數範圍。 所以返回 INT_MIN (−231) 。

解法一

嚴格來講這個問題其實沒有太多考察算法的內容不涉及數據結構、算法思惟的選取。它是一個數據的效驗篩選,更多的像開發場景中的一些原始數據的處理參數的篩選。因此不難但有較多的判斷語句涉及一些細節問題須要仔細對比

主體

  1. 去掉前導空格
  2. 處理正負號
  3. 識別數字

邊界

  1. 整數溢出
  2. 索引越界

細節

  1. 無效轉換返回0
public int myAtoi(String str) {
    char[] chars = str.toCharArray();
    int n = chars.length;
    int index = 0;
    //1.去掉前導空格
    while (index < n && chars[index] == ' ') {
        index++;
    }
    //2.符號處理
    int sign = 1;
    if(index < n && chars[index] == '+'){
        index++;
    }else if(index < n && chars[index] == '-'){
        sign = -1;
        index++;
    }
    //3.拼接數字
    int result = 0;
    while (index < n && chars[index]-'0' >= 0 && chars[index]-'0' <= 9) {
        int num = chars[index] - '0';
        if (result > (Integer.MAX_VALUE - num) / 10) {
            // 原本應該是 result * 10 + num > Integer.MAX_VALUE
            // 可是 *10 和 + num 都有可能越界,因此在上面逆向判斷
            return sign < 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE;
        }
        result = result * 10 + num;
        index++;
    }
    return result * sign;
}

解法二:正則表達式

這樣一個篩選串的過程咱們除了本身去去遍歷判斷,也可使用正則表達式的方式。用到Java類庫的相關工具但本質上仍是和解法一是同樣的。須要引入java.util.regex包下的Pattern與Matcher

public int myAtoi(String str) {
    str = str.trim();
    Pattern pattern = Pattern.compile("[-+]??[0-9]++");
    Matcher matcher = pattern.matcher(str);
    if (matcher.lookingAt()) {
        String num = str.substring(0, matcher.end());
        try {
            return Integer.parseInt(num);
        } catch (NumberFormatException nfe) {
            return num.charAt(0) == '-' ? Integer.MIN_VALUE : Integer.MAX_VALUE;
        }
    }
    return 0;
}

解法三:有限狀態自動機

正則表達的所匹配的全部字符串構成均可以用有限自動機識別,其實上面解法的每一個過程斷定就是一個有限自動機的每一個狀態。從去除空格階段到取符號階段到數字階段到結束。和KMP同樣均可解決字符串匹配相關問題

狀態機裏面有 4 個概念:

  • State ,狀態。一個狀態機至少要包含兩個狀態。
  • Event ,事件。事件就是執行某個操做的觸發條件或者口令。
  • Action ,動做。事件發生之後要執行動做。
  • Transition ,變換。也就是從一個狀態變化爲另外一個狀態。

套到這道題裏就是咱們的程序在每一個時刻有一個狀態 s ,每次從序列中輸入一個字符 c ,並根據字符 c 轉移到下一個狀態 s' 。這樣,咱們只須要創建一個覆蓋全部狀況的從 sc 映射到 s' 的表格便可解決題目中的問題。

答案中給出的示例圖是這樣的:

用圖表來表示:

我又畫了下面一張圖和上面兩個同義。(0表示狀態start,1表示狀態signed,2表示狀態int_number,3表示狀態end,'其餘'表示除[空格、正負號、數字]以外的字符)

意思就是說依次輸入字符。首先字符是進入狀態0(start)若是字符是空格那麼下個仍然進入狀態0.若是是其餘則進入狀態3(end)結束處理。那咱們就能夠實現這樣一個自動機

class Automata{
    //下次的狀態
    private int state=0;
    //狀態表
    private int[][] table={{0,1,2,3},{3,3,2,3},{3,3,2,3},{3,3,3,3}};
    //字符的四種狀況
    public int getCharState(char c){
        if(c==' ')return 0;
        if(c=='+'|| c=='-')return 1;
        if(Character.isDigit(c))return 2;
        return 3;
    }
    int result = 0; //結果
    int sign = 1; //符號
    public void input(char c){
        //經過此次的字符設定下個狀態
        state=table[state][getCharState(c)];
        //state == 0 直接過
        if(state==1 &&c=='-') sign=-1;
        if(state==2){
            int num = c - '0';
            //除了經過當前狀態和字符來判斷下個去往狀態
            //溢出發生時其實也應該把狀態設爲3(end)
            if (result > (Integer.MAX_VALUE - num) / 10) {
                result = sign < 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE;
                sign = 1;
                state = 3;
            }else result = result * 10 + num;
        }
        //state == 3 直接過
    }
}

而後就是使用咱們的自動機解題啦

public int myAtoi(String str) {
    Automata auto=new Automata();
    char[] chars=str.toCharArray();
    for(char c:chars){
        auto.input(c);
    }
    return auto.sign*auto.result;
}

或者state爲3這邊也直接結束不去掉用空方法

public int myAtoi(String str) {
    Automata auto=new Automata();
    char[] chars=str.toCharArray();
    int i = 0;
    while(i<chars.length && auto.state != 3){
        auto.input(chars[]);
        i++;
    }
    return auto.sign*auto.result;
}

總結

就這題自己來說是比較簡單的,主要是處理細節問題可能不少小夥伴在像解法一的處理過程當中沒有組織清晰致使雖然能解出題但有不少代碼是冗餘的或者重複判斷了的地方。最終解法一其實也不過兩個if而已沒有像傳聞中那樣說不少ifelse,三個處理第一個空格處理是循環處理,第二符號只有首位因此單個if,第三個數字也是循環處理。所以三個必要的判斷兩個是while一個是if。最後還加上一個溢出的判斷。除此以外這題也有學習到的地方,第一是對正則的回顧,第二是引出有限狀態自動機這樣一個計算機思想也是等於又溫習下離散數學。對於這題沒有什麼算法思惟理清思路書寫下來就是解法一的這樣一個過程。所以這一題的意圖可能就是讓咱們學習到有限狀態自動機封裝這樣一個機器或者規則往這個規則裏輸入語言串它去給咱們輸出相應的結果。

相關文章
相關標籤/搜索