【LeetCode】链表OJ题

简介: 【LeetCode】链表OJ题

👉移除链表元素👈


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


示例 1:

a6ef675da8464a3e8d17b8beb2da2671.png

输入:head = [1,2,6,3,4,5,6], val = 6

输出:[1,2,3,4,5]



示例 2:

输入:head = [], val = 1

输出:[]



示例 3:

输入:head = [7,7,7,7], val = 7

输出:[]



提示:


列表中的节点数目在范围 [0, 10^4] 内

1 <= Node.val <= 50

0 <= val <= 50


思路一


定义两个指针cur和prev,cur为当前节点的地址,prev为上一个节点的地址。

当cur->val == val且cur == head时,将头节点的下一个节点作为新的头节点,释放当前节点cur,cur重新赋值为head,此时的删除为头删。

当cur->val == val且cur != head时,将上一个节点prev的next赋值为当前节点cur的next,再将当前节点cur释放,cur重新赋值为prev->next,此时的删除为中间删。

当cur->val != val时,将prev赋值为cur记住当前节点的位置,方便后续删除节点,再将cur赋值为cur->next,迭代往后走。

d8c50cc9b2064196a5d195ce00d513a7.png


/*
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode *prev = NULL;
    struct ListNode *cur = head;
    while(cur)
    {
        //当前节点的值等于val
        if(cur->val == val)
        {
            //头删
            if(cur == head)
            {
                head = head->next;//换头
                free(cur);//释放头节点
                cur = head;//迭代往后走
            }
            //中间删
            else
            {
                prev->next = cur->next;//前一个节点指向cur的下一个节点
                free(cur);//释放当前的节点
                cur = prev->next;//迭代往后走
            }
        }
        //当前节点的值不等于val
        else
        {
            prev = cur;//prev记住当前节点的位置,方便后续删除节点
            cur = cur->next;//迭代往后走
        }
    }
    return head;
}

fb32806acc1240fab6eb5409ee53420a.png


思路二


定义一个三个变量,分别是哨兵位头节点DummyNode、当前节点cur和尾节点tail。利用while循环变量整个链表,当cur->val != val时,尾插数据到新链表中tail->next = cur, tail = cur, cur = cur->next;当cur->val == val时,删除节点并往后走struct ListNode* del, cur = cur->next, free(del)。循环结束后,尾节点指向NULL,防止野指针问题。最后,将哨兵位头节点释放掉,返回结果就行了。


struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode* cur = head;
    struct ListNode* DummyNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* tail = DummyNode;
    DummyNode->next = NULL;
    while(cur)
    {
        if(cur->val != val)
        {
            tail->next = cur;
            tail = cur;
            cur = cur->next;
        }
        else
        {
            struct ListNode* del = cur;
            cur = cur->next;
            free(del);
        }
    }
    tail->next = NULL;
    struct ListNode* newHead = DummyNode->next;
    free(DummyNode);
    return newHead;
}

2701dcb9015f4a9a98ca29581b725c63.png


👉反转链表👈


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


示例 1:

输入:head = [1,2,3,4,5]

输出:[5,4,3,2,1]

示例 2:

输入:head = [1,2]

输出:[2,1]


示例 3:

输入:head = []

输出:[]


提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000



思路一


取原链表中的节点,头插到newhead新链表中。需要借助几个变量才能完成这个过程:next记录当前节点cur的下一个节点;然后进行头插,将cur->next赋值为newhead

,让当前节点链接到要返回的链表中;再然后将newhead赋值为cur,更新头结点;最后将cur赋值为next,迭代往后走。

b9e79ec4826543ffa01f4a79e82371fd.png


/*
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head)
{
     //思路:取原链表中的节点,头插到newhead新链表中
    struct ListNode* cur = head;
    struct ListNode* newhead = NULL;
    while(cur)
    {
        //next记录当前节点的下一个节点
        struct ListNode* next = cur->next;
        //头插
        cur->next = newhead;
        newhead = cur;
        //迭代往后走
        cur = next;
    }
    return newhead;
}

82aee9e141e44435853ca6201bdf09c8.png

思路二


定义三个指针,分别是n1n2n3n1记录的是上一个节点的地址,n2记录的是当前节点的地址、n3记录的是下一个节点的地址。现将n2->next赋值为n1进行反转链表;然后将n1赋值为n2n2赋值为n3n3赋值为n3->next进行迭代。

e0cec986b65340dea42a754e49f9bf2d.png

/*
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head)
{
    //空链表
    if(head == NULL)
        return NULL;
    struct ListNode* n1,*n2,*n3;
    n1=NULL;//记录上一个节点
    n2=head;//记录当前节点
    n3=head->next;//记录下一个节点
    while(n2)
    {
        //反转
        n2->next = n1;
        //迭代
        n1 = n2;
        n2 = n3;
        //n3不为空才能解引用
        if(n3!=NULL)
        {
            n3 = n3->next;
        }
    }
    return n1;
}

f874d214f09a4c5c947bb2e81b038966.png


👉链表的中间节点👈


给定一个头结点为 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:[1,2,3,4,5]

输出:此列表中的结点 3 (序列化形式:[3,4,5]) 返回的结点值为 3 。



示例 2:


输入:[1,2,3,4,5,6]

输出:此列表中的结点 4 (序列化形式:[4,5,6]) 由于该列表有两个中间结点,值分别为 3 和4,我们返回第二个结点。



提示:


给定链表的结点数介于 1 和 100 之间。


思路:可以定义两个指针slowfastslow一次走一步,而fast一次走两步。当fast==NULL或者fast->next ==NULL 时,循环结束,此时的slow就是中间节点,将slow返回就行了。

41a0ea2aeaf84570829c0430c9fa1486.png


struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;//slow走一步
        fast = fast->next->next;//fast走两步
    }
    return slow;
}

88d05c79091949df94020eba87a63f29.png

👉链表中倒数第k个节点👈


思路:定义两个指针slow和fast,先让fast先走k步,然后slow和fast一起走。当fast==NULL时,slow就是倒数第k个节点,将slow返回就行了。也可以让fast先走k-1步,然后slow和fast一起走,当fast==NULL时,slow就是倒数第k个节点。

7a537ee4fa324cb3b1669964d9e6f447.png

/*
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* getKthFromEnd(struct ListNode* head, int k)
{
    //fast先走k步,然后slow和fast一起走
    //当fast==NULL时,slow就是倒数第k个节点
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while(k--)
    {
        //k大于链表的长度
        if(fast == NULL)
        {
            return NULL;
        }
        fast = fast->next;
    }
    while(fast)
    {
        slow = slow->next;
        fast = fast->next;
    }
    return slow;
}
/*
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* getKthFromEnd(struct ListNode* head, int k)
{
    //fast先走k-1步,然后slow和fast一起走
    //当fast->next==NULL时,slow就是倒数第k个节点
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while(--k)
    {
        //k大于链表的长度
        if(fast == NULL)
        {
            return NULL;
        }
        fast = fast->next;
    }
    while(fast->next)
    {
        slow = slow->next;
        fast = fast->next;
    }
    return slow;
}

d943fa75551843c48e1741e41d791322.png


以上两种方式均可解决这个问题,可以选择自己喜欢的一种。


👉总结👈


本篇博客讲解了几道的链表OJ题,其中涉及了链表的基本操作头插和中间插以及双指针的技巧。如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家啦!💖💝❣️










相关文章
|
11月前
【力扣】-- 移除链表元素
【力扣】-- 移除链表元素
123 1
|
11月前
Leetcode第21题(合并两个有序链表)
这篇文章介绍了如何使用非递归和递归方法解决LeetCode第21题,即合并两个有序链表的问题。
148 0
Leetcode第21题(合并两个有序链表)
|
5月前
|
算法 Go
【LeetCode 热题100】23:合并 K 个升序链表(详细解析)(Go语言版)
本文详细解析了 LeetCode 热题 23——合并 K 个升序链表的两种解法:优先队列(最小堆)和分治合并。题目要求将多个已排序链表合并为一个升序链表。最小堆方法通过维护节点优先级快速选择最小值,;分治合并则采用归并思想两两合并链表。文章提供了 Go 语言实现代码,并对比分析两种方法的适用场景,帮助读者深入理解链表操作与算法设计。
180 10
|
11月前
|
存储 Java
数据结构第三篇【链表的相关知识点一及在线OJ习题】
数据结构第三篇【链表的相关知识点一及在线OJ习题】
91 7
|
11月前
LeetCode第二十四题(两两交换链表中的节点)
这篇文章介绍了LeetCode第24题的解法,即如何通过使用三个指针(preNode, curNode, curNextNode)来两两交换链表中的节点,并提供了详细的代码实现。
121 0
LeetCode第二十四题(两两交换链表中的节点)
|
11月前
Leetcode第十九题(删除链表的倒数第N个节点)
LeetCode第19题要求删除链表的倒数第N个节点,可以通过快慢指针法在一次遍历中实现。
120 0
Leetcode第十九题(删除链表的倒数第N个节点)
|
11月前
|
索引
力扣(LeetCode)数据结构练习题(3)------链表
力扣(LeetCode)数据结构练习题(3)------链表
231 0
|
11月前
【LeetCode 10】142. 环形链表 II
【LeetCode 10】142. 环形链表 II
83 0
|
11月前
【LeetCode 09】19 删除链表的倒数第 N 个结点
【LeetCode 09】19 删除链表的倒数第 N 个结点
149 0
|
11月前
【LeetCode 08】206 反转链表
【LeetCode 08】206 反转链表
61 0