

🔥@晨非辰Tong:个人主页
💪学习阶段:C语言、数据结构与算法初学者
⏳“人理解迭代,神理解递归。”
引言:单链表知识学完了,却在刷题时无从下手?本文将单链表的操作理论转化为解决力扣经典题目的实战能力,让你彻底掌握。
目录
2. 206. 反转链表 - 力扣(LeetCode)(三指针法:变换指向关系)
1. 203. 移除链表元素 - 力扣(LeetCode)



- 方法一:根据前面学单链表的知识,先查找val值的节点并将节点返回,再删除指定节点。至于算法的好坏,根据复杂度判断:(只大致思路)

参考前面的知识:
--查找指定节点需要进行循环遍历,用条件语句判断;
--在同一个循环内,当条件语句找到节点后开始进行删除操作;
--删除操作因为要将指定节点前后进行地址链接,就也要循环从头节点开始。
--那么,算法时间复杂度:O(N^2),虽然题目没有时间的要求,但还是来进一步修改。
- 方法二:那么就还有一个普遍的思路—>创建新链表,将值不是val的节点尾插链接到新链表。
(循环中,进行条件判断值不是 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;
}
2. 206. 反转链表 - 力扣(LeetCode)(三指针法:变换指向关系)
![]()

- 方法一:(容易想到,易懂)创建新链表,申请一个节点大小的空间,每次在头节点进行头插。(创建新链表,遍历原链表,将节点头插到新链表),显然时间复杂度:O(N)。

- 方法二:创建三个指针,改变之间的指向关系。可见时间复杂度:O(N)。

/**
* 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;
}
回顾:
结语:道阻且长,行则将至。本次练习是巩固的节点,更是下一段征程的起点。刷题之路,继续前行。

1018

被折叠的 条评论
为什么被折叠?



