【代码随想录】DAY 3 链表(移除链表元素、设计链表、反转链表)

链表基础:

链表:由一串节点node组成,node里有一个data数据域和一个next指针域(存放指向下一个节点的指针),第一个节点为head,这是单链表;

双链表:node里还有一个prev,指向上一个节点的next,因此双链表可以向前查询也可以向后查询。

循环链表:就是单链表的头尾连接起来了

删除节点只需要将前一个节点的next指向后一个节点就行,但最好自己手动释放删除节点的内存。

第一题

力扣

我都想不通为啥我照抄的代码随想录的代码还是AC不了,服了

/**
 * 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 (val == head->val && head !=NULL)
    {
        ListNode* p = head;
        head = head->next;
        delete p;
    }
    ListNode* p = head;
    while (p->next != NULL && p!=NULL)
    {
        if (p->next->val == val)
        {
            ListNode* d = p->next;
            p->next = p->next->next;
            delete d;
        }
        else 
        {p = p->next;
        }
    }
    return head;
    }
};

但是直接copy上面的又过了......无语无语!

学习记录:

/**
 * 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) 
    {
        //删除头节点,不能是if
        while (head != nullptr && head->val == val)
        {
            head = head->next;
        }
        ListNode* cur_node = head;
        while( cur_node != nullptr && cur_node->next != nullptr )
        {
            if (cur_node->next->val == val)
            {
                cur_node->next = cur_node->next->next;
            }
            else 
            {
                cur_node = cur_node->next;
            }
        }
        
        return head;
    }
};

在删除头节点的while循环处,不能为if的原因是,后面的if判断的都是当前节点的下一个节点值是否=val,如果用if只判断一次,那遇到前几个节点值都为val的情况,始终会留下一个节点值为val的头节点不会被判断到。 

如果想判断当前节点值,则需要增加一个哑节点,作为头节点前一个节点:

/**
 * 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) 
    {
        //删除头节点
        ListNode dummy(0);
        dummy.next = head;
        ListNode* cur_node = head;
        ListNode* prev_node = &dummy;
        while (cur_node != nullptr)
        {
            if (cur_node->val == val)
            {
                prev_node->next = cur_node->next;
                delete cur_node;
                cur_node = prev_node->next;
            }
            else 
            {
                prev_node = cur_node;
                cur_node = cur_node->next;
            }
        }
        return dummy.next;
    }
};

代码随想录的解:

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        // 删除头结点
        while (head != NULL && head->val == val) { // 注意这里不是if
            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;
    }
};
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;
    }
};

第二题

力扣

 学习记录:

在构建链表时,记得先定义节点结构体

struct ListNode{

        ListNode* next;

        int val;

}

 同样的,链表里面还有private数据成员,虚拟节点和链表长度:

private:

ListNode* dummy_node;

int size;

在写增删改查的函数时,注意遍历节点cur_node到底是=dummy_node还是=dummy_node->next;

删除节点时,记得定义一个临时节点存储要删除的节点,然后delete释放内存,再将指针置空

ListNode* tmp = cur_node->next;

delete tmp;

tmp = nullptr;

如果是节点型指针,构造时要么为空,要么=new ListNode,访问成员时要用->:

 ListNode* cur_node = new ListNode(0);

cur_node->next; 

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

    MyLinkedList() {
        dummy_node = new ListNode(0);
        size = 0;
    }
    
    int get(int index) {
        if (index > size-1 || index < 0) {
            return -1;
        }
        ListNode* cur_node = dummy_node->next;
        while (index){ //每找一次就减一次index,直到index为0表示已经找到当前index对应的节点
            cur_node = cur_node->next;
            index--;
        }
        return cur_node->val;
    }
    
    void addAtHead(int val) {
        ListNode* add_node = new ListNode(val);
        add_node->next = dummy_node->next;
        dummy_node->next = add_node;
        size++;
    }
    
    void addAtTail(int val) {
        ListNode* cur_node = dummy_node; //注意这里cur=虚拟节点而不是头节点
        while (cur_node->next != nullptr) {
            cur_node = cur_node->next;
        }
        ListNode* add_node = new ListNode(val);
        cur_node->next = add_node;
        size++;
    }
    
    void addAtIndex(int index, int val) {
        if (index > size) {
            return;
        }
        else if (index == size) {
            addAtTail(val);
        }
        else {
            ListNode* cur_node = dummy_node;
            ListNode* add_node = new ListNode(val);
            while (index) {
                cur_node = cur_node->next;
                index--;
            }
            add_node->next = cur_node->next;
            cur_node->next = add_node;
            size++;
        }
    }
    
    void deleteAtIndex(int index) {
        if (index > size-1 || index < 0) {
            return;
        }
        ListNode* cur_node = dummy_node;
        while (index){ 
            cur_node = cur_node->next;
            index--;
        }
        ListNode* tmp = cur_node->next;
        cur_node->next = cur_node->next->next;
        delete tmp; //记得清内存
        tmp = nullptr; //避免成为野指针
        size--;
    }

private:
    ListNode* dummy_node;
    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);
 */

复习:

struct ListNodes {
    ListNodes* next;
    int val;
    ListNodes():val(0), next(nullptr){};
    ListNodes(int x):val(x), next(nullptr){};
};

class MyLinkedList {
public:
    MyLinkedList() {
        dummyhead = new ListNodes();
        length = 0;
    }
    
    int get(int index) {
        if (index < 0 || index >= length) return -1;
        ListNodes* cur = dummyhead->next;
        while (index--) {
            cur = cur->next;
        }
        return cur->val;
    }
    
    void addAtHead(int val) {
        ListNodes* newHead = new ListNodes(val);
        newHead->next = dummyhead->next;
        dummyhead->next = newHead;
        length++;
    }
    
    void addAtTail(int val) {
        ListNodes* newTail = new ListNodes(val);
        ListNodes* cur = dummyhead;
        while (cur->next != nullptr) cur = cur->next;
        cur->next = newTail;
        length++;
    }
    
    void addAtIndex(int index, int val) {
        if (index < 0 || index > length) return;
        if (index == length) {
            addAtTail(val);
            return;
            }
        else {
            ListNodes* cur = dummyhead;
            while (index--) {
                cur = cur->next;
            }
            ListNodes* temp = new ListNodes(val);
            temp->next = cur->next;
            cur->next = temp;
            length++;
        }
    }
    
    void deleteAtIndex(int index) {
        if (index < 0 || index >= length) return;
        else {
            ListNodes* cur = dummyhead;
            while (index--) {
                cur = cur->next;
            }
            ListNodes* toDelete = cur->next;
            cur->next = cur->next->next;
            delete toDelete;
            length--;
        }
    }

private:
    ListNodes* dummyhead;
    int length;

};

 

第三题

力扣

 这道题自己没有想出来,但是看了代码随想录的解释,瞬间觉得太牛了

学习记录:

首先明确的是只需要修改节点的指向,并且一次只能修改一个节点的指向

如图所示,因为修改的是遍历节点cur到next的指向,所以需要将原本的cur->next记录为temp,在将cur->next改为指向prev后,cur不能再用cur=cur->next的方式遍历(因为next的地址已经更改),但是可以通过赋值的方式,即cur=temp,如此和剩下的链表衔接起来,在挪动cur之前,因先将prev赋值为当前的cur,再完成下一个节点的指向修改。

 其中需要注意1、prev的初始化,可以联想为初始化的prev最终会变为尾节点的next,而尾节点的next都是null,因此需要将prev设为Null,即ListNode* prev=NULL。

2、最后返回值prev,因为每次循环的顺序为先改cur->next,然后temp++,cur++,prev++,当最后一条指向修改完毕后,根据执行顺序temp、cur、prev都还要往右挪一个,此时只有prev才指向修改完的头节点。

/**
 * 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* reverseList(ListNode* head) {
        ListNode* prev_node = nullptr;//因为最后的尾节点要指向null
        ListNode* temp;
        ListNode* cur_node = head; //遍历节点
        while (cur_node != nullptr) {
            temp = cur_node->next; //保存原本下一个节点
            cur_node->next = prev_node; //改变当前节点的next指向
            prev_node = cur_node; //prev++
            cur_node = temp; //cur++
        }
        return prev_node;
    }
};

复习:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* preNode = nullptr; //最后尾节点必须指向null
        ListNode* curNode = head;
        ListNode* tempNode;
        while (curNode != nullptr) {
            tempNode = curNode->next; //先记录马上要断掉的next
            curNode->next = preNode; //紧跟着更新next
            preNode = curNode; //然后更新pre
            curNode = tempNode; //最后cur继续遍历
        }
        return preNode; //最后cur遍历到了原本尾节点的next(即null),那么pre就变成了翻转后的head
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值