day3 链表(一)

2023.12.1
代码随想录刷题Day3

1. 203移除链表元素
对于链表的各种操作备考时也是烂熟于心,这么长时间没写过相关代码,也确实忘了很多,关于移除链表元素,逻辑其实很简单,主要还是看对指针的使用是否熟悉,其次就是最关键的,头节点的使用,我个人是比较习惯加头节点,头节点可以使得对链表的操作全部统一,不用特殊特殊某个节点很方便。思路很简单,加入头节点后遍历一遍链表节点值,如果是要删除的值,直接将上节点的next连接到要删除节点的next就行,也就是越过要删除的节点。再把要删除的节点delete。

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

2. 707设计链表
这道题逻辑很简单,但就是很考察指针基本功,刚开始写的时候出现了一些指针地址错误,改了很多次,代码这东西还得多写,不然长时间不写真的会生疏,最后有个情况,测试用例过了,但提交失败,有错误的情况,发现是get函数有问题,最后通过打印链表长度,发现是addindex这个函数全部插入失败了,原因是开始的index合法判断有问题。原本这里用了和get函数一样的index判断。并没有多想,但最后出错才发现,两个逻辑不一样,get函数不允许两个端点的index,而addindex却允许有两边的。一个简单的例子:现在有三个节点,索引是0,1,2。此时如果想get索引为3的,则应该报错。因为并没有索引3,但是如果想要给索引3加节点,应该是允许的,因为索引3跟原链表是相连的,这就是我开始犯的错误,没有注意到这一点。最终整体感受,这道题对于常写链表代码的人来说。应该会很简单。但对于很长时间没写过链表代码的人来说,比如我,还是有些复杂的,逻辑简单,实现有些生疏了。

class MyLinkedList {

public:
    struct ListNode{
        int val;
        ListNode* next;
        ListNode(int val):val(val),next(nullptr){}
    };
    MyLinkedList() {
        head = new ListNode(0);
        size=0; 
    }
    
    int get(int index) {
        cout<<this->size<<endl;
        if(index > (size-1) || index<0){
            return -1;
        }
        else{
            int now_index=0;
            ListNode*node = new ListNode(0);
            node = head->next;
            while(now_index<index){
                node = node->next;
                now_index++;
            }
            return node->val;
        }
        cout<<this->size<<endl;
    }
    
    void addAtHead(int val) {
        ListNode *node = new ListNode(val);
        node->next = head->next;
        head->next = node;
        size++;
        cout<<this->size<<endl;
    }
    
    void addAtTail(int val) {
        ListNode*node = new ListNode(val);
        ListNode*temp = head;
        while(temp->next != nullptr){
            temp = temp->next;
        } 
        temp->next = node;
        size++;
        cout<<this->size<<endl;
    }
    
    void addAtIndex(int index, int val) {
        if(index > size) return;
        if(index < 0) index = 0;   
        
            
        int now_index = 0;
        ListNode*node = new ListNode(val);
        ListNode*temp = new ListNode(0);
        temp = head;
        while(now_index<index){
            temp = temp->next;
            now_index++;
        }
        node->next = temp->next;
        temp->next = node;
        size++;
        cout<<this->size<<endl;
    }
    
    void deleteAtIndex(int index) {
        if(index>size-1)
            return;
        int now_index = 0;
        ListNode*node = new ListNode(0);
        node = head;
        while(now_index<index){
            node = node->next;
            now_index++;
        }
        node->next = node->next->next;
        size--;
    }
private:
    int size;//记录链表实际长度(除头节点外)
    ListNode* head;
};

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

3. 206反转链表
这道题好像是链表的典型题,很多面试、链表操作中都会有反转链表的题。具体实现也是有两种方法,第一种很简单,遍历连边,将值重新赋予一个新链表就行,最后的新链表就是反转后的链表。当然我们需要掌握的是第二种,在原链表的基础上反转,这种方法逻辑还是有点抽象的,同样是循环,每次需要在节点处断开,前面待处理的原链表,后面是已经反转过的,所以,肯定需要三个指针,一个用来遍历,一个用来记录待处理的链表,一个指向已经处理后的反转链表。最后将已经处理后的反转链表返回即可,对于链表的循环,我记得有个很重要的点就是,每次循环的操作顺序,一定要先记录下一个节点的位置,不然更新当前节点的next后则会丢失原本的next,在我印象中所有链表操作中这点很重要

/**
 * 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* temp = nullptr;  //反转后的
        ListNode* node = head;  //遍历用的
        ListNode* n_node;  //待处理的
        while(node){
            n_node = node->next; //一定优先记录待处理的部分,也就是该节点指向当前遍历节点的next
            node->next = temp; //进行翻转。当前节点next指向反转后的链表,也就是拼接
            temp = node;  //更新反转节点的头,上一步已经连接的,这一步就要把头更新为新节点
            node = n_node;  //往前循环
        }
        return temp; //最后返回反转链表即可
    }
};

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