leetCode 2 Add Two Numbers

https://leetcode.windliang.cc/ 第一時間發佈

題目描述(中等難度)

就是兩個鏈表表示的數相加,這樣就能夠實現兩個很大的數相加了,無需考慮數值 int ,float 的限制了。java

因爲本身實現的很亂,直接按答案的講解了。函數

圖示

鏈表最左邊表示個位數,表明 342 + 465 =807 。spa

思路

首先每一位相加確定會產生進位,咱們用 carry 表示。進位最大會是 1 ,由於最大的狀況是無非是 9 + 9 + 1 = 19 ,也就是兩個最大的數相加,再加進位,這樣最大是 19 ,不會產生進位 2 。下邊是僞代碼。3d

  • 初始化一個節點的頭,dummy head ,可是這個頭不存儲數字。而且將 curr 指向它。
  • 初始化進位 carry 爲 0 。
  • 初始化 p 和 q 分別爲給定的兩個鏈表 l1 和 l2 的頭,也就是個位。
  • 循環,直到 l1 和 l2 所有到達 null 。指針

    • 設置 x 爲 p 節點的值,若是 p 已經到達了 null,設置 x 爲 0 。
    • 設置 y 爲 q 節點的值,若是 q 已經到達了 null,設置 y 爲 0 。
    • 設置 sum = x + y + carry 。
    • 更新 carry = sum / 10 。
    • 建立一個值爲 sum mod 10 的節點,並將 curr 的 next 指向它,同時 curr 指向變爲當前的新節點。
    • 向前移動 p 和 q 。
  • 判斷 carry 是否等於 1 ,若是等於 1 ,在鏈表末尾增長一個爲 1 的節點。
  • 返回 dummy head 的 next ,也就是個位數開始的地方。

初始化的節點 dummy head 沒有存儲值,最後返回 dummy head 的 next 。這樣的好處是不用單獨對 head 進行判斷改變值。也就是若是一開始的 head 就是表明個位數,那麼開始初始化的時候並不知道它的值是多少,因此還須要在進入循環前單獨對它進行值的更正,不能像如今同樣只用一個循環簡潔。code

代碼

class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}
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;
}

時間複雜度:O(max(m,n)),m 和 n 表明 l1 和 l2 的長度。blog

空間複雜度:O(max(m,n)),m 和 n 表明 l1 和 l2 的長度。而其實新的 List 最大長度是 O(max(m,n))+ 1,由於咱們的 head 沒有存儲值。遞歸

擴展

若是鏈表存儲的順序反過來怎麼辦?leetcode

我首先想到的是鏈表先逆序計算,而後將結果再逆序唄,這就轉換到咱們以前的狀況了。不知道還有沒有其餘的解法。下邊分析下單鏈表逆序的思路。get

迭代思想

首先看一下原鏈表。

總共須要添加兩個指針,pre 和 next。

初始化 pre 指向 NULL 。

而後就是迭代的步驟,總共四步,順序一步都不能錯。

  • next 指向 head 的 next ,防止原鏈表丟失

  • head 的 next 從原來鏈表脫離,指向 pre 。

  • pre 指向 head

  • head 指向 next

一次迭代就完成了,若是再進行一次迭代就變成下邊的樣子。

能夠看到整個過程無非是把舊鏈表的 head 取下來,添加的新的鏈表上。代碼怎麼寫呢?

next = head -> next; //保存 head 的 next , 以防取下 head 後丟失
head -> next = pre; //將 head 從原鏈表取下來,添加到新鏈表上
pre = head;// pre 右移
head = next; // head 右移

接下來就是中止條件了,咱們再進行一次循環。

能夠發現當 head 或者 next 指向 null 的時候,咱們就能夠中止了。此時將 pre 返回,即是逆序了的鏈表了。

迭代代碼

public ListNode reverseList(ListNode head){
        if(head==null) return null;
        ListNode pre=null;
        ListNode next;
        while(head!=null){
            next=head.next;
            head.next=pre;
            pre=head;
            head=next;
        }
        return pre;
    }

遞歸思想

  • 首先假設咱們實現了將單鏈表逆序的函數,ListNode reverseListRecursion(ListNode head) ,傳入鏈表頭,返回逆序後的鏈表頭。
  • 接着咱們肯定如何把問題一步一步的化小,咱們能夠這樣想。

    把 head 結點拿出來,剩下的部分咱們調用函數 reverseListRecursion ,這樣剩下的部分就逆序了,接着咱們把 head 結點放到新鏈表的尾部就能夠了。這就是整個遞歸的思想了。

    • head 結點拿出來

    • 剩餘部分調用逆序函數 reverseListRecursion ,並獲得了 newhead

    • 將 2 指向 1 ,1 指向 null,將 newhead 返回便可。

  • 找到遞歸出口

    固然就是若是結點的個數是一個,那麼逆序的話仍是它自己,直接 return 就夠了。怎麼判斷結點個數是否是一個呢?它的 next 等於 null 就說明是一個了。但若是傳進來的自己就是 null,那麼直接找它的 next 會報錯,因此先判斷傳進來的是否是 null ,若是是,也是直接返回就能夠了。

代碼

public ListNode reverseListRecursion(ListNode head){ 
        ListNode newHead;
        if(head==null||head.next==null ){
            return head;
        }
        newHead=reverseListRecursion(head.next); //head.next 做爲剩餘部分的頭指針
        head.next.next=head; //head.next 表明新鏈表的尾,將它的 next 置爲 head,就是將 head 加到最後了。
        head.next=null;
        return newHead;
    }
相關文章
相關標籤/搜索