1. 程式人生 > 實用技巧 >leetcode-兩數相加

leetcode-兩數相加

兩數相加

題目:

我的解法:

 /**
* @Description TODO
* @Param [l1, l2]
* @return arrays.addTwoNumbers.ListNode
* @date 2020/7/14 17:21
* @author huanl
*
* 思路:先把他全部拿出來,拿出來的時候用字串的append函式連線起來,後面再用reverse去反轉.在把他們相加的到的
* 數轉換為StringBuilder型別,然後反轉,然後在把他變成ListNode型別.
*
* 問題: 為什麼要全部拿出來? 我們正常時候做加法,不就是從低位做起,然後有進位就記錄一下,下一次新增上.現在連結串列他給我們就是已經弄好了的低位,
* 直接從低位開始相加就好了.
* 直接一個一個取然後加的話怎麼判斷他本身到底有沒有值? 噢,直接判斷這個節點是不是為null
* 加到最後可能最後一次的進位記錄為一,怎麼處理? 再在後面多加一個節點,將這個一記錄上去.
*/
public ListNode addTwoNumber(ListNode l1, ListNode l2) {

//用StringBuilder取出l1和l2裡面所有的
StringBuilder num1 = new StringBuilder();
StringBuilder num2 = new StringBuilder();
num1.append(l1.val);
num2.append(l2.val);
while (l1.next != null) {
num1.append(l1.next.val);
l1 = l1.next;
}
while (l2.next != null) {
num2.append(l2.next.val);
l2 = l2.next;
}
//反轉為正常的順序
num1 = num1.reverse();
num2 = num2.reverse();

//將兩個字串相加 (因為直接一次的話會超出int或者long的長度) 其實最好就是將他們逆轉過來,這樣才符合我們日常的加法
int[] nums = new int[(Math.max(num1.length(), num2.length())) + 1];
for (int i = 1; i <= (Math.max(num1.length(), num2.length())); i++) {
if (i>num1.length()){
nums[nums.length-i] = Integer.parseInt(String.valueOf(num2.charAt(num2.length()-i)));
}else if(i>num2.length()){
nums[nums.length-i] = Integer.parseInt(String.valueOf(num1.charAt(num1.length()-i)));
}else {
nums[nums.length-i] = Integer.parseInt(String.valueOf(num1.charAt(num1.length()-i))) + Integer.parseInt(String.valueOf(num2.charAt(num2.length()-i)));
}

}

//處理進位資訊
for (int i = nums.length - 1; i > 0; i--) {
nums[i-1] = nums[i-1] + nums[i] / 10;
nums[i] = nums[i] % 10;
}

//因為兩數相加可能會多一位,也就是第一位可能為1,可能為0,這裡就先不處理第一位,直接從第二位開始處理
StringBuilder num = new StringBuilder();
for (int i = 1; i < nums.length ; i++) {
num.append(nums[i]);
}
//處理第一位,如果為一,則直接插入最前面
if (nums[0] != 0) {
num.insert(0,nums[0]);
}
num = num.reverse();

//將字串型別的轉化為ListNode型別
ListNode ret = new ListNode(num.charAt(0) - 48);

ListNode jilu = ret;

for (int i = 1; i < num.length(); i++) {
jilu.next = new ListNode(num.charAt(i) - 48);
jilu = jilu.next;
}
return ret;

}

問題:

  1. 為什麼要先全部取出來? 有什麼好處? 還是不能直接一個一個操作?

  2. 正是因為想要全部取出來,然後StringBuilder型別拼接快,然後StringBuilder又有一個反轉函式,所以去用了StringBuilder,然後反轉之後,本來想要用Integer.parseInt()轉化為int然後相加,但是發現這個太容易直接超出範圍,所以不得不直接用StringBuilder型別的直接相加,並且還用了反轉之後的來相加,這就導致了相加函式複雜,並且相加之後,還需要處理進位資訊,處理有沒有多了一位的資訊.

  3. 總結:

    1. 第一步的思路就問題,導致後續麻煩不斷的到來,那為什麼我會想取直接把他全部取出來呢? 
    沒有取過多的考慮全部取出來有什麼麻煩,就是簡單就直接去取出來
    2. 能夠去寫出不定長字串的加法,這個值得鼓勵.

優解:

   /**
* @Description TODO
* @Param [l1, l2]
* @return arrays.addTwoNumbers.ListNode
* @date 2020/7/14 17:08
* @author huanl
*/
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0);
ListNode p = l1, q = l2, curr = dummyHead;
int carry = 0;
while (p != null || q != null) {
int x = (p != null) ? p.val : 0;
int y = (q != null) ? q.val : 0;
int sum = carry + x + y;
carry = sum / 10;
curr.next = new ListNode(sum % 10);
curr = curr.next;
if (p != null) p = p.next;
if (q != null) q = q.next;
}
if (carry > 0) {
curr.next = new ListNode(carry);
}
return dummyHead.next;
}

優點:

1. 一個一個取,取出來直接進行操作,該進位的直接將進位符標誌為1.
為什麼要一個一個取?
因為他給我們的就是符合加法計算規律的,也就是先從低位算起,有進位加到高位,並且連結串列取值的方向也正好是從低位到高位,各種條件都非常適合直接加.
2. 注意細節:
a. 先建造一個頭節點,這個頭節點裡面的值不和任何有關,那為什麼要建一個這樣的頭節點呢? 因為沒有這個頭節點,第一次加法的時候就沒有連結串列去操作,那麼就勢必需要把第一次提出來先做,這樣就感覺有點不統一了.
b. 用另外一個指標去操作,因為你需要返回這個連結串列,那麼這個連結串列的頭指標你必須儲存一個,不然你就找不到這個指標了.
c. 注意判斷此時值到底取val還是0,判斷條件是什麼? 直接判斷這個節點是不是空節點,因為我們操作的就是此節點,不是next節點.
d. 注意可能最後一次加法時,進位為一,那麼則需要在給他最後再加上一個節點,例如999+1就可能得到4位,這樣就需要多加一個節點.

經驗

  1. 先想一下怎麼做,然後確定覺得這樣做可以之後,把詳細的思路寫一遍,想一想為什麼每一步需要這樣做?和實際的相符嗎?

  2. 不定長字串的加法,每位儲存一個值的演算法.