首页 > 其他分享 >【代码随想录训练营第42期 Day3打卡 LeetCode 203.移除链表元素,707.设计链表,206.反转链表】

【代码随想录训练营第42期 Day3打卡 LeetCode 203.移除链表元素,707.设计链表,206.反转链表】

时间:2024-07-19 17:27:13浏览次数:14  
标签:obj cur 随想录 next 链表 移除 节点 MyLinkedList

一、做题感受

今天是打卡的第三天,前两天题目主要考察数组相关知识,现在已经来到了链表的学习。题目共有三道,都是以考察单链表为主,整体来说难度不大,但是思路很灵活,尤其是反转链表的考察,双指针的新用法。今天做题总体感觉不错,能有自己的思考和理解。

二、链表相关知识

1.常见链表的种类:

单向链表,双向链表,循环链表,静态链表。

2.链表的定义(一般默认单链表):

// 单链表
struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};

3.虚拟头节点(哑节点,在这里我们记作dummyHead)的作用:

(1)统一插入操作:在没有虚拟头节点的情况下,插入一个节点需要分别处理在链表头部插入和在其他位置插入两种情况。而有了虚拟头节点后,所有的插入操作都可以看作在虚拟头节点之后的位置插入,这样就统一了插入操作的处理逻辑。

(2)避免空链表判断:在没有虚拟头节点的情况下,如果链表是空的,那么在进行操作时需要特殊处理,判断头指针是否为空。而有了虚拟头节点后,虚拟头节点始终存在,它代表了链表的起始位置,不会为空,因此不需要对空链表进行额外的判断。

(3)简化删除操作:删除一个节点通常需要修改被删除节点的前一个节点的指针,但如果被删除节点是头节点,就没有前一个节点可供修改。而有了虚拟头节点后,它将永远指向第一个节点,可以作为前一个节点的角色,使得删除操作变得简单统一。

4.虚拟头节点(哑节点,dummyHead)的定义和初始化:

C语言:

struct ListNode* dummyHead = malloc(sizeof(struct ListNode));
    dummyHead->next = head;

c++

struct ListNode* dummyHead = new ListNode(0, head);

三、题目及题解

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

这个题的思路很简单,但是有一点细节需要注意。

首先看看我最初的错误做法(相信有很多人会和我犯同样的错误)

这里的错误在于直接用temp指向头节点head的话while(temp->next != nullptr)就无法把head为空的情况考虑进去,为此,我们得引入虚拟头节点来解决这个问题。

正确代码如下:通过创建虚拟头节点dummyHead,使temp->next可以遍历整个链表,从而解决问题。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        //创建哑节点dummyHead(虚拟头节点),其中dummyHead->next = head
        struct ListNode* dummyHead = new ListNode(0, head);     
        struct ListNode* temp = dummyHead;
        while(temp->next != nullptr)
        {
            if(temp->next->val == val)
                temp->next = temp->next->next;
            else
                temp = temp->next;
       }
       return dummyHead->next;
    };
};

707. 设计链表 - 力扣(LeetCode)

这个题的话看上去很繁琐,但是当你仔细看完题目后发现就是很多单链表基本操作的集合,只是在这里需要注意,obj是作为虚拟头节点来使用的,这样才能更简单的实现整个链表进行各种操作。还有就是cur指针,初始化指向obj->next(即head)作为临时节点的使用,用来遍历整个链表。

这里附上一份代码随想录的代码(注释的很详尽,可以照着一个函数一个函数看,顺带也当复习一下链表的基本操作:创建,插入,删除等等):

typedef struct MyLinkedList {
    int val;
    struct MyLinkedList* next;
}MyLinkedList;

/** Initialize your data structure here. */

MyLinkedList* myLinkedListCreate() {
    //这个题必须用虚拟头指针,参数都是一级指针,头节点确定后没法改指向了!!!
    MyLinkedList* head = (MyLinkedList *)malloc(sizeof (MyLinkedList));
    head->next = NULL;
    return head;
}

/** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
//获取链表中下标为 index 的节点的值
int myLinkedListGet(MyLinkedList* obj, int index) {     //obj相当于一个虚拟头指针
    MyLinkedList *cur = obj->next;
    for (int i = 0; cur != NULL; i++){
        if (i == index){
            return cur->val;
        }
        else{
            cur = cur->next;
        }
    }
    return -1;
}

/** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
//头插法:链表末尾插入值为val的一个节点(这里记作nhead)
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
    MyLinkedList *nhead = (MyLinkedList *)malloc(sizeof (MyLinkedList));
    nhead->val = val;
    nhead->next = obj->next;
    obj->next = nhead;
}

/** Append a node of value val to the last element of the linked list. */
//尾插法:链表末尾插入值为val的一个节点(这里记作ntail)
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
    MyLinkedList *cur = obj;       //创建cur指针遍历整个链表到达tail
    while(cur->next != NULL){
        cur = cur->next;
    }
    MyLinkedList *ntail = (MyLinkedList *)malloc(sizeof (MyLinkedList));
    ntail->val = val;
    ntail->next = NULL;
    cur->next = ntail;
}

/** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
//将一个值为 val 的节点插入到链表中下标为 index 的节点之前
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
    if (index == 0){
        myLinkedListAddAtHead(obj, val);
        return;
    }
    MyLinkedList *cur = obj->next;
    for (int i = 1 ;cur != NULL; i++){
        if (i == index){
            MyLinkedList* newnode = (MyLinkedList *)malloc(sizeof (MyLinkedList));
            newnode->val = val;
            newnode->next = cur->next;
            cur->next = newnode;
            return;
        }
        else{
            cur = cur->next;
        }
    }
}

/** Delete the index-th node in the linked list, if the index is valid. */
//删除链表中下标为 index 的节点
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
    if (index == 0){
        MyLinkedList *tmp = obj->next;
        if (tmp != NULL){
            obj->next = tmp->next;
            free(tmp);     
        }
        return;
    }
    MyLinkedList *cur = obj->next;
    for (int i = 1 ;cur != NULL && cur->next != NULL; i++){
        if (i == index){
            MyLinkedList *tmp = cur->next;
            if (tmp != NULL) {
                cur->next = tmp->next;
                free(tmp);
            }
            return;
        }
        else{
            cur = cur->next;
        }
    }
    
}

void myLinkedListFree(MyLinkedList* obj) {      //清理obj
    while(obj != NULL){
        MyLinkedList *tmp = obj;
        obj = obj->next;
        free(tmp);
    }
}

/**
 * Your MyLinkedList struct will be instantiated and called as such:
 * MyLinkedList* obj = myLinkedListCreate();
 * int param_1 = myLinkedListGet(obj, index);
 
 * myLinkedListAddAtHead(obj, val);
 
 * myLinkedListAddAtTail(obj, val);
 
 * myLinkedListAddAtIndex(obj, index, val);
 
 * myLinkedListDeleteAtIndex(obj, index);
 
 * myLinkedListFree(obj);
*/

206. 反转链表 - 力扣(LeetCode)

这个题目是一道很经典的题了,以前就刷到过相关讲解的视频,其实整体思路就是双指针(不同于以往数组所用的双指针)来存储节点。pre指向先驱节点,cur指向当前节点,temp指向当前节点的下一个节点,通过操作使当前节点指向前一个节点,然后再往后依次进行同样操作。

这道题建议多画画图,这样思路就明确了。

代码如下:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        struct ListNode* pre = NULL;
        struct ListNode* cur = head;
        while(cur)
        {
            struct ListNode* temp = cur->next;      //临时指针temp存储cur的下一位,用于遍历整个链表
            cur->next = pre;        
            pre = cur;
            cur = temp;     
        }
        return pre;     //此时pre指向原链表的最后一位,也是反转后新链表的head
    }
};

当然也可以用递归的做法(其实思路是一样的),这里就不过多描述了。如果这道题还有不理解的地方,可以看看代码随想录公开课,相信你会有所收获:帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法_哔哩哔哩_bilibili

四、今日小结:

今天的打卡到此也就结束了。今天回顾了链表的相关知识,也掌握并理解了虚拟头节点的使用,算是收获满满。我是算法小白,也希望终有所成。

标签:obj,cur,随想录,next,链表,移除,节点,MyLinkedList
From: https://blog.csdn.net/2303_79786049/article/details/140553484

相关文章

  • 移除水印
    FrameworkElementobj=ControlHelper.SearchControls(Application.Current.MainWindow,"__watermark__");if(obj!=null){(objasGrid).Children.Clear();}......
  • 单链表,双链表和内核链表的比较
    首先贴上几个链接,来自一些大佬,这些文章详细易懂,可以帮助我们快速全面了解单双链表以及Linux内核链表list.h。1.C语言链表超详解;作者:rivencode;图文并茂,炒鸡详细2.链表基础知识详解;作者:不秃也很强;代码详细,条理清晰3.拒绝造轮子!如何移植并使用Linux内核的通用链表(附完整代码实现);作......
  • 代码随想录算法训练营第30天 | 贪心算法 2: 122.买卖股票的最佳时机II、55. 跳跃游戏
    代码随想录算法训练营第30天|贪心算法2:122.买卖股票的最佳时机II、55.跳跃游戏、45.跳跃游戏II、1005.K次取反后最大化的数组和122.买卖股票的最佳时机IIhttps://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/代码随想录https://programmerca......
  • 代码随想录算法训练营第29天 | 贪心算法1:455.分发饼干、376.摆动序列、53.最大子序和
    代码随想录算法训练营第29天|贪心算法1:455.分发饼干、376.摆动序列、53.最大子序和贪心算法基础理论https://programmercarl.com/贪心算法理论基础.html455.分发饼干https://leetcode.cn/problems/assign-cookies/description/代码随想录https://programmercarl.com/0455......
  • 基础数据结构——初级——链表
    链表的特点是一组位于任意位置的存储单元存储线性表的数据元素。一般分为单向链表和双向链表(如下图所示)。 使用链表时,可以用STL的list,也可以自己写链表。如果自己写代码实现链表,有两种编码实现方法:动态链表和静态链表。在算法竞赛中为加快编码速度,一般用静态链表或者STL的l......
  • 代码随想录day 29 买卖股票的最佳时机II | 跳跃游戏 | 跳跃游戏II | K次取反后最大化
    买卖股票的最佳时机II买卖股票的最佳时机II解题思路利用贪心算法,只要股票卖了后一天能获利,就买了,所以只要遍历一下整个数组,根据这个算法就能得到最终获利的数目知识点贪心心得歪打正着的一题跳跃游戏跳跃游戏解题思路利用贪心算法,只需要有一次跳转到数组之外说明就能跳......
  • 攻克链表篇
    leetcode206翻转链表题目描述:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。示例1:输入:head=[1,2,3,4,5]输出:[5,4,3,2,1]示例2:输入:head=[1,2]输出:[2,1]示例3:输入:head=[]输出:[]算法思想:创建一个空链表头指针,将原给链表的节点依次遍历......
  • 代码随想录day28 分发饼干 | 摆动序列 | 最大子序和
    分发饼干分发饼干解题思路用贪心算法,胃口最大的孩子就需要尺寸最大的饼干,如果没有符合条件的饼干则换胃口第二大的孩子,以此类推。局部最优就是全局最优。知识点贪心心得简单摆动序列摆动序列解题思路通过遍历整个数组找到峰值,峰值则是找到最长的子序列,局部最优就是全......
  • 【算法】删除有序链表中的重复元素、保留重复节点的一个
    1.概述存在一个按升序排列的链表,给你这个链表的头节点head,请你删除所有重复的元素,使每个元素只出现一次。返回同样按升序排列的结果链表。本问题和【算法】删除有序链表中的重复元素、不保留重复节点很类似,但是思考起来稍微简单些,建议看完这个,看链接的这个吧。2.......
  • 基于Python语言的入门算法和数据结构(持续更新中,求关注一波)[链表 栈 队列 复杂度 操作]
    这篇文章主要是讲的Python语言的算法,本人还在不断记笔记中,文章也会持续更新,内容比较浅薄,请大家指教另外推荐一个比较好用的记笔记的软件Typora,自己已经使用很久了,感觉不错。。。虽然但是还是有欠缺。目录第一章算法概述1.1什么是数据结构?01数据结构都有哪些组成方式02......