【数据结构】图文详解11道力扣链表OJ题


需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。


 一、移除链表元素

二、反转链表

三、链表的中间节点

四、链表中倒数第k个节点

五、合并两个有序列表

六、链表分割

七、回文链表

八、相交链表

九、环形链表

十、环形链表 II

1、思路一

2、思路二

十一、复制带随即指针的链表

1、思路

2、代码


一、移除链表元素

1、思路

新建一个哨兵位,用cur指针遍历原链表,cur->val!=val,将该节点连接至哨兵链表,反之释放。迭代遍历原链表即可,最后记得将哨兵链表的尾节点的next置空!!!

2、代码

struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode* guard=(struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* guardcur=guard;//用于记录哨兵链表的尾插
    struct ListNode* cur=head;//用于原链表的遍历
    while(cur)
    {
        struct ListNode* next=cur->next;
        if(cur->val!=val)
        {
           guardcur->next=cur;
           guardcur=cur;
        }
        else 
        {
            free(cur);
        }
            cur=next;
    }
    guardcur->next=NULL;//最后需要把尾节点的next置空
    head=guard->next;
    free(guard);//记得释放哨兵位
    return head;
}

二、反转链表

1、思路

遍历原链表,将每一个节点取下来,不断头插反转链表

2、代码

struct ListNode* reverseList(struct ListNode* head){
    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、思路

思路1:可以遍历计数找中间节点

思路2:快慢指针,快指针走两步,慢指针走一步,快指针走到尾节点或者空节点,慢指针就指向链表的中间节点

2、代码

struct ListNode* middleNode(struct ListNode* head){
    struct ListNode* fast=head,*slow=head;
    while(fast!=NULL&&fast->next!=NULL)
    {
        fast=fast->next->next;
        slow=slow->next;
    }
    return slow;
}

注意,while循环内两个条件的顺序不能颠倒。如果颠倒,当fast为NULL时,先判断fast->next!=NULL,这是一个对空指针解引用的问题!

四、链表中倒数第k个节点

1、思路

定义快慢指针,不难发现,快指针需要比慢指针快k-1步,所以先让快指针向前走k-1步,判断fast==NULL是防止原链表为空,判断fast->next=NULL是因为,后面fast=fast->next,说明k大于链表长度。

2、代码

struct ListNode* getKthFromEnd(struct ListNode* head, int k){
    struct ListNode* fast=head,*slow=head;
    int tmp=k-1;//快慢指针相差的距离
    while(tmp--)
    {
        if(fast==NULL||fast->next==NULL)//考虑下值大于节点个数的问题
            return NULL;
        fast=fast->next;
    }
    while(fast->next)
    {
        fast=fast->next;
        slow=slow->next;
    }
    return slow;
}

五、合并两个有序列表

1、思路

创建一个哨兵节点,用两个指针分别指向原链表,将val小的节点尾插即可。记得释放哨兵位。

2、代码

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode* cur1=list1,*cur2=list2;
    struct ListNode* guard=(struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* guardcur=guard;
    struct ListNode* small,*big;
    while(cur1!=NULL&&cur2!=NULL)
    {
        if(cur1->val>cur2->val)
        {
            small=cur2;
            big=cur1;
            cur2=cur2->next;
        }
        else
        {
            small=cur1;
            big=cur2;
            cur1=cur1->next;
        }
        guardcur->next=small;
        guardcur=small;
    }
    if(cur1==NULL)
    {
        guardcur->next=cur2;
    }
    else
    {
        guardcur->next=cur1;
    }
    struct ListNode* head=guard->next;
    free(guard);
    return head;
}

六、链表分割

1、思路

创建两个哨兵位,遍历原链表,将val小于x和大于等于x的节点分别尾插至两个哨兵链表,再将两个链表连接起来。注意要把大的哨兵链表的尾指针置空。

2、代码

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        ListNode* guard1=(ListNode*)malloc(sizeof(ListNode));//哨兵位1
        ListNode* guard2=(ListNode*)malloc(sizeof(ListNode));//哨兵位2
        ListNode* small=guard1,*big=guard2;
        ListNode* cur=pHead;//用于遍历链表
        while(cur)
        {
            if(cur->val<x)
            {
                small->next=cur;
                small=small->next;
                cur=cur->next;
            }
            else
            {
                big->next=cur;
                big=big->next;
                cur=cur->next;
            }
        }
        big->next=NULL;
        small->next=guard2->next;//链接两个链表
        free(guard2);
        pHead=guard1->next;
        free(guard1);
        return pHead;
    }
};

七、回文链表

1、思路

1、找出中间节点(本文题三)

2、对后半段进行逆置(本文题二)

3、比对val是否相等,当逆置指针为空,比对结束

2、代码

bool isPalindrome(struct ListNode* head){
    //先用快慢指针找到中间节点
    struct ListNode* fast=head,*slow=head;
    while(fast!=NULL&&fast->next!=NULL)
    {
        slow=slow->next;
        fast=fast->next->next;
    }
    //现在slow是中间节点
    //反转后半部分链表
    struct ListNode* newnode=NULL;
    struct ListNode* cur=slow;
    while(cur)
    {
        struct ListNode* next=cur->next;
        cur->next=newnode;
        newnode=cur;
        cur=next;
    }
    //比较
    fast=head;
    slow=newnode;
    while(slow)//fast和slow会同时为空或slow先空
    {
        if(fast->val!=slow->val)
            return false;
        fast=fast->next;
        slow=slow->next;
    }
    return true;
}

八、相交链表

1、思路

1、分别遍历链表,算出两个链表的长度。

2、让长链表先往前走,走到和短链表一样长

3、两个链表再同时走,走到两个指针一样即为相交点,如果走到空还没有找到,则不相交

2、代码

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* curA=headA,*curB=headB;//用于遍历链表
    int lenA=1,lenB=1;//统计A和B的长度
    while(curA)
    {
        curA=curA->next;
        ++lenA;
    }
    while(curB)
    {
        curB=curB->next;
        ++lenB;
    }
    int a=abs(lenA-lenB);//A和B链表长度的差值
    struct ListNode* longlist=headA,*shortlist=headB;//设两个节点
    if(lenA<lenB)//通过一个判断,让longlist指向较长的链表
    {
        longlist=headB;
        shortlist=headA;
    }
    while(a--)//让较长的链表先把差值走掉
    {
        longlist=longlist->next;
    }
    while(longlist)
    {
        if(longlist==shortlist)
            return longlist;
        longlist=longlist->next;
        shortlist=shortlist->next;
    }
    return NULL;
}

九、环形链表

1、思路

定义快慢指针,快指针一次走两步,慢指针一次走一步,如果相遇,说明有环,当快指针等于NULL,说明没环

2、代码

bool hasCycle(struct ListNode *head) {
    //快指针一次走两步,慢指针一次走一步,快指针先进环,慢指针后进环,相对静止
    //能追上说明有环,有一个指针为空说明没环
    struct ListNode* fast=head,*slow=head;
    while(fast&&fast->next)//这里不用判断slow是否为空,不管带不带环,slow没有机会为空
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
            return true;
    }
    return false;
}

十、环形链表 II

1、思路一

1、定义快慢指针,快指针一次走两步,慢指针一次走一步,找到相遇点(本文题九)

2、将相遇点的后一个节点当成另一个链表的头结点,问题转化为本文题八

2、代码

struct ListNode *detectCycle(struct ListNode *head) {
    //先找到相遇点
    struct ListNode* fast=head,*slow=head;
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
            break;
    }
    if(fast==NULL||fast->next==NULL)
        return NULL;

    struct ListNode* headA=fast->next;//两个链表的头结点
    struct ListNode* curA=headA;//cur用于遍历统计链表长度
    struct ListNode* headB=head;
    struct ListNode* curB=headB;
    int lenA=1,lenB=1;
    while(curA!=fast)
    {
        curA=curA->next;
        ++lenA;
    }
     while(curB!=fast)
    {
        curB=curB->next;
        ++lenB;
    }
    struct ListNode* longlist=headA,*shortlist=headB;
    if(lenA<lenB)
    {
        longlist=headB;
        shortlist=headA;
    }
    int a=abs(lenA-lenB);//长度的差值
    while(a--)
    {
        longlist=longlist->next;
    }
    while(1)
    {
        if(longlist==shortlist)
            return longlist;
        longlist=longlist->next;
        shortlist=shortlist->next;
    }
}

3、思路二

1、设进环前的长度为L,入环点到相遇点的距离为X,环的长度为R

2、两指针相遇时,slow走的距离是L+X,fast走的距离是L+N*R+X,N为fast指针在环里绕的圈数(注意这里N一定大于等于1,因为快慢指针在环内相遇,说明快指针和慢指针相遇前,快指针起码路过了一次相遇点)(而且当慢指针进环后,快指针肯定会一圈追上慢指针,因为快指针比慢指针每次多走一步,相对静止,走一圈的距离必定追上)

3、那么2*(L+X)=L+N*R+X,化简得L=N*R-X,再化简得L=(N-1)*R+R-X

4、可得结论:一个指针从头结点开始走,另一个指针从相遇点开始走,那么他们会在入口处相遇

4、代码

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* fast=head,*slow=head;//找相遇点
    
    while(fast!=NULL&&fast->next!=NULL)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
            break;
    }
    if(fast==NULL||fast->next==NULL)
        return NULL;
    slow=head;
    while(fast!=slow)
    {
        fast=fast->next;
        slow=slow->next;
    }
    return fast;
}

十一、复制带随即指针的链表

1、思路

题目要求是复制一个一模一样的链表,难点在于random指针的控制

1、在每个节点后边插入一个节点,复制对应的val

2、再复制random指针,复制时要分空和非空情况,对于空,复制节点random为NNULL;非空,

则为cur->next->random=cur->random->next

3、再创建两个哨兵节点,将原链表与复制链表节点分开。

2、代码

struct Node* copyRandomList(struct Node* head) {
	struct Node* cur=head;
    while(cur)//插入复制节点
    {
        struct Node* next=cur->next;
        struct Node* newnode=(struct Node*)malloc(sizeof(struct Node));
        cur->next=newnode;
        newnode->val=cur->val;
        cur=next;
        newnode->next=cur;
    }
    cur=head;
    while(cur)//复制random指针
    {
        if(cur->random==NULL)
            cur->next->random=NULL;
        else
        {
            cur->next->random=cur->random->next;
        }
        cur=cur->next->next;
    }
    //将链表复原
    cur=head;
    struct Node* guard=(struct Node*)malloc(sizeof(struct Node));
    struct Node* guardcopy=(struct Node*)malloc(sizeof(struct Node));
    guardcopy->next=NULL;
    struct Node* curA=guard,*curB=guardcopy;
    int count=1;
    while(cur)
    {
        struct Node* next=cur->next;
        if(count%2==1)
        {
            curA->next=cur;
            curA=cur;
        }
        else
        {
            curB->next=cur;
            curB=cur;
        }
        cur=next;
        ++count;
    }
    curA->next=NULL;
    struct Node* newhead=guardcopy->next;
    free(guardcopy);
    free(guard);
    return newhead;
}
  • 63
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 64
    评论
评论 64
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒋灵瑜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值