第三天|203.移除链表元素 707.设计链表 206.反转链表

第三天|203.移除链表元素 707.设计链表 206.反转链表

203.移除链表元素

203.移除链表元素 | 代码随想录

手把手带你学会操作链表 | LeetCode:203.移除链表元素_哔哩哔哩_bilibili

笔记

如果没有虚拟头节点,删除操作要分情况,分为头节点和非头节点。

使用虚拟头节点的方法能够让删除/添加操作统一化。

c++需要从内存中释放那个被删除的结点。

使用cur遍历的时候,我们删除的对象一定是cur->next ,因为我们需要删除那个结点的前一个结点地址

返回的时候应该是dummyHead->next ,因为可能头节点已经被删除掉了。

实操出现问题:

newdelete

注意ListNode* dummyHead = new ListNode(0) ,要新开辟内存。

代码/对比

我自己的:

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* dummyhead=new ListNode(0);//虚拟头节点
        dummyhead->next=head;
        auto cur=dummyhead;//从虚拟头节点的下一个结点(原本链表的头节点)开始遍历,也就是说,当前遍历的那个节点是cur->next而不是cur
        while(cur->next!=nullptr)//循环条件:处理的那个结点非空
        {
            if(cur->next->val==val)
            {
                cur->next=cur->next->next;
            }
            else
                cur=cur->next;
        }
        return dummyhead->next;
        
    }
};

其他:

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
        dummyHead->next = head; // 将虚拟头结点指向head,这样方便后面做删除操作
        ListNode* cur = dummyHead;
        while (cur->next != NULL) {
            if(cur->next->val == val) {
                ListNode* tmp = cur->next;
                cur->next = cur->next->next;
                delete tmp;
            } else {
                cur = cur->next;
            }
        }
        head = dummyHead->next;
        delete dummyHead;
        return head;
    }
};

注意删除不必要的节点和虚拟头节点

707.设计链表

帮你把链表操作学个通透!LeetCode:707.设计链表_哔哩哔哩_bilibili

707.设计链表 | 代码随想录

笔记

注意index 的起始位置。

添加node的时候注意先后顺序。

实操出现的问题

循环中修改了_size的大小,注意不要修改类似敏感参数。

代码/对比

我自己的:

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-1))//如果index无效
        {
            return -1;
        }
        LinkedNode* cur=_dummyHead;//cur->next表示当前遍历的值
        while(index--)
        {
            cur=cur->next;
        }
        return cur->next->val;
    }
    
    void addAtHead(int val) {
        LinkedNode* node=new LinkedNode(0);//创建一个新的节点
        node->val=val;
        node->next=_dummyHead->next;
        _dummyHead->next=node;
        _size++;//注意要自增
    }
    
    void addAtTail(int val) {
        //先找到最后一个节点
        LinkedNode* cur=_dummyHead;
        while(cur->next!=nullptr)
        {
            cur=cur->next;
        }//此时cur指向最后一个节点
        //再添加节点
        LinkedNode* node=new LinkedNode(0);
        node->next=nullptr;
        node->val=val;
        cur->next=node;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
        if(index>_size)//如果index无效
        {
            return;
        }
        if(index==_size)//如果等于长度,则调用addAtTtail
        {
            addAtTail(val);
            return;
        }
        if(index<0)//如果小于0则在开头加;
        {
            index=0;
        }
        //找到index
        LinkedNode* cur=_dummyHead;//cur->next表示当前遍历的值
        while(index--)
        {
            cur=cur->next;
        }
        //插入新的节点
        LinkedNode* node=new LinkedNode(0);
        node->val=val;
        node->next=cur->next;
        cur->next=node;
        //size自增
        _size++;
    }
    
    void deleteAtIndex(int index) {
        if(index<0||index>_size-1)//如果index无效
        {
            return;
        }
        //找到index
        LinkedNode* cur=_dummyHead;//cur->next是当前遍历的值
        while(index--)
        {
            cur=cur->next;
        }
        //删除cur->next
        LinkedNode* temp=cur->next;
        cur->next=cur->next->next;
        delete temp;
        temp=nullptr;
        //size自减
        _size--;

    }
    private:
    int _size;
    LinkedNode* _dummyHead;
};

206.反转链表

206.反转链表 | 代码随想录

帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法_哔哩哔哩_bilibili

笔记

双指针法:

一个pre一个cur指针,遍历完全整个链表,对逐个指针翻转指向即可。

注意循环条件为while(cur)而不是while(pre),因为最后需要一个头指针,最后返回的pre就是那个头指针。

递归法:

递归复习:

递归的基本思想是某个函数直接或者间接地调用自身,这样原问题的求解就转换为了许多性质相同但是规模更小的子问题。求解时只需要关注如何把原问题划分成符合条件的子问题,而不需要过分关注这个子问题是如何被解决的。

递归的三个注意点:

1.函数要干什么?

2.递归的停止条件?

3.怎么把大问题转换为小问题?

对于这个问题:

1.函数是为了反转链表,大体逻辑和上边的双指针方法相同;

2.递归的停止条件是while(cur)

3.怎么把大问题转换为小问题:cur指针和pre指针往前移动。

实操出现问题

双指针法:一定要注意循环条件

代码/对比

双指针:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* pre=nullptr;
        ListNode* cur=head;
        while(cur)//直到pre指针为空,停止
        {
            ListNode* temp=cur->next;
            cur->next=pre;
            pre=cur;
            cur=temp;
        }
        return pre;
    }
};

递归:

我自己的:

class Solution {
public:
    ListNode* reverse(ListNode* pre, ListNode* cur){
        //停止条件:
        if(cur==nullptr)
        {
            return pre;
        }
        else//怎么把大问题转换为小问题:方向反转、pre和cur指针后移
        {
            ListNode* tmp=cur->next;
            cur->next=pre;
            pre=cur;
            cur=tmp;
            return reverse(pre,cur);
        }
        }
    ListNode* reverseList(ListNode* head) {
        return reverse(nullptr,head);
    }
};

随想录上面的:

class Solution {
public:
    ListNode* reverse(ListNode* pre,ListNode* cur){
        if(cur == NULL) return pre;
        ListNode* temp = cur->next;
        cur->next = pre;
        // 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
        // pre = cur;
        // cur = temp;
        return reverse(cur,temp);
    }
    ListNode* reverseList(ListNode* head) {
        // 和双指针法初始化是一样的逻辑
        // ListNode* cur = head;
        // ListNode* pre = NULL;
        return reverse(NULL, head);
    }

};

注意return那块可以直接用cur和tmp;

posted @ 2026-01-16 20:18  欧尼酱ovo  阅读(8)  评论(0)    收藏  举报