OJ题专题---手把手教你刷题<保姆级讲解**带图解> C语言版---链表1

目录

第一题:面试题02.02.返回倒数第K个节点  OJ链接

题目

思路

图解

具体代码解答

第二题:160.相交链表  OJ链接

题目​

思路

图解

具体代码解答

第三题:203.移除链表元素  OJ链接

题目

思路

图解

具体代码解答

第四题:206.反转链表  OJ链接

题目

思路

图解(解法一)

图解(解法二)

具体代码解答(解法一)

具体代码解答(解法二)

第一题:面试题02.02.返回倒数第K个节点  OJ链接

题目

思路

我们可以使用快慢指针法来解决这个问题。具体思路是:

定义两个指针 fast 和 slow,初始时都指向链表的头节点 head

先让 fast 指针向前移动 k 步。

然后让 fast 和 slow 指针同时向前移动,直到 fast 指针指向链表的末尾(即 fast == NULL)。此时,slow 指针所指向的节点就是倒数第 k 个节点。

图解

具体代码解答

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 //利用快慢指针,快指针比慢指针先走K
 //当快指针走到NULL时,慢指针刚好为倒数第 k 个节点
int kthToLast(struct ListNode* head, int k) 
{
    struct ListNode* fast=head;
    struct ListNode* slow=head;
    int i=0;
    for(i=0;i<k;i++)
    {
        fast=fast->next;
    }
    while(fast!=NULL)
    {
        fast=fast->next;
        slow=slow->next;
    }
        return slow->val;  
}

第二题:160.相交链表  OJ链接

题目

思路

1.找出长链表与短链表相差的部分
2.让长指针先走相差步
3.同时走至到相交

图解

链表A的指针 curA 和链表B的指针 curB 分别遍历各自链表后,通过计算得到长度差 gap 。

 对于链表A(短链表, shortList  指向其起始段)和链表B(长链表, longlist  指向其起始段),先让长链表的指针移动  gap  步(图中体现为  longlist  从  b1  移动到  b2 ),使两个链表的剩余长度一致。
之后两个指针( shortList  和  longlist )同步移动,最终会在相交节点  c1  处相遇,以此找到两个链表的相交节点。

具体代码解答

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 //思路:1.找出长链表与短链表相差的部分
 //2.让长指针先走相差步
 //3.同时走至到相交
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* curA=headA;
    struct ListNode* curB=headB;
    int lenA=1;
    int lenB=1;
    while(curA->next)
    {
        curA=curA->next;
        lenA++;
    }
    while(curB->next)
    {
        curB=curB->next;
        lenB++;
    }
    int gap=abs(lenA-lenB);
    struct ListNode* longList=headA;
    struct ListNode* shortList=headB;
    if(lenA<lenB)
    {
        longList=headB;
        shortList=headA;
    }
    while(gap--)
    {
         longList=longList->next;
    }
    while(shortList!=longList)
    {
        longList=longList->next;
        shortList=shortList->next;
    }
    return shortList;
}

第三题:203.移除链表元素  OJ链接

题目

思路

1.创建一个新链表

2.如果原链表中Node.val!=val,则把其尾插到新链表中。

图解

if(pcur->val == val):判断当前 pcur 指向的节点值是否等于要删除的值(假设此处要删除的是 6)。
pcur = pcur->next:因为该节点值等于 val,所以不将其插入新链表,直接让 pcur 后移,处理原链表的下一个节点(值为 3 的节点)。

具体代码解答

/**
 * 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)
    {
        //找值不为val的节点,尾插到新链表中
        if(pcur->val != val)
        {
            //链表为空
            if(newHead == NULL)
            {
                newHead = newTail = pcur;
            }else{
                //链表不为空
                newTail->next = pcur;
                newTail = newTail->next;
            }
        }
        pcur = pcur->next;
    }
    if(newTail)
        newTail->next = NULL;
    return newHead;
}

题目

第四题:206.反转链表  OJ链接

题目

思路

这道题博主将介绍两种解法

解法一:创建一个新链表,将原链表的节点拿过来依次头插

解法二:创建三个指针,完成原链表的翻转

图解(解法一)

展示原链表和新链表的初始指针状态:
原链表:节点依次为 1→2→3→4→5,pcur 指向原链表的头节点(值为 1),next 暂存 pcur 的下一个节点(值为 2)。
新链表:newHead 初始为 NULL(新链表还没有节点)。

执行 pcur->next = newHead:原链表中值为 1 的节点(pcur 指向的节点)的 next,从 “指向值为 2 的节点” 变为 “指向 newHead(即 NULL)”。
执行 newHead = pcur:新链表的头节点更新为值为 1 的节点(此时新链表为 1→NULL)。
右侧的 “相当于” 图示,是对 “头插后新链表形态” 的简化呈现,帮助直观理解 “新链表的头变为 1,且 1 的 next 是 NULL”。

newHead 指向值为 1 的节点(新链表的头)。
pcur 此时还未移动(代码中下一步才会执行 pcur = next),所以仍然指向值为 1 的节点(但后续会移动到 next 暂存的 “值为 2 的节点”)。

执行 pcur = next(next 是第一步暂存的 “值为 2 的节点”),所以 pcur 现在指向值为 2 的节点。
原链表剩下的节点为 2→3→4→5,后续会重复 “头插 2、3… 直到原链表遍历完” 的步骤。

图解(解法二)

初始状态
原链表:节点依次为 1→2→3→4→5,是待反转的链表。
指针指向:
n2 指向原链表的头节点(值为 1 的节点);
n3 指向原链表中 n2 的下一个节点(值为 2 的节点);
n1 指向 NULL(代表 “已完成反转的部分”,初始时还没有反转节点,所以为 NULL)。

 

第一次反转操作后
这一步对应代码中 n2->next = n1; n1 = n2;  的逻辑:
n2->next = n1:将当前 n2 指向的节点(值为 1 的节点)的 next 指向 n1(此时 n1 是 NULL),相当于把 “值为 1 的节点” 从原链表断开,并作为已反转部分的第一个节点(尾节点,因为 next 是 NULL)。
n1 = n2:更新 “已反转部分的尾指针”n1,使其指向刚完成反转的节点(值为 1 的节点)。

n2 = n3:n2 移动到值为 2 的节点,为下一轮反转做准备。

若 n3 非空则执行 n3 = n3->next;” 的逻辑

具体代码解答(解法一)

解法一:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) 
{
    struct ListNode* newHead=NULL;
    struct ListNode* pcur=head;
    while(pcur)
    {
        struct ListNode* next=pcur->next;
        pcur->next=newHead;
        newHead=pcur;
        pcur=next;        
    }

    return newHead;
}

具体代码解答(解法二)

解法二:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) 
{
    struct ListNode* n1=NULL;
    struct ListNode* n2=head;
    //判空
    if(head==NULL)
    {
        return NULL;
    }
    struct ListNode* n3=head->next;
    while(n2)
    {
        
        n2->next=n1;
        n1=n2;
        n2=n3;
        if(n3)
        {
            n3=n3->next;
        }
    }
    return n1;   
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值