(补)代码随想录打卡Day03| LeetCode 203; LeetCode 707; LeetCode 206

今天开始关于链表的题目:

第一题、LeetCode 203 删除链表中的元素​​​​​​https://leetcode.cn/problems/remove-linked-list-elements/​​​​​​

链表中不能用头节点来遍历,否则头结点的指针一直在变,最后无法返回头节点,需要定义一个临时的指针来遍历整个列表。

方法一是直接删除。此方法需要判断是否是头节点,其次要注意在判断中用while而不是if,因为删除一个头节点会出现一个新的头节点。

此外,我们在删除时不是直接对cur进行操作,而是对cur的下一个节点进行操作,因为我们需要把删除节点的前后节点连起来,cur为需删除结点的话在单链表中找不到其上一个节点。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        while(head != NULL && head->val == val){ //这里使用while是因为每一个头节点被删除之后,
        //下一个节点就成为新的头节点,需要循环遍历。判断非空是因为操作空指针会报错、
        ListNode *tmp = head;
        head = head->next;
        delete tmp;
        }
        ListNode *cur = head;
        while(cur != NULL && cur->next != NULL){
            if(cur->next->val == val){
                ListNode *tmp = cur->next;
                cur->next = cur->next->next;
                delete tmp;
            }
            else{
                cur = cur->next;
            }
        }
        return head;
    }
};

第二种方法是设置虚拟头节点,这样不需要判断是否为头节点。只需要最后把链表的头节点设置回去,并删除虚拟头节点。

注意不要直接return head,因为原来的head可能已经被删了。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode *dummyhead = new ListNode(0);
        dummyhead->next = head;
        ListNode *cur = dummyhead;
        while(cur->next != NULL){ //要删除的是肯定不会是虚拟头节点,所以从其下一个开始判断是否为空
            if(cur->next->val == val){
                ListNode *tmp = cur->next;
                //dummyhead->next = cur->next->next;
                //这里是cur在更新
                cur->next = cur->next->next;
                delete tmp;
            }
            else{
                cur = cur->next;
            }
        }
        //return dummyhead->next;
        head = dummyhead->next;
        delete dummyhead;
        return head;
    }
};

第二题,LeetCode707 设计链表 https://leetcode.cn/problems/design-linked-list/

设计一个链表需要注意的点比较多,C++中需要注意private里面的size和虚拟链表头。

虚拟表头的妙处:虚拟头节点让你的cur等于需要删除结点的前一个节点,使得删除的操作更加简单。

注意的点:

1、获得第n个几点的值,注意判断index时边界的问题。

2、加表头:要先设置加入节点的next,否则就找不到next了(先设置前一个结点的next的话)

3、加表尾:定义一个新节点时,默认其next是NULL。

4、在第index个节点前加:需要遍历到其前一个节点(体现了虚拟表头的妙处),并同时注意2。

5、删除节点:体现虚拟表头的妙处。

class MyLinkedList {
public:
    struct LinkedNode{
        int val;
        LinkedNode *next;
        LinkedNode(int val):val(val), next(nullptr){};
    };

    MyLinkedList() {
        dummyhead = new LinkedNode(0);//和private相对应
        _size = 0;
    }
    
    int get(int index) {
        LinkedNode *cur = dummyhead->next;
        if(index >= 0 && index <= (_size-1)){//注意边界条件
            while(index--){
                cur = cur->next;
            }
            return cur->val;
        }
        else{
            return -1;
        }
        // LinkedNode* cur = dummyhead->next;
        // if (index > (_size - 1) || index < 0) {
        //     return -1;
        // }
        // while(index--){ // 如果--index 就会陷入死循环
        //     cur = cur->next;
        // }
        // return cur->val;

    }
    
    void addAtHead(int val) {
        LinkedNode *newnode = new LinkedNode(val);
        //dummyhead->next = newnode;要先设置新加入节点的next,否则就找不到dummyhead的next了
        newnode->next = dummyhead->next;
        dummyhead->next = newnode;
        _size++;

    }
    
    void addAtTail(int val) {
        //需要让遍历的节点是目前链表的尾巴
        LinkedNode *newnode = new LinkedNode(val);
        //在定义newnode的时候,默认其next指向NULL
        LinkedNode *cur = dummyhead;
        while(cur->next != NULL){
            cur = cur->next;
        }
        cur->next = newnode;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
        // if (index >= _size || index < 0) {
        //     return;
        // }
        //注意题目要求
        if(index > _size) return;
        if(index < 0) index = 0;   
        //需要遍历到index前一个的节点,才能在两个节点之间插入一个节点
        LinkedNode *newnode = new LinkedNode(val);
        LinkedNode *cur = dummyhead;
        while(index){
            cur = cur->next;
            index--;
        }
        //插入节点都同理,先更新需插入的next
        newnode->next = cur->next;
        cur->next = newnode;
        _size++;
    }
    
    void deleteAtIndex(int index) {
        if (index >= _size || index < 0) {
            return;
        }
        LinkedNode *cur = dummyhead;
        //这里就体现了虚拟头节点的妙处,虚拟头节点让你的cur等于需要删除结点的前一个节点,
        //是的删除的操作更加简单。
        while(index){
            cur = cur->next;
            index--;
        }
        LinkedNode *tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        _size--;

    }

    private:
    int _size;
    LinkedNode* dummyhead;
};

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

第三题、LeetCode206 反转链表https://leetcode.cn/problems/reverse-linked-list/

反转一个链表,用双指针法的思路是设置pre作为目前表头的前一个节点,并为NULL,cur设为表头,依次遍历下去

注意:遍历时更新各个变量的次序,以及while循环里的判断。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        //双指针法
        ListNode *tmp;
        ListNode *cur = head;
        ListNode *pre = NULL;
        while(cur){
            //cur在最后会指向尾节点next的null,以此来判断
            tmp = cur->next; //先存cur->next,否则待会找不到
            cur->next = pre;//再反转
            pre = cur;//再移动pre,否则cur变了
            cur = tmp;//最后移动cur
        }
        return pre;


    }
};

递归方法: 先熟练掌握双指针法后,才能更好地理解递归法。

定义一个reverse函数,反转()前后两个参数。

可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步pre = cur;cur = temp;

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

    ListNode* reverseList(ListNode* head) {
        
        return reverse(NULL,head);
        // 和双指针法初始化是一样的逻辑,ListNode* cur = head;ListNode* pre = NULL;
 
    }
};

最后调用reverse返回时注意和双指针法初始化是一样的逻辑辑ListNode* cur = head;ListNode* pre = NULL;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值