代码随想录算法训练营第3天 | 203.移除链表元素 707.设计链表 206.反转链表

代码随想录算法训练营第3天 | 203.移除链表元素 707.设计链表 206.反转链表


3 第二章 链表

day1 任务以及具体安排:训练营一期day 1

day 2 任务以及具体安排:day 2 第一章数组

今日任务

● 链表理论基础

● 203.移除链表元素

● 707.设计链表

● 206.反转链表

详细布置

链表理论基础

建议:了解一下链接基础,以及链表和数组的区别

文章链接:代码随想录

203.移除链表元素

【链接】(文章,视频,题目)

建议: 本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。

题目链接/文章讲解/视频讲解::代码随想录

【第一想法与实现(困难)】暴力实现,但是runtime error,估计是哪里判断的空指针不对,

【看后想法】

  • 原链表移除。

    • 需要考虑头结点的特殊情况。头结点删掉之后,会有新的头结点需要检查,因此需要一直检查头结点的删除,要用while不是if。

    • 注意因为删除某结点,需要删去指向这个结点的指针。实际上每次删除的只能是cur->next,也就是修改cur的next指针。

    • 删除非头结点之后,cur->next就更新了,因此再次判断cur->next->val,而cur不需要后移

    • 使用某个指针的字段之前,都要检查指针自身非空(因为总是可能有某个后移操作,就到了空指针,导致runtime error)

    • c++实现要手动delete删除掉的结点

  • 虚拟头结点

    • 手动生成与释放一个链表节点的语法,ListNode* dummy_head = new ListNode(0, head);

    • 返回的head是虚拟头结点的后继。dummy_head->next

    • 最后要将虚拟头结点delete掉

【实现困难】

【自写代码】

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        // 原链表移除
        while (head != nullptr && head->val == val) {
            ListNode* ori_head = head;
            head = head->next;
            delete ori_head;
        }
        ListNode* cur = head;
        while (cur != nullptr && cur->next != nullptr) {
            if (cur->next->val == val) {
                ListNode* next_node_del = cur->next;
                cur->next = cur->next->next;
                delete next_node_del;
            } else {
                cur = cur->next;
            }
        }
        return head;
    }
};
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        // 虚拟头结点
        ListNode* dummy_head = new ListNode(0, head);
        ListNode* cur = dummy_head;
        while (cur != nullptr && cur->next != nullptr){
            if (cur->next->val == val) {
                ListNode* next_node_del = cur->next;
                cur->next = cur->next->next;
                delete next_node_del;
            } else {
                cur = cur->next;
            }
        }
        head = dummy_head->next;
        delete dummy_head;
        return head;
    }
};

【收获与时长】

707.设计链表

【链接】(文章,视频,题目)

建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点

题目链接/文章讲解/视频讲解:代码随想录

【第一想法与实现(困难)】

  • 由于有头尾操作,想用双链表,加上一个size_维护内部大小。

  • 自己定义了双链表结点MyListNode,及其构造函数

  • 问题:

    • runtime error, 问题在于,虚拟头,虚拟尾是指针,在初始化要手动new,否则永远是空指针

    • 如何在class中调用自定义的struct,将结构体放里面外面均可

【看后想法】

  • 随想录的参考代码是使用单链表,我可以自己在实现一次单链表,ok

  • 内部自己增加一个打印链表函数void printLinkedList(),方便debug

【实现困难】

  • 对于单链表,找index前一个节点的方法,while(index–)很巧妙。

  • addAtIndex,index0,indexsize_的可以融合到一起,不用再单独写。

【自写代码】

class MyLinkedList {
public:
    // 单链表
    // - 对于单链表,找index前一个节点的方法,while(index--)很巧妙。
    // addAtIndex,index==0,index==size_的可以融合到一起,不用再单独写。
    struct MyListNode {
        int val;
        MyListNode* next;
        MyListNode() : val(0), next(nullptr) {}
        MyListNode(int x) : val(x), next(nullptr) {}
        MyListNode(int x, MyListNode* n) : val(x), next(n) {}
    };
    MyLinkedList() : dummy_head_(new MyListNode()), size_(0) {}
    
    int get(int index) {
        if (index < 0 || index > size_ - 1) {
            return -1;
        }
        MyListNode* cur = dummy_head_;
        for (int i = 0; i <= index; ++i) {
            cur = cur->next;
        }
        return cur->val;
    }
    
    void addAtHead(int val) {
        MyListNode* new_node = new MyListNode(val, dummy_head_->next);
        dummy_head_->next = new_node;
        size_++;
    }
    
    void addAtTail(int val) {
        MyListNode* new_node = new MyListNode(val, nullptr);
        MyListNode* cur = dummy_head_;
        while (cur->next != nullptr) {
            cur = cur->next;
        }
        cur->next = new_node;
        size_++;
    }
    
    void addAtIndex(int index, int val) {
        if (index > size_) {
            std::cout << "addAtIndex error, index:" << index << std::endl;
            return;
        }
        if (index < 0) {
            index = 0;
        }
        MyListNode* cur = dummy_head_;
        while (index--) {
            cur = cur->next;
        }
        // 在cur后增加
        MyListNode* new_node = new MyListNode(val, cur->next);
        cur->next = new_node;
        size_++;
    }
    
    void deleteAtIndex(int index) {
        if (index < 0 || index > size_ - 1) {
            std::cout << "deleteAtIndex error, index:" << index << std::endl;
            return;
        }
        MyListNode* cur = dummy_head_;
        while (index--) {
            cur = cur->next;
        }
        // 在cur后删除
        MyListNode* del_node = cur->next;
        cur->next = cur->next->next;
        delete del_node;
        size_--;
    }
private:
    MyListNode* dummy_head_;
    int size_;
};

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

class MyLinkedList {
public:
    // 双链表
    struct MyListNode {
        int val;
        MyListNode* prev;
        MyListNode* next;
        MyListNode(): val(0), prev(nullptr), next(nullptr) {}
        MyListNode(int x): val(x), prev(nullptr), next(nullptr) {}
        MyListNode(int x, MyListNode* p, MyListNode* n): val(x), prev(p), next(n) {}
    };

    MyLinkedList(): dummy_head_(new MyListNode()), dummy_tail_(new MyListNode()), size_(0) {
        dummy_tail_->prev = dummy_head_;
        dummy_head_->next = dummy_tail_;
    }

    int get(int index) {
        if (index >= 0 && index < size_) {
            MyListNode* cur = dummy_head_;
            for (int i = 0; i <= index; ++i) {
                cur = cur->next;
            }
            return cur->val;
        } else {
            return -1;
        }
    }
    
    void addAtHead(int val) {
        MyListNode* head = dummy_head_->next;
        MyListNode* new_head = new MyListNode(val, dummy_head_, head);
        dummy_head_->next = new_head;
        head->prev = new_head;
        size_++;
    }
    
    void addAtTail(int val) {
        MyListNode* tail = dummy_tail_->prev;
        MyListNode* new_tail = new MyListNode(val, tail, dummy_tail_);
        tail->next = new_tail;
        dummy_tail_->prev = new_tail;
        size_++;
    }
    
    void addAtIndex(int index, int val) {
        if (index < 0) {
            addAtHead(val);
            return;
        }
        if (index == size_) {
            addAtTail(val);
            return;
        }
        if (index > size_) {
            std::cout << "addAtIndex, wrong index:" << index << std::endl;
            return;
        }
        MyListNode* cur = dummy_head_;
        for (int i = 0; i <= index; ++i) {
            cur = cur->next;
        }
        MyListNode* prev = cur->prev;
        MyListNode* new_node = new MyListNode(val, prev, cur);
        prev->next = new_node;
        cur->prev = new_node;
        size_++;
    }
    
    void deleteAtIndex(int index) {
        if (index < 0 || index >= size_) {
            std::cout << "deleteAtIndex, wrong index:" << index << std::endl;
            return;
        }
        MyListNode* cur = dummy_head_;
        for (int i = 0; i <= index; ++i) {
            cur = cur->next;
        }
        cur->prev->next = cur->next;
        cur->next->prev = cur->prev;
        delete cur;
        size_--;
    }
private:
    MyListNode* dummy_head_;
    MyListNode* dummy_tail_;
    int size_;
};

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

【收获与时长】2-3小时

  • 指针,在初始化要手动new,否则永远是空指针。析构要手动delete

  • 单双链表实现

  • 类class中调用结构体struct

  • 对于单链表,找index前一个节点的方法,while(index–)很巧妙

206.反转链表

【链接】(文章,视频,题目)

建议先看我的视频讲解,视频讲解中对 反转链表需要注意的点讲的很清晰了,看完之后大家的疑惑基本都解决了。

题目链接/文章讲解/视频讲解:代码随想录

【第一想法与实现(困难)】暴力,用vector存起来。好像没啥困难,就是笨办法

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        // 暴力,用vector存起来
        std::vector<int> vec;
        ListNode* cur = head;
        while (cur != nullptr) {
            vec.emplace_back(cur->val);
            cur = cur->next;
        }
        cur = head;
        for (int i = vec.size() - 1; i >= 0 ; --i, cur = cur->next) {
            cur->val = vec.at(i);
        }
        return head;
    }
};

【看后想法】

  • 递归方法与双指针方法的while循环,有对应关系。

    • 递归结束条件对应while循环结束条件

    • 递归返回值对应while循环结束返回值

    • 递归函数的变量变化,对应while循环一轮的变量变化

【实现困难】

  • 双指针。注意while循环结束条件

  • 递归。与双指针的对应关系。结束条件,返回值,变化关系

  • 从后往前的递归。从后往前的递归。0,1,…,k,…,m。从后往前假设k+1~m已经完成反转,每一步只完成k, k+1的反转。也就是k+1指向k。同时k要指向空。

【自写代码】

【收获与时长】

3小时,有点久了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值