代码随想录算法训练营第3天 | 203.移除链表元素 707.设计链表 206.反转链表

203 太久没写链表了非常不熟练,有dummy 没dummy全弄懂 加上理清自己问题,写总结花了1.5h

第一个版本
遇到的一些问题: 
1. head->val 是指向的大框,head->next 是大框后面的小框 
2. curr= curr->next 这个是遍历,因为curr是我们自己创的东西,本质上没改变链表结构
curr->next=curr->next->next 这个是删除,因为 curr->next是列表结构的内容
3. 不用dummy的话 head就要特殊处理,还要用while,一直把需要删掉的head全部删掉
4. 要把一会失去ptr的东西 先用tmp存起来,之后delete释放内存 (java python)不用
5. NULL 和 nullptr应该是一样的

ListNode* removeElements(ListNode* head, int val) {
      
         while(head != nullptr && head->val == val) {
            ListNode* tmp = head;
            head = head->next;
            delete tmp;
        }

        if (head == nullptr) {
            return nullptr;
        }
        ListNode *curr=head;
        while(curr->next!=nullptr){  //没有处理head 开头
            
            if(curr->next->val==val){
                ListNode* tmp = curr->next;
                curr->next=curr->next->next;
                delete tmp;
            }
            else{
                curr=curr->next;
            }
            
        }
        return head;
    }

第二个版本
while那里改成while(curr!=nullptr && curr->next!=nullptr),直接去掉 if (head == nullptr) {return nullptr;  }

版本三 dummy head

ListNode* removeElements(ListNode* head, int val) {
      
        ListNode * dummy = new ListNode(0);
        dummy->next = head;

        ListNode *curr = dummy;
        while(curr->next != nullptr ){
            if(curr->next->val == val){
                ListNode* tmp = curr->next;
                curr->next = curr->next->next;
                delete tmp;
            } else {
                curr = curr->next;
            }
        }

        head = dummy->next;
        delete dummy;
        return head;
    }

关于遍历和删除的while的判断条件
一般要遍历 要走完, 应该是 while curr!=nullptr (这里是不处理,跳出。是说 我这样 遍历其实是没走完的,但是由于我的逻辑是用到 curr->next->val ,后续改也是 next变 next next,所以不用走完 只用走到倒数第二个

#707 居然自己写过了,好久没有自己implementation无误写出来题了,good for me!dummy head 在处理增删方面真是好用
才发现lc的 test and run功能,好用的

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

    int _size;
    Node* _dummyHead;

    MyLinkedList() {
        _size=0;
        _dummyHead=  new Node(0);
        
    }
    
    int get(int index) {
        if(index>_size-1|| index<0){return -1;}
        Node * curr= _dummyHead->next;
        int idx=0;
        while(curr!=NULL&& idx!=index){
            curr=curr->next;
            idx++;
        }

        if(curr==NULL){return -1;}
        return curr->val;
    }
    
    void addAtHead(int val) {
        Node * newnode= new Node (val);
        newnode->next=_dummyHead->next;
        _dummyHead->next=newnode;
        _size++;
    }
    
    void addAtTail(int val) {
        Node * curr= _dummyHead;
        while (curr->next!=NULL){
            curr=curr->next;
        }
        Node * newnode= new Node (val);
        curr->next=newnode;
        newnode->next=NULL;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
       if(index>_size){return;}
       if(index==_size){ addAtTail(val); return;}
       Node * curr=_dummyHead;
       while(index>0){
           curr=curr->next;
           index--;
       }

       Node * newnode= new Node (val);
       newnode->next=curr->next;
       curr->next=newnode;

       _size++;

    }
    
    void deleteAtIndex(int index) {
        if (index<0|| index >_size-1){return;}
        Node * curr=_dummyHead;
        while(index>0){
           curr=curr->next;
           index--;
       }

       if(curr->next!=NULL){
            Node * tmp= curr->next;
            curr->next=curr->next->next;
            delete tmp;
       }     
    }
};

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

#206 面试经常考,反转链表,法一双指针, pre是指向当前这个方块的,cur是用来遍历的


ListNode* reverseList(ListNode* head) {
        if (head==NULL){return head;}

        ListNode * pre = NULL;
        ListNode * curr = head;
        ListNode * tmp;

        while(curr!=NULL){
            tmp= curr->next;
            curr->next=pre;
            pre = curr;
            curr = tmp;
        }
        return pre;
    }

双指针 时间复杂度: O(n) 空间复杂度: O(1)

代码随想录另一个方法是递归 时间复杂度: O(n) 空间复杂度: O(n)

【gpt说:空间复杂度由递归栈的深度(n)决定 。

递归栈并不是你在代码中直接看到的一个结构,而是在程序执行递归函数时,操作系统或运行时环境隐式地使用的一个数据结构。

当你调用一个函数时,系统会在内存中为这个函数调用创建一个"栈帧"。这个栈帧包含了函数的局部变量,返回地址,以及其他函数调用需要的信息。当函数返回时,这个栈帧就会被销毁。

如果函数是递归调用的,那么系统会为每次递归调用都创建一个新的栈帧,并把这些栈帧都压入到一个"调用栈"(或者叫"递归栈")中。这就是为什么你可以在一个函数中调用它自己,然后再返回到原来的函数调用中:每个递归调用都有它自己的栈帧,包含了它自己的局部变量,返回地址等信息。

因此,在分析递归函数的空间复杂度时,需要考虑这个隐式的调用栈。如果递归的深度(也就是调用栈的深度)是n,那么空间复杂度就是O(n)。这里的n并不是指ListNode的数量,而是指的递归的深度,即递归调用的次数。】

今天总共花了3h左右

你可能感兴趣的:(代码随想录一刷,算法)