Add Two Numbers LeetCode第二題
阿新 • • 發佈:2019-01-24
- 題目描述:
You are given two linked lists representing two non-negative numbers.
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.
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
起初沒看懂啥意思,後來百度了一下,又回過頭看了一下題目描述,才知道題目是讓幹啥的了,也就是我上面標紅的部分,其實這個題目是簡化了的“大數相加”,從以下兩個方面來降低了題目的難度:
- 連結串列,每一個node中儲存的是一位,可以直接使用
- 給的連結串列是所要求和的書的倒序(reverse),即:2->4->3實際上所表示的是342,帶上進位直接迴圈相加就行了
但是在處理的過程中,要注意以下問題:
- 兩個連結串列的長度相同,並且相加所得的和的長度仍然不變,這是最簡單的情形,記之為:ABC+DEF=GHI(注,這裡的加數也是逆序的,與題目中描述的連結串列的形式是一致的,即ABC對應的真實的資料位CBA,下面沒有特別說明的,都是按照這種形式),這種情況下,相加結束之後,即得到了最終的結果
- 兩個連結串列的長度相同,但是相加所得的和的長度比原來的數的長度要長,這種情形較之剛才的稍微複雜一點,但還也不是特別複雜,記之為:ABC+DEF=GHIJ,這種情況下,相加結束之後,最終會剩餘一個進位,這就需要在返回結果之前,還需要將進位新增到結果連結串列的尾部
- 兩個連結串列的長度不同,這種情形下,又可以分出一下幾種小情況:
- 較短部分相加之後,沒有向上的進位,將兩個加數記為ABC和DEFG,即ABC和DEF相加完之後,沒有往上的進位,此時只需要將剩餘的F位新增到結果連結串列的末尾即可
- 較短部分相加之後,有向上的進位,但是較長加數的剩餘部分的第一位和進位相加後,沒有再向上的後續進位,若將兩個加數分別記為:AB和CDEF,即AB和CD向加之後,產生了一個向上的進位carry,此時CDEF中的E位要和carry相加,但是E和carry向加之後,沒有進一步的進位,這種情況下,直接將E和carry相加的值重新賦值給E位,並將後續的位直接連線到結果連結串列中末尾就行了
- 較短部分相加之後,有向上的進位,並且較長加數的剩餘部分的第一位和進位相加後,又產生了後續的進位,這種進位關係,直到後邊的某一位才結束,若將兩個加數分別記為:AB和CDEFGHI,即AD和CD相加之後,產生了一個向上的進位carry,此時CDEFGHI中E為和carry相加之後又產生了新的進位,這種進位關係直到後邊的某一位才結束,這種情況下,除了要將EFGHI新增到結果連結串列外,還行要按序執行和carry的相加,直到某位加完之後,不再產生進位
直接上程式碼,先上一個C++的
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* pResultList = new ListNode(-1);
pResultList->next = NULL;
ListNode* p1 = l1;
ListNode* p2 = l2;
ListNode* pResultTailNode, *pResultNode;
int carry = 0, number = 0; //記錄進位資訊和結果(指mod 10 之後)
//初始化resultList
pResultTailNode = pResultList;
pResultNode = NULL;
while(NULL != p1 && NULL != p2)
{
number = (p1->val + p2->val + carry) % 10;
carry = (p1->val + p2->val + carry) / 10;
pResultTailNode->next = new ListNode(number);
pResultTailNode = pResultTailNode->next;
pResultTailNode->next = NULL;
p1 = p1->next;
p2 = p2->next;
}
if (NULL != p1)
{
pResultTailNode->next = p1;
while(NULL != p1)
{
pResultTailNode = pResultTailNode->next;
number = (p1->val + carry) % 10;
carry = (p1->val + carry) / 10;
p1->val = number;
p1 = p1->next;
}
}
if (NULL != p2)
{
pResultTailNode->next = p2;
while(NULL != p2)
{
pResultTailNode = pResultTailNode->next;
number = (p2->val + carry) % 10;
carry = (p2->val + carry) / 10;
p2->val = number;
p2 = p2->next;
}
}
if (0 != carry)
{
pResultTailNode->next = new ListNode(carry);
pResultTailNode = pResultTailNode->next;
pResultTailNode->next = NULL;
}
pResultTailNode = pResultList->next;
delete pResultList;
pResultList = pResultTailNode;
return pResultList;
}
};
該程式碼中,在相加完公共部分後,處理剩餘部分的時候(即在if條件語句總的while判斷條件處),只是在p1(或者p2)不為NULL為結束條件,不論carry是否為0,一直到結束,這會耗費時間,提交上之後,統計結果提示用了44ms
剛剛又修改了一下,只是在第41和第54行處的while迴圈條件上加了對carry的判斷,因為當carry為0時,就不需要再往後遍歷執行了,
if (NULL != p1)
{
pResultTailNode->next = p1;
<span style="background-color: rgb(255, 0, 0);">while(NULL != p1 && 0 != carry)</span>
{
pResultTailNode = pResultTailNode->next;
number = (p1->val + carry) % 10;
carry = (p1->val + carry) / 10;
p1->val = number;
p1 = p1->next;
}
}
if (NULL != p2)
{
pResultTailNode->next = p2;
<span style="background-color: rgb(255, 0, 0);">while(NULL != p2 && 0 != carry)</span>
{
pResultTailNode = pResultTailNode->next;
number = (p2->val + carry) % 10;
carry = (p2->val + carry) / 10;
p2->val = number;
p2 = p2->next;
}
}
這時,又看了下Details,發現執行時間減少了8ms,但是距離C++程式碼中的最優程式碼,還有一點點差距,後來又寫了一個C版本的,與C++版本的相比,執行時間要減少了一半,直接貼程式碼
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) {
struct ListNode* pResultList = (struct ListNode*)malloc(sizeof(struct ListNode));
pResultList->next = NULL;
struct ListNode* p1 = l1;
struct ListNode* p2 = l2;
struct ListNode* pResultTailNode, *pResultNode;
int carry = 0, number = 0; //記錄進位資訊和結果(指mod 10 之後)
//初始化resultList
pResultTailNode = pResultList;
pResultNode = NULL;
while(NULL != p1 && NULL != p2)
{
number = (p1->val + p2->val + carry) % 10;
carry = (p1->val + p2->val + carry) / 10;
pResultTailNode->next = (struct ListNode *)malloc(sizeof(struct ListNode));
pResultTailNode = pResultTailNode->next;
pResultTailNode->val = number;
pResultTailNode->next = NULL;
p1 = p1->next;
p2 = p2->next;
}
if (NULL != p1)
{
pResultTailNode->next = p1;
while(NULL != p1)
{
pResultTailNode = pResultTailNode->next;
number = (p1->val + carry) % 10;
carry = (p1->val + carry) / 10;
p1->val = number;
p1 = p1->next;
}
}
if (NULL != p2)
{
pResultTailNode->next = p2;
while(NULL != p2)
{
pResultTailNode = pResultTailNode->next;
number = (p2->val + carry) % 10;
carry = (p2->val + carry) / 10;
p2->val = number;
p2 = p2->next;
}
}
if (0 != carry)
{
pResultTailNode->next = (struct ListNode *)malloc(sizeof(struct ListNode));
pResultTailNode = pResultTailNode->next;
pResultTailNode->val = carry;
pResultTailNode->next = NULL;
}
pResultTailNode = pResultList->next;
free(pResultList);
pResultList = pResultTailNode;
return pResultList;
}
與C++版本的類似,在while迴圈中,如果加上對carry的判斷,執行時間會減少2ms,
if (NULL != p1)
{
pResultTailNode->next = p1;
while(NULL != p1 && 0 != carry)
{
pResultTailNode = pResultTailNode->next;
number = (p1->val + carry) % 10;
carry = (p1->val + carry) / 10;
p1->val = number;
p1 = p1->next;
}
}
if (NULL != p2)
{
pResultTailNode->next = p2;
while(NULL != p2 && 0 != carry)
{
pResultTailNode = pResultTailNode->next;
number = (p2->val + carry) % 10;
carry = (p2->val + carry) / 10;
p2->val = number;
p2 = p2->next;
}
}
details
注:關於程式碼最後的三句:
pResultTailNode = pResultList->next;
delete pResultList;
pResultList = pResultTailNode;
至於為何要新增這三句,連結串列有帶頭結點的連結串列和不帶頭結點的連結串列,而此處我是將其看做不帶頭結點的連結串列來處理的。
有更近一步優化的,還望進一步交流~~