代码随想录60天刷题之链表 | Day3

目录

链表

1.链表基础

2. 链表定义(手撕用)

Day 3:

3.1.  LeetCode203 移除链表元素:题意:删除链表中等于给定值 val 的所有节点。

3.2 Leetcode707 设计链表:手撕建立一个单链表

3.3 反转链表:力扣


  • 链表

  • 1.链表基础

什么是链表,链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针)。

特别注意:最后一个节点的指针域指向null(空指针的意思)。

链接的入口节点称为链表的头结点也就是head。

数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。

数组和链表对比:

2. 链表定义(手撕用)

   //单链表(重点记住)

    struct ListNode{

        int val;  //节点上存储的元素

        ListNode* next;  //指向下一个节点的指针

        ListNode(int x):val(x),next(nullptr){}  //节点的构造函数

};

//力扣的定义示例:

Day 3:

3.1.  LeetCode203 移除链表元素:题意:删除链表中等于给定值 val 的所有节点。

力扣

注意:

(1)虚拟头结点的使用:其实可以设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。

(2)结点删除的内存清理delete

  (3)  个人还是习惯用pre和cur配合

//注意与牛客剑指18的区别,那个简单没有重复的数,用那个潜意识参考思路吃大亏了!!

class Solution {
public:
    //注意与牛客剑指18的区别,那个简单没有重复的数,用那个潜意识参考思路吃大亏了!!
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* VirNode=new ListNode(-1);
        VirNode->next=head;//虚拟头结点指向头指针
        ListNode* cur=head; //cur指针移动
        ListNode* pre=VirNode;//pre指针代表cur指针指向元素的前一个元素

        while(cur)//只有if 和 else两种情况,至于连续重复的一开始先别管,先把最简单的实现了
        {
           if(cur->val==val)//如果这个结点符合条件,删除结点,记得清理内存
           //不能用while,如果cur最后空了,cur->val报错了,不可能null->val
           {
              ListNode* temp=cur; //保存地址用以删除,因为cur地址后面改变了
              pre->next=cur->next;//只有cur不是NULL,就可以使用cur->next的,若是则会出错,不能NULL->next
              cur=cur->next;
              //pre->next=cur;//用这行替换上面的那行也行,但要注意前后语句顺序
              delete temp;//清理删除结点的内存
           }
           else    //如果不符合,移动pre和cur
           {
               pre=cur;
               cur=cur->next;
           }
        }
        //ListNode* temp=VirNode;//这里其实不用temp再delelte temp也行,因为VirNode以后不用了
        head=VirNode->next;//这里因为有可能head移除了
        delete VirNode;//记得删除建立的结点
        return head;
    }
};

3.2 Leetcode707 设计链表:手撕建立一个单链表

力扣

注意题目中的链表是怎么初始化的。

注意类中的成员变量是啥:链表长度要不断更新,还有一个虚拟头结点!

class MyLinkedList {
public:
    struct ListNode{
        int val;
        ListNode* next;
        ListNode(int x):val(x),next(nullptr){}
    };
    //初始化,记住这个
    MyLinkedList() {
        size=0;
        dummyNode=new ListNode(-1);
    }
    //获取index处元素,index从0开始,0代表首元结点
    int get(int index) {
      if(index<0||index>size-1)
      {
          return -1;
      }
      ListNode* cur=dummyNode->next;//首元结点
      int count=-1;
      while(cur) //使用for循环从首元直接循环(index-1)次也行,老想不起来for,for(int i=1;i<index;i++){cur=cur->next;}
      {
        count++;//计数
        if(count==index) 
        {
            break;
        }
        cur=cur->next;
      }
       return cur->val;
    }

    //头插法
    void addAtHead(int val) {
       ListNode* p=new ListNode(val);
       p->next=dummyNode->next;//先进行这个操作,和下行顺序不能反,要不然后面地址丢失
       dummyNode->next=p;
       size++;   //链表长度加1
    }
    //尾插法
    void addAtTail(int val) {
      ListNode* p=new ListNode(val);
      ListNode* cur=dummyNode;//注意这里不能写作dummy->next,因为可能一开始不存在这个首元结点,注意!!!
      while(cur->next)//找到最后一个元素的位置停止
      {
         cur=cur->next;
      }
      cur->next=p;
      size++; //链表长度加1
    }
    //在指定位置index之前插入元素
    void addAtIndex(int index, int val) {
       if(index>size)
        {
            return;//把return的if条件语句方法最前面,可以提高运行效率,一开始放到后面了,也能AC
        }
       else
        {
            if(index==size)
            {
               addAtTail(val);//此时不用size++了,因为调用的函数已经加了,废了俩小时才找到错误,血的教训!!
            }
            else if(index<0)
            {
               addAtHead(val);//此时不用size++了,因为调用的函数已经加了,废了俩小时才找到错误,血的教训!!
            }
           else
           {
            ListNode* p=new ListNode(val);
            ListNode* pre=dummyNode;
            ListNode* cur=dummyNode->next;
            int count=-1;
            while(cur)
            {
              count++;
              if(count==index)//找到第index个结点,还有前一个结点
              {
                break;
              }
              pre=cur;
              cur=cur->next;
            }
            p->next=cur;//这一行和下一行顺序可互换,因为有双指针pre和cur
            pre->next=p;
            size++;
           }
        }
    }
    //删除第index个元素,从0开始
    void deleteAtIndex(int index) {
        if(index<0||index>size-1)
        {
            return;//索引index无效
        }
          ListNode* pre=dummyNode;
          ListNode* cur=dummyNode->next;
          int count=-1;
          while(cur)
          {
              count++;
              if(count==index)
              {
                  pre->next=cur->next;
                  delete cur;//这里可以直接删除cur,因为不用了
                  size--;
                  break;
              }
              pre=cur;
              cur=cur->next;
          }

    }
private:
   int size;//定义一个链表长度
   ListNode* dummyNode;//定义一个虚拟头结点,用于初始化

};

3.3 反转链表:力扣

注意:pre和cur指针的配合,还是习惯各种用pre和cur,跟代码随想录题解思路不太一致

//思路都有,一写都是浆糊,移动pre指针老弄不对,因为多写了个虚拟头结点,pre指向它了,这里根本不用,要指向NULL

class Solution {
public:
    //思路都有,一写都是浆糊,移动pre指针老弄不对,因为多写了个虚拟头结点,pre指向它了,这里根本不用,要指向NULL
    ListNode* reverseList(ListNode* head) {
        ListNode* pre=nullptr;//这里pre注意要写成nullptr空指针
        ListNode* cur=head;
        
        while(cur)//遍历链表
        {
            ListNode* temp=cur->next;//先保存cur指向右边的下一个元素,放到外面毛定义也可
            cur->next=pre;//cur反向指向pre(指向左边)。这里不能写反,是要给cur->next赋值,改变cur的指向
            //如果是pre=cur->next那相当于改变pre,使pre指向cur的下一个结点了,如第一步中那就是pre指向2

            //更新pre和cur指针
            pre=cur;//改变pre的指向,右移一个元素
            cur=temp;//cur右移一个元素
        }
      
        return pre;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值