1.移除链表元素
题目
题解
(1)可以在单链表元素之前设置一个哨兵头,这样的话删除链表的头结点跟其他结点的方法一致。
(2)先找到要删除结点的前一个结点,然后前一个结点的next更新为要删除结点的next,释放要被删除的结点。
(3)来看看如何设置一个虚拟头(哨兵头)。依然还是在这个链表中,移除元素1。
(4)图解
实现注意点
(1)不过oj题中基本不带头,题目也没有说明链表带头所以我们需要自己创建一个哨兵头。
struct ListNode \* guard=(struct ListNode\*)malloc(sizeof(struct ListNode));
需要注意的是,最后哨兵头需要被释放,头结点更新,头结点指向哨兵结点的下一个。
head=guard->next;
free(guard);
代码
struct ListNode\* removeElements(struct ListNode\* head, int val){
struct ListNode\* cur=head;
//创造哨兵节点
struct ListNode\* guard=(struct ListNode\*)malloc(sizeof(struct ListNode));
struct ListNode\* tail=guard;
while(cur)
{
if(cur->val!=val)
{
//后一个节点更新
tail->next=cur;
tail=tail->next;
//前一个节点更新
cur=cur->next;
}
else
{
struct ListNode\* del=cur;
cur=cur->next;
free(del);
}
}
if(tail)
tail->next=NULL;
head=guard->next;
free(guard);
return head;
}
2.反转一个单链表
题目
题解(方法一)
(1)尾插变头插,取结点头插到新链表
(2)代码细节过程图解
方法一代码
struct ListNode\* cur=head;
struct ListNode\* newhead=NULL;
while(cur)
{
struct ListNode\* next=cur->next;
cur->next=newhead;
newhead=cur;
cur=next;
}
return newhead;
题解(方法二)
(1)翻转方向
(2)代码实现细节图解
方法二代码
struct ListNode\* reverseList(struct ListNode\* head){
struct ListNode\* n1,\*n2,\*n3;
//防止链表为空n3赋值放在循环里面
n1=NULL,n2=head,n3=NULL;
while(n2)
{
n3=n2->next;
n2->next=n1;
//迭代
n1=n2;
n2=n3;
}
return n1;
}
3.链表的中间结点
题目
题解
(1)通过快慢指针,可以只遍历一遍链表就能得出正确答案
(2)快指针一次走两步,慢指针一次走一步,快指针走完链表,慢指针刚好在一半处。
(3)图解
代码
struct ListNode\* middleNode(struct ListNode\* head){
//快慢指针
struct ListNode\* f=head,\*s=head;
//注意两个判断条件
while(f&&f->next)
{
s=s->next;
f=f->next->next;
}
return s;
}
4. 链表中倒数第k个结点
题目
[链表中倒数第k个结点_牛客题霸_牛客网 ](链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com))
题解
(1)方法还是快慢指针
(2)fast先走一步,fast跟slow再同时走,最后slow停在的地方就是倒数第一个。
类推,fast先走k步,fast跟slow再同时走,最后slow停在的地方就是倒数第几个。
(3)图解:
注意
(1)为什么fast先走的循环中使用k–,使用–k可以吗?
代码while(k–),k–的含义:每次先用k,然后再自减。每次循环k的变化(以上图为例):3,2,1。当k==0的时候不执行。
当while(–k)时,–k的含义:每次用k的时候先自减。k的变化:2,1。导致就执行两次。
(2)链表可能为空需要判断。
代码
struct ListNode\* FindKthToTail(struct ListNode\* pListHead, int k ) {
struct ListNode \*fast,\*slow;
fast=slow=pListHead;
//fast先走n步
while(k--)
{
//当链表为空的时候,返回空值
if(!fast)
{
return fast;
}
fast=fast->next;
}
//fast跟slow一直走到fast为空的时候
while(fast)
{
fast=fast->next;
slow=slow->next;
}
return slow;
}
5.合并两个有序链表
题目
题解
(1)写一个哨兵头,比大小,小的只管尾插,不需要考虑头插。
(2)图解
代码
struct ListNode\* mergeTwoLists(struct ListNode\* list1, struct ListNode\* list2){
struct ListNode\* guard =(struct ListNode\*)malloc(sizeof(struct ListNode));
struct ListNode\* cur1,\*cur2,\*newhead,\*cur3;
guard->next=NULL;
cur1=list1,cur2=list2,newhead=cur3=guard;
//小的尾插
while(cur1&&cur2)
{
if(cur1->val>=cur2->val)
{
cur3->next=cur2;
cur3=cur3->next;
cur2=cur2->next;
}
else
{
cur3->next=cur1;
cur3=cur3->next;
cur1=cur1->next;
}
}
//剩余的尾插
if(cur1)
{
while(cur1)
{
cur3->next=cur1;
cur3=cur3->next;
cur1=cur1->next;
}
}
else
{
while(cur2)
{
cur3->next=cur2;
cur3=cur3->next;
cur2=cur2->next;
}
}
//释放哨兵结点,更新newhead的位置
newhead=guard->next;
free(guard);
return newhead;
}
6.给定值x为基准将链表分割
编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结
点之前
题目
[链表分割](链表分割_牛客题霸_牛客网 (nowcoder.com))
题解
(1)创建两个哨兵结点,一个哨兵结点(暂且叫它lessGuard)后面插入的是小于x值的结点,另一个哨兵结点(叫它greaterGuard)后面插入的是大于x值的结点。
(2)过程图解:
注意
(1)greaterGuard指向的那条链表最后一个结点的next为什么要处理?
如果greaterGuard指向的那条链表的最后一个结点在原链表中不是最后一个结点的话,那么就会指向lessGuard指向的链表中的最后一个结点,如图所示:
两个链表合并的话就会出现下面这张图的情况,即形成一个环:
代码
ListNode\* partition(ListNode\* pHead, int x) {
// write code here
//lessGuard:小于x的结点的链表的哨兵头,lessTail:用于操作链表
//greaterGuard:大于x的结点的链表的哨兵头,greaterTail:用于操作链表
//tail用于遍历原链表
struct ListNode \*lessGuard,\*lessTail,\*greaterGuard,\*greaterTail,\*tail;
lessGuard=(struct ListNode\*)malloc(sizeof(struct ListNode));
greaterGuard=(struct ListNode\*)malloc(sizeof(struct ListNode));
lessTail=lessGuard;
greaterTail=greaterGuard;
tail=pHead;
//结点根据大小尾插到相应的链表后面
while(tail)
{
if(tail->val<x)
{
lessTail->next=tail;
tail=tail->next;
lessTail=lessTail->next;
}
else
{
greaterTail->next=tail;
tail=tail->next;
greaterTail=greaterTail->next;
}
}
//最后两个链表合并
lessTail->next=greaterGuard->next;
//greaterTail->next后面应该是NULL
//否则可能形成环
greaterTail->next=NULL;
pHead=lessGuard->next;
//释放掉两个哨兵结点
free(greaterGuard);
free(lessGuard);
return pHead;
}
7. 链表的回文结构
题目
[链表的回文结构](链表的回文结构_牛客题霸_牛客网 (nowcoder.com))
题解
(1)可以先用快慢指针,找出链表的中间点,然后将后半段链表反转,最后两个链表进行比较。
(2)这题可以看成是第2题与第3题的结合。
(3)图解
代码
bool chkPalindrome(ListNode\* A) {
// write code here
struct ListNode\* f,\*s;
f=s=A;
while(f&&f->next)
{
f=f->next->next;
s=s->next;
}
struct ListNode \*cur=s, \*n = NULL;
while(cur)
{
struct ListNode\* next=cur->next;
cur->next=n;
n=cur;
cur=next;
}
struct ListNode \*cur1=A;
while(n&&cur1)
{
if(n->val!=cur1->val)
{
return false;
}
n=n->next;
cur1=cur1->next;
}
return true;
}
8.相交链表
输入两个链表,找出它们的第一个公共结点。
题目
题解
(1)a.先可以遍历一遍,如果两个链表尾结点的地址相同就说明两个链表相交。反之,则不想交。
b.通过a步骤的遍历,分别求出两个链表的长度,然后 长度长的链表先走差距步(两个链表的长度差)。走完之后,同时走,第一个地址相等的结点就是相交结点。
(2)图解:
代码
struct ListNode \*getIntersectionNode(struct ListNode \*headA, struct ListNode \*headB) {
//1.分别遍历
struct ListNode\* curA,\*curB;
![img](https://img-blog.csdnimg.cn/img_convert/b226d0d8d8d7e8baeb8449ade1b16d70.png)
![img](https://img-blog.csdnimg.cn/img_convert/f315fc5643ea84691de36e37dde16c4f.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
说明两个链表相交。反之,则不想交。
b.通过a步骤的遍历,分别求出两个链表的长度,然后 长度长的链表先走差距步(两个链表的长度差)。走完之后,同时走,第一个地址相等的结点就是相交结点。
(2)图解:
![image-20221008095020868](https://img-blog.csdnimg.cn/img_convert/59a60ae42c344024424af3874ce4db6d.png)
>
> 代码
>
>
>
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
//1.分别遍历
struct ListNode* curA,*curB;
[外链图片转存中…(img-NcbUvIy7-1714673512455)]
[外链图片转存中…(img-d5Ud1N17-1714673512455)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!