算法训练营Day3|203.移除链表元素,707.设计链表,206.反转链表

算法训练营Day3

链表理论基础,203.移除链表元素,707.设计链表,206.反转链表

题目

203.移除链表元素

题目链接:https://leetcode.cn/problems/remove-linked-list-elements/
文章讲解:https://programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html

思路

链表基础题
迭代:使用虚拟头节点,这样可以不用额外考虑删除头节点的情况(把头节点视作普通节点),假设有三个节点,那么删除第二个节点时需要让第一个节点的next指向第三个节点,所以指针应该指向第一个节点才能完成这个操作,所以每次判断p->next是否需要删除,如果p->next 为NULL则表示已经遍历完成(最后一个节点在p还没有指向该节点之前已经完成了判断)
刚开始犯了一个错误,再每次判断是否为val之后都将指针移动一位,没有考虑到删除节点之后仍然需要判断这个节点的值是否为val(已经是新节点)
递归:自己没有想出来,看了题解
递归函数返回的是不包含val值的链表的头节点,递归的终止条件是 head 为空,先判断除了头节点之外的值是否为val,最后判断头节点

代码
//迭代
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode *dummy = new ListNode(0, head); //虚拟头节点
        ListNode *p = dummy; 
        while(p -> next != nullptr){
            if(p -> next -> val == val) p -> next = p -> next -> next;
            else p = p -> next;
        }
        return dummy -> next; //返回头节点
    }
};
//递归
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        if(head == nullptr) return head;
        head -> next = removeElements(head -> next, val);
        return head -> val == val? head -> next : head;
    }
};

707.设计链表

题目链接:https://leetcode.cn/problems/design-linked-list/
文章讲解:https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html

思路

链表模拟题
最容易忘记的点是size没有及时更新,size++,size–
链表初始化时需要生成虚拟头节点方便后续处理,要注意index的范围,插入和删除时,cur指针要指向插入和删除位置的前一个节点,可以从下标为0的位置来思考cur初始指向dummy还是dummy->next

代码
class MyLinkedList {
private:
    int size;
    ListNode* dummy;
public:
    MyLinkedList() {
        size = 0;
        dummy = new ListNode(0, nullptr);
    }
    void printList() {
        ListNode* p = dummy->next;
        while (p != nullptr) {
            cout << p->val << ' ';
            p = p->next;
        }
        cout << endl;
    }
    int get(int index) {
        if (index < 0 || index > size - 1)
            return -1;
        // 以下情况index一定在[0,size - 1]范围内
        ListNode* cur = dummy->next; // 考虑到第0位不用移动
        while (index--) {
            cur = cur->next;
        }
        return cur->val;
    }

    void addAtHead(int val) { addAtIndex(0, val); }

    void addAtTail(int val) { addAtIndex(size, val); }

    void addAtIndex(int index, int val) {
        // 考虑需要指向要插入位置的前一个节点
        if (index > size)
            return;
        else if (index < 0)
            index = 0;
        ListNode* cur = dummy;
        while (index--) {
            cur = cur -> next;
        }
        ListNode* newNode = new ListNode(val, cur-> next);
        cur -> next = newNode;
        size ++;
    }

    void deleteAtIndex(int index) {
        if (index < 0 || index > size - 1)
            return;
        ListNode* cur = dummy;
        while (index --) {
            cur = cur->next;
        }
        cur -> next = cur -> next -> next;
        size --;
    }
};

206.反转链表

题目链接:https://leetcode.cn/problems/reverse-linked-list/
文章讲解:https://programmercarl.com/0206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.html

思路

画图理解
双指针:从前往后遍历,让后一个节点指向前一个节点 ,pre从空节点开始,cur从第一个节点开始,当cur为空时遍历结束;注意让虚拟头指针不断指向新的头节点,还又一个易错点是cur=temp,不能cur=cur->next,因为next已经被修改过了
递归:函数返回的是链表反转后的头节点,迭代可以从一直压栈到栈底之后弹栈来思考

代码
//双指针
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode *dummy = new ListNode(0, head);
        ListNode *pre = nullptr, *cur = head;
        //每次操作是让cur指向pre,所以要保证cur不为NULL
        while(cur != nullptr){
            ListNode *temp = cur -> next; //保存cur的下一个节点,防止丢失
            cur -> next = pre;
            dummy -> next = cur; //dummy指向第一个节点,不断更新
            pre = cur;
            cur = temp; //这里不能cur=cur->next,因为next已经修改过了
        }
        return dummy -> next;
    }
};
//递归
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == nullptr || head -> next == nullptr) //可能链表为空,所以有head==nullptr的情况
            return head;
        //栈到最后一个节点,会返回最后一个节点,然后弹栈到倒数第二个节点,让倒数第二个节点实现反转链表,以此类推
        ListNode *newHead = reverseList(head -> next); 
        head -> next -> next = head; //实现反转链表,后一个节点指向前一个节点
        head -> next = nullptr;
        return newHead;
    }
};

补充内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值