1. 程式人生 > >Add Two Numbers LeetCode第二題

Add Two Numbers LeetCode第二題

  • 題目描述:
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,帶上進位直接迴圈相加就行了

但是在處理的過程中,要注意以下問題:

  1. 兩個連結串列的長度相同,並且相加所得的和的長度仍然不變,這是最簡單的情形,記之為:ABC+DEF=GHI(注,這裡的加數也是逆序的,與題目中描述的連結串列的形式是一致的,即ABC對應的真實的資料位CBA,下面沒有特別說明的,都是按照這種形式),這種情況下,相加結束之後,即得到了最終的結果
  2. 兩個連結串列的長度相同,但是相加所得的和的長度比原來的數的長度要長,這種情形較之剛才的稍微複雜一點,但還也不是特別複雜,記之為:ABC+DEF=GHIJ,這種情況下,相加結束之後,最終會剩餘一個進位,這就需要在返回結果之前,還需要將進位新增到結果連結串列的尾部
  3. 兩個連結串列的長度不同,這種情形下,又可以分出一下幾種小情況:
  • 較短部分相加之後,沒有向上的進位,將兩個加數記為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;
至於為何要新增這三句,連結串列有帶頭結點的連結串列和不帶頭結點的連結串列,而此處我是將其看做不帶頭結點的連結串列來處理的。

有更近一步優化的,還望進一步交流~~大笑大笑大笑