之前雖然對鏈表有進行學習,但是沒有怎麼使用過,導致對鏈表有很大程度的遺忘以及應用過程中遇到了很多問題。
題目:合併兩個有序鏈表
將兩個有序鏈表爲一個新的有序鏈表並返回,新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。
示例:
輸入: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]