目录
必备知识:单链表的增删查改
【数据结构初阶】-3-链表 | 链表是什么?| 【单链表的增删查改|思路+源码】
概览及题目链接
- 一、反转链表(头插)leetcode题目链接
- 二、移除单链表的元素(尾插)leetcode题目链接
-
三、合并两个有序链表(尾插)leetcode题目链接
-
四、链表分割(尾插)牛客网题目链接
题一:反转链表(头插)
思路一:依次反转
源码
注意:不能对空指针解引用!
易错:
struct ListNode* n3 = head->next; //如果head不能NULL(所以要提前处理head为NULL的情况)
if (n3)
{
n3 = n3->next; //最后一次循环中n3已经为NULL不能对其解引用
//(所以对n3是否为空要进行判断!)
}
struct ListNode* reverseList2(struct ListNode* head)
{
if (head == NULL)
{
return NULL;
}
struct ListNode* n2 = head, * n1 = NULL, * n3 = head->next;
while (n2)
{
n2->next = n1;
n1 = n2;
n2 = n3;
if (n3)
{
n3 = n3->next;
}
}
return n1;
}
思路二:依次头插
源码
struct ListNode* reverseList1(struct ListNode* head)
{
if (head == NULL)
{
return NULL;
}
struct ListNode* cur = head;
struct ListNode* next = head->next;
struct ListNode* newhead = NULL;
while (cur)
{
cur->next = newhead;
newhead = cur;
cur = next;
if (next)
next = next->next;
}
return newhead;
}
(本质上以上两个代码的实现没有很大的区别,思路一更像是思路二的具体化)
题二:移除单链表的元素(尾插)
思路:非val的尾插
- 首先,如上。移除链表元素后的链表的头节点可能被更改,因此我们要先解决头节点的问题。
我们根据head寻找链表中第一个有效的头节点,它是第一个尾插的对象(虽然它实际上它并没有插入谁),此时,我们需要考虑,移除链表元素后的newhead是否为空? - 移除链表元素后的head是否为空?
- 链表本身为空链表(如上,示例2)
- 因为非val的将被尾插,然而从第一个结点到最后一个结点我们未找到第一个(此时也是唯一一个)非val的结点(如上,示例3)
综上,尾插的时候有一次必要的判断(详情见完整源码)
if (!newhead)
{
newhead = cur;
}
注意:但凡对指针进行了解引用的都有必要判断这个指针是否为空,不能对空指针解引用!
if (next)
next = next->next;
if (tail)
tail->next = NULL;
源码(无哨兵位头节点)
//无哨兵位
struct ListNode* removeElements1(struct ListNode* head, int val)
{
if (!head)
return NULL;
struct ListNode* cur = head, * next = head->next;
struct ListNode* newhead = NULL, * tail = NULL;
//尾插
while (cur)
{
if (cur->val == val)
{
free(cur);
cur = next;
if (next)
next = next->next;
}
else
{
if (!newhead)
{
newhead = cur;
}
else
{
tail->next = cur;
}
tail = cur;
cur = next;
if (next)
next = next->next;
}
}
if (tail)
tail->next = NULL;
if (newhead)
return newhead;
return NULL;
}
【哨兵位头结点】
哨兵位头节点:不存储有效数据的头节点(malloc申请一块空间作为形式上的头节点)👉方便尾插,尾插完后记录下真正的头节点(哨兵位头结点的下一个),再 free 掉申请的空间
- 带头的链表(以下均指代 带哨兵位头结点)还需要传二级指针吗?(关于传二级指针——详见单链表的增删查改【数据结构初阶】-3-链表 | 链表是什么?| 【单链表的增删查改|思路+源码】)👉不需要,通过
guardhead->next
就可以对实际上的头结点进行修改 - 注意:一般的单链表都不带头,带头会说明
源码(含哨兵位头节点)
struct ListNode* removeElements2(struct ListNode* head, int val)
{
struct ListNode* cur = head;
struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));//哨兵位头节点
if (!guard)
{
perror("removeElements()");
return NULL;
}
struct ListNode* tail = guard;
//尾插
while (cur)
{
if (cur->val != val)
{
tail->next = cur;
tail = tail->next;
cur = cur->next;
}
else
{
struct ListNode* next = cur->next;
free(cur);
cur = next;
}
}
tail->next = NULL;//tail只在新链表为空的情况下才可能为空
//新链表有哨兵位头节点之后不可能为空,所以这里不需要判空
struct ListNode* newhead = guard->next;
free(guard);
return newhead;
}
题三:合并两个有序链表(尾插)
思路:双指针,取小者进行尾插
- 首先如果其中一个为空,则返回另一个(如果两个都为空,返回其中一个也还是为空)
- 申请一块空间作为头结点
- 双指针比较,取小者进行尾插,当有一个为空时停止比较(所以循环的条件是两个指针都不为空)
- 其中有一个链表走到空指针停下,但是还有另一个链表剩下的结点需要继续尾插 ,剩下的直接接上
- 记录哨兵位头结点的下一个
- 释放哨兵位头结点
- 返回记录的地址
源码
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
if(!list1)
return list2;
if(!list2)
return list1;
struct ListNode* guard =(struct ListNode*)malloc(sizeof(struct ListNode));
guard->next=NULL;
struct ListNode* tail=guard;
struct ListNode* cur1=list1,*cur2=list2;
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;
}
题四:链表分割(尾插)
题目描述:现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。
思路:<x的尾插到第一个链表;≥x的尾插到第二个链表;两个链表连接起来
struct ListNode* partition(struct ListNode* pHead, int x)
{
//创建两个头结点
struct ListNode* lesshead = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* greaterhead = (struct ListNode*)malloc(sizeof(struct ListNode));
if ((!greaterhead) || (!lesshead))
{
perror("partition");
return NULL;
}
lesshead->next = NULL;
greaterhead->next = NULL;
struct ListNode* lesstail = lesshead, * greatertail = greaterhead;
struct ListNode* cur = pHead;
//尾插
while (cur)
{
if (cur->val < x)
{
lesstail->next = cur;
lesstail = cur;
cur = cur->next;
}
else {
greatertail->next = cur;
greatertail = cur;
cur = cur->next;
}
}
lesstail->next = greaterhead->next;
greatertail->next = NULL;
struct ListNode* head = lesshead->next;
//释放
free(lesshead);
free(greaterhead);
return head;
}
—————————————————————————————————————@fantasy_13_7——————END——————