当前位置: 首页 > article >正文

707. 设计链表 链表的知识复习

707. 设计链表

class MyLinkedList {


public:
      struct LinkedNode {
        int val;
        LinkedNode *next;
        LinkedNode(int val):val(val),next(nullptr){}
    };
    MyLinkedList(){
        dummyhead = new LinkedNode(0);
        size=0; 
    }
    
    int get(int index) {
        if(index < 0 || index >= size){
            return -1;
        }
        LinkedNode * cur = dummyhead->next;//注意这里cur指针的指向是哪儿,画图判断逻辑是否是正确的。
        while(index--){
           //初始化要放在外面 ListNode * cur = dummyhead;
            cur = cur->next;
        }
       // return cur->next->val;
        return cur->val;
    }
    
    void addAtHead(int val) {
        LinkedNode * newNode = new LinkedNode(val);
        newNode->next = dummyhead->next;
        dummyhead->next = newNode;
        size++;
        
    }
    
    void addAtTail(int val) {
        LinkedNode *newNode = new LinkedNode(val);
        LinkedNode *cur = dummyhead;
        while(cur->next != nullptr){
            cur= cur->next;
        }
        cur->next = newNode;
        size++;
    }
    
    void addAtIndex(int index, int val) {
        if(index > size || size < 0) return ;
        // if(index == size ){
        //     addAtTail(val);
        //     return;
        // }
        LinkedNode * newNode = new LinkedNode(val);
        LinkedNode *cur=dummyhead;
        while(index--){
            cur=cur->next;
        }
        newNode->next = cur->next;
        cur->next= newNode;
        size++;
    }
    
    void deleteAtIndex(int index) {
        if(index<0||index>= size){
            return;
        }
        LinkedNode*cur=dummyhead;
        while(index--){
            cur= cur->next;
        }
        LinkedNode *temp= cur->next;
        cur->next = cur->next->next;
        delete temp;
        size--;
        temp =nullptr;
    }
private:
int size;
LinkedNode * dummyhead;
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

这三个构造函数在 ListNode 结构体中定义的作用和用法有所不同,它们分别处理了不同的初始化场景。我们来逐一分析这三个构造函数的区别:

1. 默认构造函数

ListNode() : val(0), next(nullptr) {}
  • 作用:这是一个 默认构造函数,用于在不提供任何参数的情况下创建 ListNode 对象。

  • 初始化

    • val(0):将节点的 val 初始化为 0
    • next(nullptr):将节点的 next 指针初始化为 nullptr,意味着该节点不指向任何其他节点。
  • 用途:如果你只是创建一个空的节点(没有指定值或下一个节点),使用这个构造函数非常方便。比如:

    ListNode node; // 创建一个 val 为 0, next 为 nullptr 的节点
    

2. 带一个参数的构造函数

ListNode(int x) : val(x), next(nullptr) {}
  • 作用:这是一个 带一个参数的构造函数,用于在创建节点时指定 val 的值,但不指定 next,即将 next 指向 nullptr

  • 初始化

    • val(x):将节点的 val 初始化为传入的参数 x
    • next(nullptr):将节点的 next 指针初始化为 nullptr,意味着该节点没有连接到其他节点。
  • 用途:当你创建一个节点,并且只关心它的值而不关心它的 next 指针时使用这个构造函数。例如:

    ListNode node(5); // 创建一个 val 为 5, next 为 nullptr 的节点
    

3. 带两个参数的构造函数

ListNode(int x, ListNode* next) : val(x), next(next) {}
  • 作用:这是一个 带两个参数的构造函数,用于在创建节点时同时指定 valnext

  • 初始化

    • val(x):将节点的 val 初始化为传入的参数 x
    • next(next):将节点的 next 指针初始化为传入的参数 next,即指向另一个 ListNode 对象。这个参数通常是指向另一个链表节点的指针,表示该节点的 "下一个" 节点。
  • 用途:当你需要创建一个包含特定值且与其他节点连接的节点时使用。例如:

    ListNode* node3 = new ListNode(3);
    ListNode* node2 = new ListNode(2, node3); // 创建一个 val 为 2 的节点,它的 next 指向 node3
    

    在这个例子中,node2val2next 是指向 node3 的指针,node3val3nextnullptr


总结

  • 默认构造函数:不带任何参数,创建一个 val0nextnullptr 的节点。
  • 带一个参数的构造函数:只传入一个 val 参数,next 默认为 nullptr。常用于创建值已知但不关心 next 的节点。
  • 带两个参数的构造函数:同时传入 valnext,用于创建值已知并且与其他节点连接的节点。

这三种构造函数提供了不同的初始化方式,使得你可以灵活地创建和管理链表节点,特别是在处理链表时。

是的,虚拟头节点(dummy head)会作为链表中的第一个节点,但它的作用并不是作为链表数据的 "实际头节点"。它的存在是为了简化链表的操作,尤其是在插入和删除节点时避免处理空链表或单节点链表时的特殊情况。

虚拟头节点的作用

在链表中,特别是单向链表,在执行插入和删除操作时,往往需要特殊处理头节点,尤其是在头节点是空时。例如:

  • 删除头节点:你需要把头节点的指针指向第二个节点。
  • 插入新节点到头部:你需要更新头节点的指针。

这些操作如果没有虚拟头节点,可能会导致一些复杂的边界条件需要单独处理,增加代码的复杂性。

通过虚拟头节点,简化了这些边界条件的处理:
  • 无论链表是否为空,操作都变得统一。虚拟头节点总是存在的,插入和删除操作都可以直接从虚拟头节点开始,避免了处理空链表或只有一个节点的特殊情况。

虚拟头节点的工作原理

  1. 初始化时

    • MyLinkedList() 构造函数中通过 new LinkedNode(0) 创建一个值为 0 的虚拟头节点,并将 _dummyHead 指向它。
    • 这个节点的 next 指针初始为 nullptr,表示链表为空。
  2. 链表的操作

    • 例如,当你插入一个节点时,不需要检查链表是否为空,只需从虚拟头节点开始。
    • 例如,在插入新节点时(addAtHead),你只需将虚拟头节点的 next 指针指向新节点。
    • 这样,无论链表是空的,还是有节点,代码的逻辑都很简单。

虚拟头节点如何作为第一个节点?

即使虚拟头节点是链表中的第一个节点,它本身并不存储有效数据。它的 val 通常不被使用或不重要,它只是为了简化链表操作而存在。

  • 链表中的第一个有效节点 是紧随虚拟头节点之后的节点。虚拟头节点只是在链表结构中占据一个位置,用来保持链表的一致性和简化操作。

举个例子

假设你通过 addAtHead 函数添加一个新节点 5 到链表中:

list.addAtHead(5);  // 向链表头部添加一个节点,值为 5

在调用 addAtHead(5) 后,链表结构会变成:

虚拟头节点 (val=0) → 新节点 (val=5) → nullptr
  • 这里的虚拟头节点的值 0 是无关紧要的,它的作用只是作为链表的 "头" 节点。
  • 第一个有效的节点是 val=5 的节点,它是链表中的实际数据节点。

如何理解链表中的头节点

  • 链表的头节点(head node)通常指向链表中的第一个有效数据节点。
  • 虚拟头节点(dummy head)是为了方便链表操作而引入的,通常它的值是无关紧要的。

总结

  • 虚拟头节点MyLinkedList 类中是一个特殊的辅助节点,它是链表结构的一部分。
  • 在初始化时,虚拟头节点被创建并赋值给 _dummyHead,但它并不包含有效数据。
  • 链表的第一个有效数据节点 是虚拟头节点的 next 指向的节点。
  • 通过引入虚拟头节点,链表的插入和删除操作变得更简洁、统一。

http://www.kler.cn/a/445312.html

相关文章:

  • 【Tomcat】第六站(最后一站啦!):数据的返回
  • 202412月最新植物大战僵尸杂交版【V3.0.1】更新内容与下载
  • 【Flutter_Web】Flutter编译Web第二篇(webview篇):flutter_inappwebview如何改造方法,变成web之后数据如何交互
  • Linux crontab 使用教程
  • YOLOv8全解析:高效、精准的目标检测新时代——创新架构与性能提升
  • 【原生js案例】前端封装ajax请求及node连接 MySQL获取真实数据
  • 【前端面试】三次握手/http/https,是否跳转携带cookie,跨域
  • C 语言: sizeof 运算符深度解析
  • 【PGCCC】Postgresql Varlena 结构
  • bicycle 和cycle区别及使用场景
  • 线上虚拟展厅支持哪些类型的素材添加?
  • 农村的PCDN
  • Mysql语法之DQL查询的多行函数
  • 电子应用设计方案-62:智能鞋柜系统方案设计
  • ChromeOS 131 版本更新
  • * 和 .* 的区别(MATLAB)
  • redis数据类型:list
  • SpringCloud无介绍快使用,sentinel注解@SentinelResource的基本使用(二十三)
  • HTTP 常见的请求头有哪些? 作用?常见的使用场景都有哪些?
  • python 中使用pip操作flask离线下载(包含依赖包下载)和安装
  • 排序概述及Python实现
  • 玩转OCR | 探索腾讯云智能结构化识别新境界
  • Deepin/Linux clash TUN模式不起作用,因网关导致的问题的解决方案。
  • 智能座舱进阶-应用框架层-Jetpack主要组件
  • Python 爱心代码实现动态爱心图案展示
  • Elasticsearch8.17.0在mac上的安装