1. 程式人生 > >面試演算法題(2)--兩個大數相加

面試演算法題(2)--兩個大數相加

兩個大數相加


聽說這是個頻繁出現的演算法題,在某某網站是的排行很靠前。
在找工作之前,同事提過一次,我沒當回事,恰恰在我第一家面試時就碰到了。


兩個大數相加。
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();
}

如果你有更好的想法,歡迎留言