面試演算法題(2)--兩個大數相加
阿新 • • 發佈:2018-12-31
兩個大數相加
聽說這是個頻繁出現的演算法題,在某某網站是的排行很靠前。
在找工作之前,同事提過一次,我沒當回事,恰恰在我第一家面試時就碰到了。
兩個大數相加。
1、是整數;
2、兩個數無限大,long都裝不下;
3、不能用BigInteger;
4、不能用任何包裝類提供的運算方法;
5、兩個數都是以字串的方式提供。
兩個字串的數字,怎麼相加?
其實也簡單,核心點考的是ASCII碼和相加進位的問題。
比喻字元型別的'9'怎麼轉換成int的9?
'9' - '0' = 9。這個演算法能理解嗎?
char型別進行算數運算時是不是自動轉換為int型別的了?
字元9的ASCII值是不是比字元0的多9個?這是第一個關鍵點。
第二個關鍵點是,注意相加時的進位。
對兩個字串的數字,可以使用字元陣列的方式處理,也可以用StringBuffer。
有人會問,StringBuffer每次append都是在後面追加,但是處理數字進位時需要在前面處理。
其實StringBuffer有個方法reverse(),翻轉字串
舉個例子比喻下整個處理過程:
String str1 = "123459";
string str2 = "123";
兩個字串需要翻轉,為什麼?
因為相加處理是從最右邊的個位開始的,還有進位處理。
呼叫reverse()方法處理後:
String str1 = "954321";
string str2 = "321";
現在就好辦了,個位對齊了。可以開始相加了,注意進位。
int carry = 0;
計算個位:
int value = str1.charAt(0) + str2.charAt(0) - 2*'0' + carry;
上面這行程式碼能理解嗎?
翻譯過來就是:'9' + '3' - 2*'0' + carry;
為什麼 - 2*'0' ?
字元進行算術運算時,會轉換成ASCII值在進行運算。
故繼續翻譯為:57 + 51 - 2*48 + 0;
是不是等於 9 + 3 + 0 ?
好,不多廢話了。相加後的值為12。
直接把12儲存在個位上嗎?當然不是,把進位的10部分儲存到carry變數中,便於進行十位數字相加時使用:
carry = 12 / 10;
value = 12 % 10;
然後把value的值儲存到最終的個位上。
一次上述步驟。
最後需要注意的是,輸入的兩個字串數字長度不一定相等。
第一次迴圈時要以較短的字串未結束點,計算兩個數字和carry的相加和進位。
然後在基於較長的字串長出的部分迴圈,與carry相加,處理進位。
聽說這是個頻繁出現的演算法題,在某某網站是的排行很靠前。
在找工作之前,同事提過一次,我沒當回事,恰恰在我第一家面試時就碰到了。
兩個大數相加。
1、是整數;
2、兩個數無限大,long都裝不下;
3、不能用BigInteger;
4、不能用任何包裝類提供的運算方法;
5、兩個數都是以字串的方式提供。
兩個字串的數字,怎麼相加?
其實也簡單,核心點考的是ASCII碼和相加進位的問題。
比喻字元型別的'9'怎麼轉換成int的9?
'9' - '0' = 9。這個演算法能理解嗎?
char型別進行算數運算時是不是自動轉換為int型別的了?
字元9的ASCII值是不是比字元0的多9個?這是第一個關鍵點。
第二個關鍵點是,注意相加時的進位。
對兩個字串的數字,可以使用字元陣列的方式處理,也可以用StringBuffer。
有人會問,StringBuffer每次append都是在後面追加,但是處理數字進位時需要在前面處理。
其實StringBuffer有個方法reverse(),翻轉字串
舉個例子比喻下整個處理過程:
String str1 = "123459";
string str2 = "123";
兩個字串需要翻轉,為什麼?
因為相加處理是從最右邊的個位開始的,還有進位處理。
呼叫reverse()方法處理後:
String str1 = "954321";
string str2 = "321";
現在就好辦了,個位對齊了。可以開始相加了,注意進位。
int carry = 0;
計算個位:
int value = str1.charAt(0) + str2.charAt(0) - 2*'0' + carry;
上面這行程式碼能理解嗎?
翻譯過來就是:'9' + '3' - 2*'0' + carry;
為什麼 - 2*'0' ?
字元進行算術運算時,會轉換成ASCII值在進行運算。
故繼續翻譯為:57 + 51 - 2*48 + 0;
是不是等於 9 + 3 + 0 ?
好,不多廢話了。相加後的值為12。
直接把12儲存在個位上嗎?當然不是,把進位的10部分儲存到carry變數中,便於進行十位數字相加時使用:
carry = 12 / 10;
value = 12 % 10;
然後把value的值儲存到最終的個位上。
一次上述步驟。
最後需要注意的是,輸入的兩個字串數字長度不一定相等。
第一次迴圈時要以較短的字串未結束點,計算兩個數字和carry的相加和進位。
然後在基於較長的字串長出的部分迴圈,與carry相加,處理進位。
上面都把核心點說了,直接來擼程式碼吧,有註釋的:
public static void main(String[] args) { String str1 = "123459"; String str2 = "123"; System.out.println(add(str1, str2));//123582 } private static String add(String str1, String str2) { //任何一個字串為null或空字串,都不需要相加了 if (str1 == null || "".equals(str1)) { return str2; } if (str2 == null || "".equals(str2)) { return str1; } int maxLength = Math.max(str1.length(), str2.length()); //定義一個存貯結果的字串,長度要比最大長度字串還長一位,用於儲存可能出現的進位 StringBuffer result = new StringBuffer(maxLength + 1); //翻轉兩個字串 str1 = new StringBuffer(str1).reverse().toString(); str2 = new StringBuffer(str2).reverse().toString(); //反轉後的結果分別為: //954321 //321 int minLength = Math.min(str1.length(), str2.length()); //進位 int carry = 0; //當前位上的數值 int currentNum = 0; //迴圈變數 int i = 0; for (; i < minLength; i++) { //分別獲取兩個字元對應的字面數值,然後相加,再加上進位 currentNum = str1.charAt(i) + str2.charAt(i) - 2 * '0' + carry; //獲取進位 carry = currentNum / 10; //處理當前位的最終值 currentNum %= 10; //儲存當前位的值到最終的字元緩衝區中 result.append(String.valueOf(currentNum)); } if (str1.length() < str2.length()) { //選擇 str1 = str2; } for (; i < str1.length(); i++) { //分別獲取兩個字元對應的字面數值,然後相加,再加上進位 currentNum = str1.charAt(i) - '0' + carry; //獲取進位 carry = currentNum / 10; //處理當前位的最終值 currentNum %= 10; //儲存當前位的值到最終的字元緩衝區中 result.append(String.valueOf(currentNum)); } //處理最後一個的進位(當迴圈結束後,是不是還可能會有一個進位) if (carry > 0) { result.append(String.valueOf(carry)); } //最後翻轉恢復字串,再返回 return result.reverse().toString(); }
如果你有更好的想法,歡迎留言