LeetCode第2題思悟——兩數相加(Add Two Numbers)
第二題
1. 題目要求
You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself. (給定兩個非空連結串列來表示兩個非負整數。位數按照逆序方式儲存,它們的每個節點只儲存單個數字。將兩數相加返回一個新的連結串列。
你可以假設除了數字 0 之外,這兩個數字都不會以零開頭。)
2. 示例
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) Output: 7 -> 0 -> 8 Explanation: 342 + 465 = 807.
3. 我的思路
首先是逆序連結串列表示一個整數,也就是說連結串列中前面的數字在正常計算中要先計算;結果也是逆序表示的連結串列,所以正常計算中先計算的結果要先出現在結果連結串列中;這是從問題到解法的一個基本轉化;從該轉化中我們得到了我們需要的資料結構:先訪問的連結串列節點先計算——先進先出的佇列~先計算得到的結果先出現在結果連結串列中——先進先出的佇列; 之後就是計算上要考慮的幾種情況:位數長度不一致、進位問題。關於位數長度不一致,我的處理方法是分兩部分計算:一部分是兩個運算元相加,另一部分是一個運算元相加;進位問題不是很難處理,只需要一箇中間變數記錄即可;一個可能忽略的問題就是當兩個數位數一樣又產生了進位時,需要往結果中新增一個1。關於這題,我的思悟是第一遍編譯沒有通過是因為我在第二部分的處理中,忘記了poll(),結果死迴圈了;第二遍提交後結果錯,因為剛開始我使用的是棧!不是佇列!這是因為我沒有真正理清這裡的操作關係,然後平時使用棧比較多,有一點思維定式!真正靜下來思考後,得到了上面的思路; 程式碼如下(49 ms):
public ListNode addTwoNumbers(ListNode l1, ListNode l2) { ArrayDeque<Integer> firstQueue=new ArrayDeque<>(); ArrayDeque<Integer> secondQueue=new ArrayDeque<>(); ArrayList<Integer> resultList=new ArrayList<>(); ArrayDeque<Integer> nextStack; ListNode currentNode=l1; while(currentNode!=null){ firstQueue.add(currentNode.val); currentNode=currentNode.next; } currentNode=l2; while(currentNode!=null){ secondQueue.add(currentNode.val); currentNode=currentNode.next; } int carryBit=0; int currentResult; Integer firstNum,secondNum; while((firstNum=firstQueue.peek())!=null&&(secondNum=secondQueue.peek())!=null){ currentResult=firstNum+secondNum+carryBit; if(currentResult>=10){ currentResult-=10; carryBit=1; }else{ carryBit=0; } resultList.add(currentResult); firstQueue.poll(); secondQueue.poll(); } if(firstQueue.size()==0){ nextStack=secondQueue; }else{ nextStack=firstQueue; } while((firstNum=nextStack.peek())!=null){ currentResult=firstNum+carryBit; if(currentResult>=10){ currentResult-=10; carryBit=1; }else{ carryBit=0; } nextStack.poll(); resultList.add(currentResult); } if(carryBit==1){ resultList.add(1); } ListNode resultHead=new ListNode(-1); ListNode currentTail=resultHead; int length=resultList.size(); for(int i=0;i<length;i++){ currentTail.next=new ListNode(resultList.get(i)); currentTail=currentTail.next; } return resultHead.next; }
4. 大神們解法
//35ms
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode result=null;
ListNode index=null;
int tmpTotal=0;
boolean isFirst=true;
boolean isAdd1=false;
int l1val=0,l2val=0;
while(l1!=null||l2!=null||isAdd1) {
if(l1!=null) {
l1val=l1.val;
l1=l1.next;
}
if(l2!=null) {
l2val=l2.val;
l2=l2.next;
}
tmpTotal = (l1val+l2val);
if(isAdd1) {
tmpTotal++;
isAdd1 = false;
}
if(tmpTotal>=10) {
tmpTotal-=10;
isAdd1=true;
}
ListNode tmp = new ListNode(tmpTotal);
l1val=0;l2val=0;tmpTotal=0;
if(isFirst) {
result = tmp;
index=result;
isFirst=false;
continue;
}
index.next = tmp;
index=tmp;
}
return result;
}
//32ms
public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
//==========預處理,保證計算不出nullpointer錯
if(l1 == null){
l1 = new ListNode(0);
}
if(l2 == null){
l2 = new ListNode(0);
}
//========================================
if(l1.next==null && l2.next==null){//最小情況
int val = l1.val+l2.val;
if(val>9){
ListNode node = new ListNode(val%10);//設定第二位
node.next = new ListNode(val/10);//設定第一位
return node;
}else
return new ListNode(val);
}else {//可繼續迭代的情況
int val = l1.val+l2.val;
if(val>9){
val = val-10;
if(l1.next!=null) l1.next.val++;
else if(l2.next!=null) l2.next.val++;
//else //不存在的,必為上述兩種情況之一。
}
ListNode node = new ListNode(val);
//開始迭代
node.next = addTwoNumbers(l1.next,l2.next);
return node;
}
}
32ms和35ms的解法思路有一點不同,一個使用while迴圈,一個使用遞迴;但是他們的共同點是在對連結串列的訪問過程中建立了結果連結串列;而我是先將連結串列存入了佇列中,然後將計算得到的所有結果資料存在了連結串列裡,之後再遍歷一遍連結串列生成結果連結串列;這樣就比大神們的解法多了對連結串列的插入和遍歷訪問的時間;這是因為我“吸取”上一題教訓,活用資料結構,結果為了“資料結構”而“資料結構”了,現在看來很多此一舉,明明可以訪問的時候就處理,非要把它放到佇列裡然後再處理,於是就差了10ms。(雖然測試有可能不太準確,但是蠢就是蠢,是思路上的差距~沒什麼好說的)
其實,還有一個收穫!明明題目中限定了測試用例非空,但是大神們的程式碼裡還是有對此的檢測,我想,這就是良好的程式設計習慣吧!