之前虽然对链表有进行学习,但是没有怎么使用过,导致对链表有很大程度的遗忘以及应用过程中遇到了很多问题。
题目:合并两个有序链表
将两个有序链表为一个新的有序链表并返回,新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入: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剩下链表的头指针。单向链表只能通过上一个节点找到下一个节点。
仓促成文,不当之处,尚祈方家和读者批评指正。联系邮箱1772348223@qq.com