【C++ 数据结构:链表】二刷LeetCode707设计链表

【C++链表】

使用c++重新写一遍LeetCode707设计链表

目的是熟悉c++中链表的操作

知识点

C++链表节点的实现

在c++中,一般通过结构体来定义链表的节点,也需要写构造函数(使用初始化列表)

如:

struct ListNode{
        int val;
        ListNode* next;
        //要写构造函数
        //结构体中的构造函数要用初始化列表来初始化属性
        ListNode(int val) :val(val), next(nullptr){}
    };

访问节点中的属性遵循结构体指针操作

即利用操作符 -> 可以通过结构体指针访问结构体属性

例如,遍历链表

		while(cur->next != nullptr){
            cur = cur->next;
        }

实现一个链表节点

c++中对于链表的操作均通过指针完成,例如:

//创建一个待插入的节点
ListNode* node4add = new ListNode(val);

上述代码在堆区开辟一块新内存存放一个ListNode对象,将指针node4add指向该区域

内存释放

在c++中操作节点,如果不再需要某个节点,一定要把该节点删除(释放内存)

例如删除节点的操作中,我们将待删除节点的上一个节点指向待删除节点的后一个节点,绕过了待删除节点,实现对该节点的删除

如果是其他语言,就直接操作就行了,在c++中不行

我们还需要将待删除节点保存下来,再删除释放

void deleteAtIndex(int index) {
        if(index < 0 || index >= m_size){
            return;
        }
        //将当前指针指向dummy
        ListNode* cur = m_dummy;
        while(index){
            cur = cur->next;
            index--;
        }
        //要先把待删除的节点用临时节点保存,以便之后进行delete操作
        //如果不删除的话会报错
        ListNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        m_size--;
     
    }

完整代码

class MyLinkedList {

public:
    //要先定义一个结构体作为节点
    struct ListNode{
        int val;
        ListNode* next;
        //要写构造函数
        //结构体中的构造函数要用初始化列表来初始化属性
        ListNode(int val) :val(val), next(nullptr){}
    };

    MyLinkedList() {
        //初始化(定义/实现)链表的头节点和size
        //实际上这两个属于MyLinkedList类的成员属性,由于我们不想其被修改
        //因此可以把它们写在private区
        m_size = 0;
        m_dummy = new ListNode(0);//在堆区开辟一块新内存存放一个ListNode对象,将指针m_dummy指向该区域
    }
    
    int get(int index) {
        if (index > (m_size - 1) || index < 0) {
            return -1;
        }
        //将当前指针指向头节点(即dummy的下一个)
        // ListNode* cur = m_dummy->next;
        ListNode* cur = m_dummy;
        while(index){
            cur = cur->next;
            index--;
        }
        return cur->next->val;//记得返回的是cur的下一个节点,因为最初cur指向的是dummy
        //要么一开始就让cur指向dummy->next
    }
    
    void addAtHead(int val) {
        //创建一个待插入的节点
        ListNode* node4add = new ListNode(val);
        node4add->next = m_dummy->next;
        m_dummy->next = node4add; 
        m_size++;
    }
    
    void addAtTail(int val) {
        //创建一个待插入的节点
        ListNode* node4add = new ListNode(val);
        ListNode* cur = m_dummy;
        //遍历到链表末尾处
        while(cur->next != nullptr){
            cur = cur->next;
        }
        //插入新节点
        cur->next = node4add;
        m_size++;


    }

    // 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
    // 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
    // 如果index大于链表的长度,则返回空
    // 如果index小于0,则在头部插入节点
    void addAtIndex(int index, int val) {
        if(index > m_size){
            return;
        }else if(index < 0){
            index = 0;
        }
        //将当前指针指向dummy
        ListNode* cur = m_dummy;
        //遍历到index位置
        while(index){
            cur = cur->next;
            index--;
        }
        //找到插入位置之后开始插入
        //创建一个待插入的节点
        ListNode* node4add = new ListNode(val);
        node4add->next = cur->next;
        cur->next = node4add;

        //链表长度增加
        m_size++;
    }
    
    void deleteAtIndex(int index) {
        if(index < 0 || index >= m_size){
            return;
        }
        //将当前指针指向dummy
        ListNode* cur = m_dummy;
        while(index){
            cur = cur->next;
            index--;
        }
        //要先把待删除的节点用临时节点保存,以便之后进行delete操作
        //如果不删除的话会报错
        ListNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        m_size--;
     
    }
private:
    int m_size;//声明链表长度
    ListNode* m_dummy;//声明dummy头节点

};

/**
 * 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);
 */

707二刷错误点

1、忘记处理链表size

记得定义链表长度size

2、next的含义

举个例子

 void addAtIndex(int index, int val) {
        if(index > m_size){
            return;
        }else if(index < 0){
            index = 0;
        }
        //将当前指针指向dummy
        ListNode* cur = m_dummy;
        //遍历到index位置
        while(index){
            cur = cur->next;
            index--;
        }
        //找到插入位置之后开始插入
        //创建一个待插入的节点
        ListNode* node4add = new ListNode(val);
        node4add->next = cur->next;
        cur->next = node4add;

        //链表长度增加
        m_size++;

    }

这里node4add->next = cur->next;的意思是:
node4add节点的下一个节点指向cur的下一个节点A,此时cur->next代表的是一个节点

cur->next = node4add;的意思是:

cur的下一个节点指向node4add节点,此时cur->next表示访问cur节点的next属性并对其进行操作

3、get函数,要注意cur的指向

代码注释有说明,在用dummy节点的时候不要搞错返回对象

707三刷错误点

1、在删除指定节点时要记得,cur指针是从dummy开始遍历的,因此需要删除的节点应该是cur->next,而不是cur

2、建议先实现addAtIndex和deleteAtIndex

3、野指针问题

在定义ListNode时,我们会为结构体提供构造函数

struct ListNode{
        int val;
        ListNode* next;
        //要写构造函数
        //结构体中的构造函数要用初始化列表来初始化属性
        ListNode(int val) :val(val), next(nullptr){}
    };

注意,在写构造函数是,最好是用成员初始化列表的方式,并且需要给next进行初始化

posted @ 2023-01-28 22:13  dayceng  阅读(255)  评论(0编辑  收藏  举报