【leetcode刷题】链表篇

在这里插入图片描述

移除链表元素

移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

指针法

思路:创建指针cur遍历链表, 当cur->next指向的val为val时,用prev指针保存cur->next,然后

cur->next=cur->next->next,在把prev指向的空间free掉

那么会有两种方法:一种是不设哨兵位,另一种是设了哨兵位

不设哨兵位:要考虑头节点是不是含有val的节点

image-20221005114454177

struct ListNode* removeElements(struct ListNode* head, int val){
typedef struct ListNode ListNode;

//如果(头节点存在且)头节点是val

while( head && head->val==val)
{
ListNode* tmp=head;
head=head->next;
free(tmp);
tmp=NULL;
}
ListNode* cur=head;
while(cur && cur->next!=NULL)
{
 if(cur->next->val==val)
 {//删除Val的节点
     ListNode* prev=cur->next;
     cur->next=cur->next->next;
     free(prev);
 }
 else 
 {
     cur=cur->next;//迭代
 }
}
return head;//返回头节点噢
}

设哨兵位设了哨兵位就少了考虑头节点是不是含val的节点!

设哨兵位phead,最后返回phead->next即可!

image-20221005115420934

struct ListNode* removeElements(struct ListNode* head, int val){
typedef struct ListNode ListNode;
//给哨兵位开空间
ListNode* phead=(ListNode*)malloc(sizeof(ListNode));
phead->next=head;//连接链表
ListNode* cur=phead;
while(cur->next!=NULL)
{
 if(cur->next->val==val)
 {//删除Val的节点
     ListNode* prev=cur->next;
     cur->next=cur->next->next;
     free(prev);
 }
 else 
 {
     cur=cur->next;//迭代
 }
}
return phead->next;//返回phead->next节点噢
}

反转链表

反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

image-20221005120213107

双指针法

image-20221005121125843

struct ListNode* reverseList(struct ListNode* head){
typedef struct ListNode ListNode;
ListNode* cur=head;
ListNode*prev=NULL;
while(cur!=NULL)
{
    ListNode*tmp=cur->next;
    cur->next=prev;//链接
    //迭代
    prev=cur;
    cur=tmp;
}
return prev;//返回prev节点
}

递归法

struct ListNode* reverse (struct ListNode* prev,struct ListNode* cur)
{
if(cur==NULL)
return prev;
struct ListNode* tmp=cur->next;//保存cur的下一个节点
cur->next=prev;//链接
//迭代用递归 prev=cur, cur=tmp
return reverse(cur,tmp);
}
struct ListNode* reverseList(struct ListNode* head){
// struct ListNode*prev=NULL;
// struct ListNode*cur=head;
return reverse(NULL,head);
}

两两交换链表中的节点

两两交换链表中的节点

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

image-20221005191408969

注意!这道题是两个节点两两交换节点,然后到下一对节点两两交换…

如果不满足一对节点时就不交换!

image-20221005193556891

我们要操作第一个节点,所以我们得在前面创建哨兵节点(phead),然后用cur指针指向它;

第一步cur->next=cur->next->next;这时候我们就失去了cur->next这个节点的地址了,所以我们要先在第一步前保存cur->next的地址: tmp1=cur->next

第二步cur->next=tmp1;

第三步:tmp1->next=cur->next->next->next???有没有发现经过第一步和第二步我们也找不到cur->next->next->next的节点了所以我们要在第一步前先保存cur->next->next->next的地址:

tmp2=cur->next->next->next

第三步tmp1->next=tmp2

递归条件就是cur=cur->next->next(因为要操作后两个节点,所以指针要在前一个节点)

终止条件就是操作的两个节点都要存在:cur!=NULL && cur->next!=NULL

最后返回头:return phead->next;

具体实现看下面代码:

迭代法

struct ListNode* swapPairs(struct ListNode* head){
typedef struct ListNode ListNode;
ListNode* phead=(ListNode*)malloc(sizeof(ListNode));
phead->next=head;
ListNode*cur=phead;
while(cur->next!=NULL&& cur->next->next!=NULL)
{
    ListNode*tmp1=cur->next;//保存节点
    ListNode*tmp2=cur->next->next->next;//保存节点
    cur->next=cur->next->next;//第一步
    cur->next->next=tmp1;//第二步
    tmp1->next=tmp2;//第三步
    cur=cur->next->next;//迭代
}
return phead->next;//把头传回去
}

递归法

思路跟迭代法一样

struct ListNode* swapPairs(struct ListNode* head){
if(head==NULL || head->next==NULL)
{
return head;//终止条件
}
struct ListNode*nhead=head->next;//保存head的下一个结点

head->next=swapPairs(nhead->next);//寻找head连接的下一对的头节点

nhead->next=head;
return nhead;
}

删除链表的倒数第N个节点

删除链表的倒数第N个节点

创建一个fast点,一个slow结点,fast先走n步,然后fast和slow一起走,当fast为空时,slow指向的结点就是要删除的结点,提前保存好slow的前一个结点(用cur保存),然后cur->next=slow->next再把slow指向的空间free掉,最后返回头结点即可。(fast指针和slow指针永远间隔n个结点)

struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
typedef struct ListNode ListNode;
ListNode*fast=head;
ListNode*slow=head;
ListNode*cur=NULL;
while(n--)
{
    fast=fast->next;
}
if(fast==NULL)//考虑链表只有一个结点
{
    head=head->next;
    return head;
}

while(fast!=NULL)
{
    fast=fast->next;
cur=slow;
slow=slow->next;
}
cur->next=slow->next;
return head;
}

链表相交

链表相交

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null

image-20221005234530418

设链表A长度为lengthA,链表B长度为lengthB,然后比较找出最长和最短,向减后得到两个链表同样的起点,让curl遍历长的链表先遍历长链表比短链表多出的部分,然后让curs指针和curl指针一起遍历,相等即有交点。

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
typedef struct ListNode ListNode;
ListNode*curl=headA;
ListNode*curs=headB;
int lengthA=0;
int lengthB=0;
int index=0;
while(curl)
{
    lengthA++;
    curl=curl->next;
}
while(curs)
{
    lengthB++;
    curs=curs->next;
    
}
if(lengthA>lengthB)
{
    index=lengthA-lengthB;//算差值
    curl=headA;
    curs=headB;
}
else
{
 index=lengthB-lengthA;//算差值
curs=headA;
curl=headB;
}

while(index--)
{
curl=curl->next;
}
while(curl)
{
    if(curl==curs)
    {
        return curs;
    }
    curl=curl->next;
    curs=curs->next;
}
return NULL;
}

环形链表

环形链表

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。

image-20221006001925646

设一个cur指针一次走两步,prev指针一次走一步,当cur指针进到环里并且到到(即将cur遇见prev)相遇点,此时cur走了x+y步,等到cur和prev相遇时,prev走了x+y步;cur转了n圈,所以是走了n*(y+z)+x+y步;加上cur走的路程是prev的两倍,所以得等式

2(x+y)=n (y+z)+x+y 简化得x=(n-1) (y+z)+zn=1*时,x=z

当prev也进到环里了,cur走两步,prev走一步,等于cur一步一步靠近prev,所以不怕错过噢~~~(就像你终究会遇到你的心上人那样,不会错过~ ~ ~

struct ListNode *detectCycle(struct ListNode *head) {
    typedef struct ListNode ListNode;
    ListNode*cur=head;
    ListNode*prev=head;
    while(cur&& cur->next)
    {
        cur=cur->next->next;//cur走两步
        prev=prev->next;//prev走一步
        if(cur==prev)//相遇在相遇点
        {
            ListNode*phead=cur;//相遇点
            ListNode*shead=head;//表头
            while(phead!=shead)//相遇在入环处
            {
                phead=phead->next;
                shead=shead->next;
            }
            return shead;//返回入环处
        }

    }
    return NULL;//如果没有环记得返回空
}

总结

以上内容是我看[代码随想录]并做题后的感想和思路,如果对你有帮助的话不妨给个小小的👍~~~

代码随想录链接:代码随想录

最后最后,祝大家国庆节快乐呀!!!(虽然只剩一天了呜呜呜~~~)

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值