《一招吃透链表操作:三指针反转法,面试遇到链表题再也不慌!》

🔥@晨非辰Tong:个人主页 

👀专栏:《C语言》《数据结构与算法》

💪学习阶段:C语言、数据结构与算法初学者

⏳“人理解迭代,神理解递归。”


引言:单链表知识学完了,却在刷题时无从下手?本文将单链表的操作理论转化为解决力扣经典题目的实战能力,让你彻底掌握。


目录

1.  203. 移除链表元素 - 力扣(LeetCode)

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;
}

回顾:

《算法面试“必杀技”:双指针法高效解决数组原地操作》-CSDN博客

结语:道阻且长,行则将至。本次练习是巩固的节点,更是下一段征程的起点。刷题之路,继续前行。

评论 36
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值