Leetcode之合併兩個有序鏈表

之前雖然對鏈表有進行學習,但是沒有怎麼使用過,導致對鏈表有很大程度的遺忘以及應用過程中遇到了很多問題。

 

題目:合併兩個有序鏈表

將兩個有序鏈表爲一個新的有序鏈表並返回,新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。

示例:

輸入:1 -> 2 -> 4 ,  1 -> 3 -> 4
輸出:1 -> 1 -> 2 -> 3 -> 4 -> 4

題目分析

有序鏈表:指的是輸入的兩個鏈表按從小到大的關係都排好了順序

新鏈表是通過拼接給定的兩個鏈表的所有節點組成的:不能定義新的鏈表的返回,需要使用原先的節點。

示例給的並不是太清楚,這裏給出一個更清晰的示例:

輸入:1 -> 3 -> 4 ,  2 -> 5 -> 7
輸出:1 -> 2 -> 3 -> 4 -> 5 -> 7

簡單的來說,這個題目就是要求我們將兩個排好序的鏈表,將他們的節點拆分,重新拼接成一個從小到大排列的鏈表。這裏我們有些便利我們需要利用上,輸入鏈表是有序的,所以我們不需要將鏈表遍歷然後重新排序,直接按序讀取進行比較即可。

這裏我們使用方法是

1.先判斷鏈表l1和l2是否有爲空的情況,若其中一條表爲空,則直接返回另一條鏈表。

2.在兩條鏈表皆不爲空的情況下,先創建一個頭結點(後面會釋放掉),並且創建一個鏈表指針,指向頭結點。

3.然後開始循環判斷兩條鏈表當前節點數據域內的數據的大小,那條鏈表上當前節點數據域數據小,就將那條鏈表接到新創             建的頭結點上。

4.重複第三步,直到有一條鏈表爲空爲止,然後再將另一條鏈表剩餘部分接到新的鏈表上。

5.將鏈表指針指向新鏈表的頭結點,然後釋放頭結點並返回新鏈表。

 

代碼

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
    
    if (l1 == NULL)    //鏈表1爲空,直接返回鏈表2
        return l2;

    if (l2 == NULL)    //同上,這裏用於檢測到一條鏈表爲空時快速返回結果,提高函數效率
        return l1;

    struct ListNode *head = (struct ListNode*)malloc(sizeof(struct ListNode));    //創建頭節點

    struct ListNode *temp = head;
    head->next = NULL;

    while((l1 != NULL) && (l2 != NULL))     //兩條鏈表皆不爲空,比較他們當前數據域內的數據大小,誰小就將誰接到新鏈表上
    {
        if (l1->val < l2->val)
        {
            temp->next = l1;                //將l1鏈表當前節點接到新鏈表上
            temp = temp->next;              //保存節點              
            l1 = l1->next;                  //l1指向後一個節點
            temp->next = NULL;              //將新鏈表和l1鏈表斷開連接
        }
        else
        {
            temp->next = l2;
            temp = temp->next;
            l2 = l2->next;
            temp->next = NULL;
        }
    }

    while((l1 != NULL))                      //當一條鏈表爲空之後,將另一條鏈表剩餘部分都接到新鏈表上
    {
        temp->next = l1;
        temp = temp->next;     
        l1 = l1->next;
        temp->next = NULL;
    }

    while((l2 != NULL))
    {
        temp->next = l2;
        temp = temp->next;     
        l2 = l2->next;
        temp->next = NULL;
    }

    temp = head->next;                       //將temp指向新鏈表頭,用於返回新鏈表

    free(head);                              //釋放之前申請的內存,否則不符合題目要求,創建了新的節點

    return temp;
   
}

 

部分程序圖例

在該段程序當中,相對難理解的就是在鏈表節點拼接部分,這裏將該部分程序用圖解來分析程序執行過程。

if (l1->val < l2->val)
{
    temp->next = l1;                //將l1鏈表當前節點接到新鏈表上
    temp = temp->next;              //保存節點              
    l1 = l1->next;                  //l1指向後一個節點
    temp->next = NULL;              //將新鏈表和l1鏈表斷開連接
}

假設:( l1 -> val  < l2 ->  val )

1.該段程序第一步 temp->next = l1; 通過temp的指針域將頭結點和鏈表l1連接。

2.第二步 temp = temp->next; 將temp指針後移一個節點,並且保存l1的第一個節點。此時temp 與 l1相等,上圖中的表達式有誤。

3.第三步 l1 = l1->next; 將l1指針後移一個節點。

4.將新鏈表和l1鏈表斷開。

 

這裏我當初遇到了兩個疑問:

爲什麼第一步不能使用 temp = l1;  ?

爲什麼第三和第四步不能互換

這裏如果有分析清楚上面的圖示應該已經清楚這兩個問題的原因了。

第一個問題,使用temp->next = l1; 是我們需要使用節點指針域來將節點和鏈表連接,假如我們使用temp = l1;則對temp的操作等同對l1進行操作,不能將l1和temp的操作分開。

第二個問題,第二步完成後,temp和l1指向同一個節點,若是我們不將l1指針向後移一個節點,直接將新鏈表和l1鏈表從當前節點斷開,會導致l1鏈表剩下節點永久丟失,因爲我們沒有保存l1剩下鏈表的頭指針。單向鏈表只能通過上一個節點找到下一個節點。

 

倉促成文,不當之處,尚祈方家和讀者批評指正。聯繫郵箱[email protected] 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章