day03| 203.移除链表元素, 707.设计链表, 206.反转链表

203.移除链表元素

leetcode链接

题意:删除链表中等于给定值 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 输出:[]

这个题就是考察链表的操作。我比较喜欢使用虚拟头节点进行操作,因为如果直接使用原链表操作,需要单独写一段逻辑来处理移除头结点的情况。所以直接使用原来链表进行操作的方法我也没看,如果不存在必须使用原链表进行操作的情况,以后我都用虚拟头节点进行操作。

链表操作的两种方式:

1.直接使用原来的链表来进行删除操作。
2.设置一个虚拟头结点在进行删除操作。

要这个tmp干什么,下面不是没用上就删掉了吗。
嗷,懂了,因为tmp指向的是地址,并没有新建空间,所以加上tmp是为了释放删除元素的空间。这是一个好习惯。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* cur = dummyHead;
        while(cur->next != NULL){//记住这里的条件是cur的next不为空,而不是cur的当前不为空
            if(cur->next->val == val){
                ListNode* tmp = cur->next;//要这个tmp干什么,下面不是没用上就删掉了吗
                cur->next = cur->next->next;
                delete tmp;
            }else{
                cur = cur->next;
            }
        }
        head = dummyHead->next;//dummyHead是虚拟节点,虚拟节点的下一个才是原本真正的头节点
        delete dummyHead;
        return head;
    }
};

707.设计链表

leetcode链接

题意:

在链表类中实现这些功能:

get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。 addAtIndex(index,val):在链表中的第
index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index
大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。 deleteAtIndex(index):如果索引 index
有效,则删除链表中的第 index 个节点。
day03| 203.移除链表元素, 707.设计链表, 206.反转链表_第1张图片

说实话这个题好几个函数都没法看到输出结果,只有null。而我又是直接在leetcode编辑的,在写的过程中不怎么好看到底写得对不对。

这个题的关键感觉是先写好表节点的结构体,然后初始化好,并确定使用虚拟头节点的方式写。这次我就是只抄了节点定义部分,然后接口部分我基本上都能写出来。无非就是定义p(新节点)和定义cur(用于遍历的节点),然后就是while里面搞循环走cur,接下来就是各种next,都还行,会增基本上也会删。
但是,但是有好几个细节差点把我搞哭了,一开始自己写,判题不正确,一个一个对应作者的题解看,找了好久。
细节大概有那么几个,都代码的注释中了。还没练熟和理解透,这里先不总结。反正位置都标记在下面的注释里了。
这次只写了单节点。【双节点后面补】

class MyLinkedList {
public:
    // 定义链表节点结构体
    struct LinkedNode {
        int val;
        LinkedNode* next;
        LinkedNode(int val):val(val), next(nullptr){}//nullptr是空指针关键字
    };

    // 初始化链表
    MyLinkedList() {
        _dummyHead = new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
        _size = 0;
    }
    
    int get(int index) {
        //这个边界条件如果不写就会超时,但是总感觉这种条件很烦
        if (index > (_size - 1) || index < 0) {
            return -1;
        }
        LinkedNode* cur = _dummyHead->next;
        while(index--){
            cur = cur->next;
        }
        return cur->val;
    }
    
    void addAtHead(int val) {
        LinkedNode* p = new LinkedNode(val);
        p->next = _dummyHead->next;
        _dummyHead->next = p;
        _size++;
    }
    
    //这个接口好像就是遍历一下链表,作者在while里面写的
    //终止条件是cur->next != nullptr,确实,但是我用size写应该也没啥问题。
    //嗷不行,后面有用例因为while终止条件报错了,估计是什么边界值有问题,现在改过来了。
    void addAtTail(int val) {
        LinkedNode* p = new LinkedNode(val);
        // LinkedNode* cur = _dummyHead->next;
        LinkedNode* cur = _dummyHead;
        // int i = _size - 1;
        while(cur->next != nullptr){
            cur = cur->next;
        }
        cur->next = p;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
        if(index < 0){
            index = 0;
        }
        if(index > _size) return;

        LinkedNode* p = new LinkedNode(val);
        // 下面注释的是出错的地方,为什么这里不是用_dummyHead->next而是用_dummyHead了?
        // 不是说_dummyHead->next才是标号0吗?
        // LinkedNode* cur = _dummyHead->next;
        LinkedNode* cur = _dummyHead;
        while(index--){
            cur=cur->next;
        }
        p->next = cur->next;
        cur->next = p;
        _size++;
    }
    
    void deleteAtIndex(int index) {
        //我之前写的条件是if(index < 0 || index > _size),没有等于号,
        //这里没有等于号在有些用例里面也会报错,天...
        if(index < 0 || index >= _size){
            return;
        }
        // 下面注释的是出错的地方
        // LinkedNode* cur = _dummyHead->next; 
        LinkedNode* cur = _dummyHead;
        while(index--) {
            cur = cur ->next;
        }
        LinkedNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        _size--;
    }

private:
    int _size;//确实有一个size记录链表长度比较好,但是要记得增删操作的时候进行维护
    LinkedNode* _dummyHead;
};

206.反转链表

leetcode链接

题意:反转一个单链表。

示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

虽然属于简单题,我看着题目也觉得挺简单。只是卡住我的地方是,单链表遍历只能从前面往后面一个一个遍历,现在要翻转过来,怎么翻,从后往前不是没法遍历吗?

看到代码随想录的第一个动画我悟了,双指针,试试。

写出来了,开心!!虽然这个题不需要虚拟头节点,cur直接等于head就可以,但是为了保持良好习惯,我就还是把虚拟头节点写上,也就多两三行代码。另外要记得虚拟头节点如下的定义方式,要先new一下,然后再next等于head。因为刚刚我错写成"ListNode* dummyHead = head;"了。

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

另外这个题好像还有递归写法,也是从前往后的,和双指针差不多。还有一种从后往前的。【这两个解法还没看】


参考资料

代码随想录

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