携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第24天,点击查看活动详情
前言
数据结构与算法刷题专栏第三篇,本文是基于C语言实现的。
本文就来分享一波作者对单链表的一些习题的学习心得与见解。
笔者水平有限,难免存在纰漏,欢迎指正交流。
Leetcode21——合并两个有序链表
题目描述
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
链接:Leetcode21
示例 1:
输入: l1 = [1,2,4], l2 = [1,3,4] 输出: [1,1,2,3,4,4]
示例 2:
输入: l1 = [ ], l2 = [ ] 输出: [ ]
示例 3:
输入: l1 = [ ], l2 = [0] 输出: [0]
提示:
- 两个链表的节点数目范围是 [0, 50]
- -100 <= Node.val <= 100
- l1 和 l2 均按 非递减顺序 排列
核心代码模式
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
}
思路分析和代码实现(C语言)
思路其实很简单,这里用了哨兵结点作为新链表的头结点,用两个指针把两个链表中的结点互相比较,小的先尾插到新链表中,总会有一条链表先插完,没插完的链表把剩下的结点一次尾插到新链表。
有一些要注意的点,首先创建了哨兵结点,定义了guard指针指向哨兵结点,这时应该立即给哨兵结点的next指针初始化为NULL,不然有野指针的潜在风险。定义两个指针cur1和cur2分别对应list1链表和list2链表,while循环的条件应当是cur1&&cur2
,为什么呢?这样的话只要有一个链表先插完就立马跳出循环。在循环结束后要根据哪个链表还没插完,把剩余结点全部尾插到新链表。我们这里使用的辅助结点——哨兵结点是题目没提供的,我们应当在最后将其释放,不过释放前得先把哨兵结点的next指针的内容取出来,作为头指针返回。
代码实现
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
struct ListNode* cur1 = list1;
struct ListNode* cur2 = list2;
struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));
guard->next = NULL;
struct ListNode* tail = guard;
while(cur1 && cur2)
{
if(cur1->val > cur2->val)
{
tail->next = cur2;
tail = tail->next;
cur2 = cur2->next;
}
else
{
tail->next = cur1;
tail = tail->next;
cur1 = cur1->next;
}
}
if(cur1)
tail->next = cur1;
if(cur2)
tail->next = cur2;
struct ListNode* head = guard->next;
free(guard);
return head;
}
Leetcode206——反转链表
题目描述
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
链接:Leetcode206
示例
示例 1:
输入: head = [1,2,3,4,5] 输出: [5,4,3,2,1]
示例 2:
输入: head = [1,2] 输出: [2,1]
示例 3:
输入: head = [] 输出: []
提示:
- 链表中节点的数目范围是 [0, 5000]
- -5000 <= Node.val <= 5000
核心代码模式
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head)
{
}
思路分析与代码实现(C语言)
1.新链表头插法
实际上有一道题和这个基本一样,只是要求的是使用数组,而这里使用的是单链表。思路也是可以借鉴互通的,不妨创建一个新的链表,不过相比于数组,这里不需要开辟新结点,只是把结点间的指向关系进行修改。定义一个新的头指针newHead,定义一个查找当前结点的cur指针,查找下一结点的next指针,只要cur不指向NULL,就把cur指向的结点放到新链表去,怎么放呢?把newHead的内容给cur指向结点的next指针,让它指向newHead原来指向的东西,再把cur指向结点的地址给newHead,让newHead指向该结点,不过要注意在执行这些操作之前要先把cur->next的值放到next去,在执行完后要把next的值再放到cur去。其实也就是让原来的结点从前向后一个一个头插到新链表中。
代码实现
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* cur = head;
struct ListNode* newHead = NULL;
while(cur)
{
struct ListNode* next = cur->next;
cur->next = newHead;
newHead = cur;
cur = next;
}
return newHead;
}
2.三指针逆置法
有没有想过这样一种方法,既然链表是通过指针联系的,那把指针指向关系逆转了不就能实现反转了吗,用三个指针,分别指向前一结点、当前结点和下一结点(prev、cur、next),只要cur指向不为NULL,就说还有结点没有反转,就把前一结点的地址赋给cur指向的当前结点的next指针,让它指向前一结点,然后三个指针都向后移动一位,不断循环至cur指向NULL。反转后的新链表的头结点由prev指向,返回prev即可。
代码实现
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* prev = NULL;
struct ListNode* cur = head;
struct ListNode* next = NULL;
while(cur)
{
next = cur->next;
cur->next = prev;
prev = cur;
cur = next;
}
return prev;
}
以上就是本文全部内容,感谢观看,你的支持就是对我最大的鼓励~