Day03|链表01:203.移除链表元素、707.设计链表、206.反转链表

今天进入链表章节的学习了,也是之前学过的内容,这次争取快速AC。

203.移除链表元素

leetcode链接:https://leetcode.cn/problems/remove-linked-list-elements/

题意:删除链表中等于给定值 val 的所有节点。

示例 1: 输入: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 输出:[]

没什么好说的,这里注意引入了一个虚拟头节点dummy,这样就不用处理需要删除第一个节点的特殊情况。删除时C++需要手动detete。我本来使用free的,后来发现两者其实也是有区别的:详见文章new和malloc,delete和free的区别

new的底层也是通过malloc来开辟内存的,new比malloc多一项功能,就是开辟完内存,还可以进行初始化:

int *p=new int(10);

Test *p=new Test();

第一条语句是new的基本操作,10代表开辟整型内存的初始值。

第二条语句会在堆上开辟Test类型的一块内存,同时构造出一个对象。

以上两条malloc均办不到。

new开辟内存失败抛出bad_alloc类型的异常,要捕获异常才能判断内存是否分配成功。而malloc内存开辟失败返回的是NULL指针。

再谈谈delete和free:

delete比free多一项功能,就是不仅可以释放内存,还可以析构指针指向的对象。

还有一点要补充:

newdelete不仅仅是运算符,它们实际上是运算符重载函数的调用,对应的函数名是operator newoperator delete,可以在全局或者类的作用域中提供自定义的newdelete运算符的重载函数,以改变默认的malloc和free内存开辟释放行为。

然后删除的顺序,先让前一个节点指向要删除节点的后一个节点,再把要删的节点delete掉

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode *first = new ListNode(0);
        first->next = head;//虚拟头节点
        ListNode *p = first;//用于保存前节点的指
        while (p->next != NULL) {
            if (p->next->val == val) {
                ListNode *tmp = p->next;
                p->next = p->next->next;
                delete tmp; //这里不是free(p);

            } else {
                p = p->next;
            }
        }
        return first->next;
    }
};

707.设计链表

题目链接:力扣题目链接(opens new window)

这题难度不大,主要是链表基本操作的实现。重点如下:

  • 手撸链表的数据结构,我之前学的是C语言版,对于struct的比较熟悉,但C++需要补充一个构造函数和初始化函数,这样才可以new出对象。
  • 这个class的调用方式是这样的:

Day03|链表01:203.移除链表元素、707.设计链表、206.反转链表_第1张图片

这里使用new出一个MyLinkedList而不是我们自己创建的ListNode。然后使用其中的方法,简单来说就是包装吧。

class MyLinkedList {
public:
    struct ListNode{
        int val;
        ListNode* next;
        ListNode(int x):val(x),next(NULL){};//构造函数
    };
    //初始化链表,
    MyLinkedList() {
        _dummy = new ListNode(0);
        _size = 0;
    }
    
    int get(int index) {
        if(index >= _size || index < 0){
            return -1;
        }
        ListNode* cur = _dummy->next;//这里指向的是_dummy->next
        while(index--){
            cur = cur->next;
        }
        return cur->val;
    }
    
    void addAtHead(int val) {
        ListNode* p = new ListNode(val);
        p->next = _dummy->next;
        _dummy->next = p;
        _size++;
    }
    
    void addAtTail(int val) {
        ListNode* p = new ListNode(val);
        ListNode* cur = _dummy;
        while(cur->next != nullptr){
            cur = cur->next;
        }
        cur->next = p;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
        ListNode *p = new ListNode(val);
        ListNode* cur = _dummy;//这里指向_dummy,因为我们要的效果是循环结束后cur停在对于节点的前面
        if(index > _size){
            return;//index大于链表长度,不插入
        }
        while(index--){
            cur = cur->next;
        }
        p->next = cur->next;
        cur->next = p;
        _size++;
    }
    
    void deleteAtIndex(int index) {
        if(index >= _size || index < 0){
            return;
        }
        ListNode* cur = _dummy;
        while(index--){
            cur = cur->next;
        }
        ListNode* temp = cur->next;
        cur->next = cur->next->next;
        delete temp;
        _size--;
    }
private:
    ListNode* _dummy;
    int _size;
};

206.反转链表

题目链接:力扣题目链接(opens new window)

题解:题解

Day03|链表01:203.移除链表元素、707.设计链表、206.反转链表_第2张图片

这里使用双指针,用pre节点保存cur的前一个节点,注意顺序:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
//        ListNode* dummy = new ListNode(0);
//        dummy->next = head;
        ListNode* pre = nullptr;
        ListNode* cur = head;
        while(cur != nullptr){
            ListNode* tmp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
};

这里不需要创建虚拟头节点了,因为对head节点的处理是一样的,不需要特殊处理。

递归法没怎么看,今天有点忙,有时间再看,感觉跟双指针的思路是一样的。

你可能感兴趣的:(链表,数据结构)