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

看了卡哥的链表基础
之前都是在数据结构上学链表,实际操作忘了,看到了链表的构造函数,很新奇。
是我孤陋寡闻了~

struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数,这玩意儿居然没分号,太久没编码了,都不记得了,将NULL换成nullptr试试
};

定义了构造函数的能够直接赋值

ListNode* head = new ListNode(5);

而不定义构造函数的,系统会自动生成一个默认构造函数
使用默认构造函数初始化节点:

ListNode* head = new ListNode();
head->val = 5;

203.移除链表元素

    • ListNode * p 和 ListNode * p= new ListNode()的区别
    • NULL与nullptr的区别,前面构造函数里指针赋的是NULL,而后面用的是nullptr

题目链接

他题目给的这个头节点不是数据结构里那个没有赋值只为了便于链表操作的头节点
自己还因为这个纠结了一会儿
1.带不带头结点的处理方式是不同的,这道题让俺深刻的体会到了
2.自己又犯了一个错误,在处理后续的遍历链表的时候,一定要额外新建一个结点指针,否则一直用head,好家伙最后返回head,只返回了最后一个元素6.
我说我一开始感觉自己写的没错,咋就。。。。。。
不戴手套就去和泥好家伙手扎刺了扎流血了,手也用不了了。

错误代码:

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
            ListNode *p=head->next;
            
            while(head!=NULL&&head->val==val){
                  ListNode *q=head;
                  head=head->next;
                delete q;}
                //处理非头节点的部分没有额外新建结点来代替head
            while(head!=NULL&&head->next!=NULL){
                if(head->next==NULL){ 
                ListNode *q=head->next;
                head->next=q->next;
                delete q; 
                }
                else head=head->next;
            }
            return head;
    }
};

浏览器这截图扩展也不咋好用啊
代码随想录算法训练营第三天|链表:203.移除链表元素 707.设计链表 206.反转链表_第1张图片

不带头节点的正确代码

    • ListNode * p 和 ListNode * p= new ListNode()的区别
    • NULL与nullptr的区别,前面构造函数里指针赋的是NULL,而后面用的是nullptr

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
           //如果头节点是val如何删除头节点
           while(head!=NULL&&head->val==val){
               ListNode *q=head;//新建一个中间变量
               head=head->next;
               delete q;
           }
           //处理非头节点是val。
           ListNode *p=head;
           while(p!=NULL&&p->next!=NULL)//别忘了对head判空,我要是直接对head->next判空,力扣就举报我用了空指针。
           {
               if(p->next->val==val){
                ListNode*q=p->next;
                p->next=q->next;
                delete q;
               }else p=p->next;

           }
            return head;
    }
};

带头结点

    • ListNode * p 和 ListNode * p= new ListNode()的区别
    • NULL与nullptr的区别,前面构造函数里指针赋的是NULL,而后面用的是nullptr

设置一个虚拟头节点来让题目所给头节点的处理与一般结点一样
方便操作处理。

最后为了恢复内存还要给新建的头节点删除

ListNode * p 和 ListNode * p= new ListNode()的区别

通俗的来说就是一个是指针,一个是链表结点。

ListNode * p 是指向结构节点的指针,里面只有一个地址。
ListNode * p= new ListNode()是一个结构节点,里面有val和指向下一个节点的结构体指针,而且该节点已经被系统分配内存,在函数体里不会被自动释放。


class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode *m=new ListNode(0);
        m->next=head;
        ListNode *p=m;
        while(p->next!=NULL){
            if(p->next->val==val){
                ListNode *q =p->next;
                p->next=q->next;
                delete q;}
            else p=p->next;
        }
        head=m->next;
        delete m;
        return head;
    }     
};

707.设计链表

    • ListNode * p 和 ListNode * p= new ListNode()的区别
    • NULL与nullptr的区别,前面构造函数里指针赋的是NULL,而后面用的是nullptr

题目链接

这个题考的是对链表的各种功能的实现
有点数据结构的意思
对不起,我C++面向对象又给忘了

NULL与nullptr的区别,前面构造函数里指针赋的是NULL,而后面用的是nullptr

代码随想录算法训练营第三天|链表:203.移除链表元素 707.设计链表 206.反转链表_第2张图片
这道题跟昨天那个循环排数的一样心塞,主要是我对这些操作不够熟练导致的
赋一个卡哥的解析
自己爱出的问题主要有这个index从0开始,对循环上边界问题有点晕,还有一开始的判断index 所给是否合理上上
还有就是指针应该通常是取头节点,还是头节点->next
在这个过程中爱犯得一个错误:LeetCodeBug-member access within null pointer of type ‘struct ListNode’
无法判断这个指针是否是一个空指针时,你给他赋值力扣会报错,要加一个条件判断,判断它不能为空的时候你才能给它赋值。这有篇博客

class MyLinkedList {
public:
    //初始化之前得有一个链表的结构体
    struct LinkNode{
        int val;
        LinkNode *next;
        LinkNode(int val):val(val),next(nullptr){}
    };
    //为了方便操作,新建一个虚拟头结点
    MyLinkedList() {
        nhead=new LinkNode(0);
        _size=0;
    }
    
    int get(int index) {//获取链表中第 index 个节点的值,如果下标无效,则返回 -1 。//题目里给出了
        if(index>(_size-1)||index<0){//index的值是从0开始的
            return -1;
        }//吸取上次删除的教训,要新建一个指针指向头结点,不要直接用头结点。
        LinkNode*p=nhead->next;
        while(index--){
            p=p->next;
        }return p->val;

    }
    
    void addAtHead(int val) {//将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。头插法?
        LinkNode *p=new LinkNode(val);
        p->next=nhead->next;
        nhead->next=p;
        _size++;

    }
    
    void addAtTail(int val) {//将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
    LinkNode *p=new LinkNode(val);
    LinkNode *q=nhead;

    while(q->next!=NULL){q=q->next;}
    q->next=p;
    _size++;
    }
    
    void addAtIndex(int index, int val) {// 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
    if(index>_size||index<0){//index的值是从0开始的
            return ;
        }
           LinkNode *p=new LinkNode(val);
           //LinkNode *q=nhead->next;
            LinkNode *m=nhead;
        while(index--){
            //=q->next;
            m=m->next;
        }   p->next=m->next;
            m->next=p;
            _size++;
    }
    
    void deleteAtIndex(int index) {//如果下标有效,则删除链表中下标为 index 的节点。 
    if(index>=_size||index<0){//index的值是从0开始的
            return ;
        }
        LinkNode *p=nhead;
       // 
         while(index--){
            // q=q->next;
            p=p->next;
        }LinkNode*q=p->next;
        p->next=q->next;
        delete q;
        _size--;

    }
private://私有类函数成员别忘了,别光写公有成员函数。
        int _size;
        LinkNode* nhead;
};


206.反转链表

    • ListNode * p 和 ListNode * p= new ListNode()的区别
    • NULL与nullptr的区别,前面构造函数里指针赋的是NULL,而后面用的是nullptr

题目链接

本来想到的是头插法,但是那是新建逆序链表常用的
卡哥给讲的是双指针法,

对不起卡哥,我盗了你的图


让快指针指向前一个指针,改变链表方向

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
 
        ListNode *q=NULL;
        ListNode *p=head;
        
        while(p!=nullptr){
            ListNode *m=p->next;
            p->next=q;
            q=p;//不是p=p->next,因为这个p可能为空
            p=m;
           // m=m->next;
        }
        return q;
    }
};

卡哥还讲了一个递归法
递归函数是由出口和递归函数体(每次调用函数要做的关键步骤)组成的
声明完递归函数还要调用递归函数

class Solution {
public:
    ListNode* reverse(ListNode *pre,ListNode *cur){
            if(cur==NULL)return pre; 
            ListNode *t =cur->next;
            cur->next=pre;
           return reverse(cur,t);
    }
    ListNode* reverseList(ListNode *head) {
      return reverse(NULL,head);}
};

你可能感兴趣的:(代码随想录,链表,算法,数据结构,leetcode,c++)