引言:单链表知识学完了,却在刷题时无从下手?本文将单链表的操作理论转化为解决力扣经典题目的实战能力,让你彻底掌握。




参考前面的知识: --查找指定节点需要进行循环遍历,用条件语句判断; --在同一个循环内,当条件语句找到节点后开始进行删除操作; --删除操作因为要将指定节点前后进行地址链接,就也要循环从头节点开始。
--那么,算法时间复杂度:O(N^2),虽然题目没有时间的要求,但还是来进一步修改。
(循环中,进行条件判断值不是 val 的节点,再尾插链接)
--那么,算法时间复杂度就很明显了:O(N)。

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val)
{
ListNode* newHead, *newTail;
newHead = newTail = NULL;
ListNode* pcur = head;//新指针,放置原链表首节点指向改变
while(pcur)
{
if(pcur->val!=val)
{
//刚开始拷贝到新链表,链表为空
if(newHead == NULL)
{
//首、尾相同
newHead = newTail = pcur;
}
else
{
newTail -> next = pcur;
newTail = newTail -> next;
}
}
//指向节点后移
pcur=pcur->next;
}
//跳出循环,手动将尾节点的next置空代表后面不链接节点
if(newTail)
{
newTail->next=NULL;
}
return newHead;
}--为什么参数不是二级指针接收首节点地址? :创建新链表,将节点拷贝一份(没有改变指向对象的具体内容 -->二级),只是创建了新的头指针指向原首节点(改变指向对象-->一级);

--为什么最后手动将新尾节点指针置空? :上面说了是将节点拷贝过来,那么新尾节点指向值为5的节点时,将节点next(存储下一个指针的地址)一并拷过来了,那么最后就是1 2 3 4 5 6。所以置空。
test.c具体VS2022调试代码:
#include<stdio.h>
#include<stdlib.h>
//链表结构--一个节点
struct ListNode
{
int val;//data
struct ListNode* next;
};
typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val)
{
//创建新链表
ListNode* newHead, * newTail;
newHead = newTail = NULL;
ListNode* pcur = head;
while (pcur)
{
//判断pcur节点的值是否为val
if (pcur->val != val)
{
//尾插
if (newHead == NULL)
{
//链表为空
newHead = newTail = pcur;
}
else
{
//链表非空
newTail->next = pcur;
newTail = newTail->next;
}
}
pcur = pcur->next;
}
//将新头节点指针返回
return newHead;
}
void test01()
{
//创建新链表
ListNode* node1 = (ListNode*)malloc(sizeof(ListNode));
ListNode* node2 = (ListNode*)malloc(sizeof(ListNode));
ListNode* node3 = (ListNode*)malloc(sizeof(ListNode));
ListNode* node4 = (ListNode*)malloc(sizeof(ListNode));
ListNode* node5 = (ListNode*)malloc(sizeof(ListNode));
ListNode* node6 = (ListNode*)malloc(sizeof(ListNode));
ListNode* node7 = (ListNode*)malloc(sizeof(ListNode));
node1->val = 1;
node2->val = 2;
node3->val = 6;
node4->val = 3;
node5->val = 4;
node6->val = 5;
node7->val = 6;
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = node5;
node5->next = node6;
node6->next = node7;
node7->next = NULL;
ListNode* plist = node1;
ListNode* newHead = removeElements(plist, 6);
}
int main()
{
test01();
return 0;
}



/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{
//创建三个指针
ListNode* n1, * n2, * n3;
//链表为空
if (head == NULL)
{
return head;
}
//链表不为空
//首先n1指向空
n1 = NULL;
n2 = head;
n3 = n2->next;
while (n2)//循环条件n2不为空(不超出链表)
{
n2->next = n1;
n1 = n2;
n2 = n3;
//当n2到达尾节点时,n3为空,不能对空指针解引用
if (n3)
{
n3 = n3->next;
}
}
return n1;
}回顾: 《算法面试“必杀技”:双指针法高效解决数组原地操作》-CSDN博客
结语:道阻且长,行则将至。本次练习是巩固的节点,更是下一段征程的起点。刷题之路,继续前行。