Day3 链表初步

203. 移除链表元素

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

解答链接:代码随想录

解答:

这道题比较基本,用链表移除元素。

(1)移除头节点:将头节点移动至下一个元素;或者创造虚拟头节点避免这个问题;

(2)移除中间元素:上一个指向下一个;

感悟:

1. 链表的结构需要熟悉:

链表头指针为head,这就是链表的调用方式。最后一个指针指向NULL,因此ptr==NULL是结束遍历的条件。链表最小是空链表,也就是head==NULL。

2. 移除元素需要分头节点和其他节点,方法有所不同

3. 移动操作共分为三步:替换(将需要删除的赋给另一个变量);换位(进行搭建新链的操作);移除(将新变量删除,释放内存)

错题:

1. 判断条件:判断条件分为两块

(1) 一个是元素是否存在(例如,删除头元素需要看头元素是否存在,head!=NULL,删除中间元素需要看上一个和这个是否存在,cur!=NULL && cur->next!=NULL)

(2) 另一个是元素是否应该被操作(这里是cur->val==val)。

2. 创造节点,需要用new

3. 如果使用虚拟节点,之后返回的时候需要返回head=dummy->next,而不是head。因为这里head也是可以被删除的,所以可能不存在,dummyhead是不会变动的。

代码:

ListNode* removeElements(ListNode* head, int val) {
        // 删除头节点:将头节点进行替换
        // 前提:头节点存在(表不为空),头节点需要被删除
        while(head != NULL && head->val == val){
            ListNode *temp = head; // 替换
            head = head->next; // 换位
            delete temp; // 删除
        }

        // 删除非头节点:从它前一个元素开始,跳过它
        // 前提:前一个元素和这个元素存在,这个元素需要被删除
        ListNode *cur = head;
        while(cur != NULL && cur->next != NULL){
            if(cur->next->val == val){
                ListNode *temp = cur->next; // 替换
                cur->next = cur->next->next; // 换位
                delete temp; // 删除
            }else{
                cur = cur->next; // 挪动
            }
        }
        return head;
    }
ListNode* removeElements(ListNode* head, int val) {
        ListNode *dummyfirst = new ListNode();
        dummyfirst->next = head;
        ListNode *cur = dummyfirst;
        // 删除非头节点:从它前一个元素开始,跳过它
        // 前提:前一个元素和这个元素存在,这个元素需要被删除
        while(cur != NULL && cur->next != NULL){
            if(cur->next->val == val){
                ListNode *temp = cur->next; // 替换
                cur->next = cur->next->next; // 换位
                delete temp; // 删除
            }else{
                cur = cur->next; // 挪动
            }
        }
        head = dummyfirst->next;
        delete dummyfirst;
        return head;
    }

707. 设计链表

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

答案链接:代码随想录

解答:

这个题比较简单,但是bug非常多,接下来逐一梳理。

错题:

(1)get,addindex,delete需要判断给定的index是否合适。对于get,index属于[0,size),addindex的index属于[0,size],delete的index属于[0,size)。

        addindex,delete操作的时候要从index-1开始,因此用于遍历的tempnode = dummyhead,while里面一共循环index次,循环完正好能将tempnode移动至index-1。

        get操作的时候要在index上,因此用于遍历的tempnode = dummyhead->next,循环完正好能将tempnode移动至index。

(2)利用while(index--)来代替计数器

(3)在add的时候,new->next = temp->next,如果是加入头节点,temp是dummy。容易想成temp->next->next,这里其实new和temp是并行的关系,之后new加入

(4)size不要忘记变化

(5)delete后,空的指针地址是随机的,为了避免之后误触,需要赋值为nullptr

(6)最关键的bug:插入元素

new_node->next = temp_node->next

temp_node->next = new_node

第一行写成了 temp_node->next =new_node->next

先联络,再脱钩,这里相当于先脱钩,再联络了,后面的已经跑掉了。联络需要让新的被赋值,被赋值的在前面,顺序一定不能搞乱

代码:

class MyLinkedList {
public:
    struct LinkNode{
        int val;
        LinkNode *next;
        LinkNode(int val):val(val),next(nullptr){}
        LinkNode():val(0),next(nullptr){}
        LinkNode(int v,LinkNode* n):val(v),next(n){}
    };

    MyLinkedList(){ // 成员函数
        dummyhead = new LinkNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
        size = 0;
    }
    
    int get(int index) {
        if(index > (size - 1) || index < 0){
            return -1; // 先进行判断
        }
        LinkNode *tempnode = dummyhead->next;
        while(index--){ // 比较好的方式,不用空置count判断
            tempnode = tempnode->next;
        }
        return tempnode->val;
    }
    
    void addAtHead(int val) {
        LinkNode *newnode = new LinkNode(val);
        newnode->next = dummyhead->next; // 不是nextnext
        dummyhead->next = newnode;
        size++; // 一定要记住size变化
    }
    
    void addAtTail(int val) {
        LinkNode *newnode = new LinkNode(val);
        LinkNode *tempnode = dummyhead; // 
        while(tempnode->next!=nullptr){
            tempnode = tempnode->next;
        }
        tempnode->next = newnode;
        size++;
    }
    
    void addAtIndex(int index, int val) {
        if(index > size) return;
        if(index < 0) index = 0; // 需要注意一下
        LinkNode *newnode = new LinkNode(val);
        LinkNode *tempnode = dummyhead;
        // 插入最好从当前元素的后面插入,而非index之前
        while(index--){
            tempnode = tempnode->next;
        } // tempnode 会指向下标为index-1的节点,因为tempnode是从dummy(index=-1)开始的。如果从head(index=0)开始,就会指向下标为index的节点
        newnode->next = tempnode->next;
        tempnode->next = newnode;
        size++;
    }
    
    void deleteAtIndex(int index) {
        if(index > (size - 1) || index < 0){
            return; // 先进行判断
        }
        LinkNode *tempnode = dummyhead;
        while(index--){
            tempnode = tempnode->next;
        }
        LinkNode *delnode = tempnode->next;
        tempnode->next = tempnode->next->next;
        delete delnode;
        delnode=nullptr;
        size--;
    }
private:
    int size;
    LinkNode* 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);
 */

206. 反转链表

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台​​​​​​

解答链接:代码随想录

思路:

想到了遍历、逐一操作这件事,但是在具体实现过程中卡在了如何保证脱链但维持信息

方法:

方法就是将脱离的,暂时存在一个地方。其实这个过程中有两个脱离,一个是和之前一个节点的(需要指回去),一个是和之后一个节点的(temp = temp->next)。

因此,需要设置三个临时变量,一个记录前一个,一个记录当前的,一个记录后一个。让后一个成为后一个,当前的指向前一个,前一个变成当前的,当前的变成后一个(temp)。

感悟:

在临时设置变量方面,需要考虑有多少个节点需要断开。

同时,这道题可以用递归的方法进行,但是必要性不高(如同Fib数列),因此先不进行实现。

代码:

ListNode* reverseList(ListNode* head) {
        ListNode *temp;
        ListNode *Cur = head;
        ListNode *Pre = NULL;
        while(Cur != nullptr){
            temp = Cur->next;
            Cur->next = Pre;
            Pre = Cur;
            Cur = temp;
        }
        return Pre;
    }

你可能感兴趣的:(java,服务器,算法)