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

203.移除链表元素 

题目链接:力扣题目链接

视频链接:链表基础操作| LeetCode:203.移除链表元素

文章链接:代码随想录 (programmercarl.com)

 移除链表元素有两种方法,1.从原链表上进行移除   2.使用虚拟头结点

链表元素的移除,实际上就是改变移除元素上一个节点的指针,使其跳过这个移除元素,直接指向这个移除元素的下一个节点,这样,这个包含移除元素的节点就“孤立无援”了,改变这个节点也不会对其他节点带来影响,但它依旧占据了内存中的一块位置,C++还需要用delete对这个孤家寡人进行清除。

我一开始想到的就是第一种方法,据刚刚所分析,想要删除某个节点,就要知道它的上一个节点,而如果是头结点的话,不存在上一个节点,所以要进行分类,分为头结点和非头结点两类。

代码应运而生:

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        while(head!=NULL&&head->val==val)//考虑头结点包含移除元素的情况
        {
            ListNode* tmp = head;//重新定义一个指针指向待会将要移除的节点位置,起到一个定位的作用
            head = head->next;
            delete tmp;
        }
        ListNode* cur=head;
        while(cur!=NULL&&cur->next!=NULL)//非头结点移除元素
        {
            if(cur->next->val==val)
            {
                ListNode* tmp=cur->next;
                cur->next=cur->next->next;
                delete tmp;
            }
            else
            {
                cur=cur->next;
            }
        }
        return head;
    }
};

第二种方法:使用虚拟头结点

就是在头结点前再次添加一个节点作为新的头结点,这样原来的头结点和其他节点就没有什么区别了。不必进行分类,代码如下:

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
       ListNode* dumm=new ListNode(0);//虚拟头结点
        dumm->next=head;
        ListNode* cur=dumm;
        while(cur!=NULL&&cur->next!=NULL)
        {
            if(cur->next->val==val)
            {
                ListNode* tmp=cur->next;
                cur->next=cur->next->next;
                delete tmp;
            }
            else
            {
                cur=cur->next;
            }
        }
        head=dumm->next;//注意!!!此时原先的头结点有可能被移除了,需要重新定义head
        delete dumm;
        return head;
    }
};

 707.设计链表  

 

 这道题是一道很经典的链表设计题,包含了查找第x个节点的元素,头部插入新节点,尾部插入新节点,任意位置插入新节点,删除一个节点的操作,运用了虚拟头节点,代码如下:

class MyLinkedList {
private:
    struct Listnode{//定义链表结点结构体
        int val;//结点的值val
        Listnode* next;//结点的指针域next,指向下一个结点
        //三个Listnode构造函数
        Listnode():val(0),next(nullptr){}//参数为空
        Listnode(int v):val(v),next(nullptr){}//一个参数
        Listnode(int v,Listnode* n):val(v),next(n){}//两个参数
    };
    int len;//链表长度
    Listnode* dummyhead;//链表虚拟头结点
public:
    MyLinkedList() {//MyLinkedList构造函数
        len=0;//链表长度初始化为0
        dummyhead=new Listnode();//创建链表虚拟头结点
    }
    
    int get(int index) {//获取链表中第index个节点的值
        if(index<0||index>len-1) return -1;//索引无效返回-1
        Listnode* head=dummyhead->next;//dummyhead是虚拟头结点,而dummyhead->next是头结点
        while(index--) head=head->next;//找到第index个链表结点
        return head->val;//返回第index个链表结点的值val
    }
    
    void addAtHead(int val) {//在链表的第一个元素之前添加一个值为val的节点
        if(val<0||val>1000) return;//val值无效,直接返回
        Listnode* n=new Listnode(val);//新建值为val的链表结点n
        n->next=dummyhead->next;//将新链表结点n插入头结点前,新结点的指针域next为头结点指针,即新结点n的next指针指向头结点
        dummyhead->next=n;//虚拟头结点指针域next为新结点指针n,即虚拟头结点的next指针指向新结点n
        len++;//链表长度加一
    }
    
    void addAtTail(int val) {//将值为val的节点追加到链表的最后一个元素
        if(val<0||val>1000) return;//val值无效,直接返回
        Listnode* n=new Listnode(val),*cur=dummyhead;//新建值为val的链表结点n和当前结点指针变量cur
        while(cur->next) cur=cur->next;//通过cur找到链表中最后一个结点
        cur->next=n;//将新结点n插入链表末尾,即最后一个结点的指针域为新结点指针n
        len++;//链表长度加一
    }
    
    void addAtIndex(int index, int val) {//在链表中的第index个节点之前添加值为val的节点
        if(val<0||val>1000||index>len) return;//val值或index值无效,直接返回
        if(index<=0) addAtHead(val);//index<=0时,在头部插入值为val的新结点
        else if(index==len) addAtTail(val);//index=len时,在尾部插入值为val的新结点
        else{
            Listnode* n=new Listnode(val),*cur=dummyhead;//新建值为val的链表结点n和当前结点指针变量cur
            while(index--) cur=cur->next;//找到第index-1个链表结点
            n->next=cur->next;//将新链表结点n插入第index个链表结点前,新结点的指针域next为第index个链表结点的指针域next,即新结点n的next指针指向第index个链表结点
            cur->next=n;//第index个链表结点的指针域next为新结点指针n,即第index个链表结点的next指针指向新结点n
            len++;//链表长度加一
        }
    }
    
    void deleteAtIndex(int index) {//删除链表中的第index个节点
        if(index<0||index>len-1) return;//index值无效,直接返回
        Listnode* cur=dummyhead,*t=nullptr;//当前结点指针变量cur和临时结点t
        while(index--) cur=cur->next;//找到第index-1个结点
        t=cur->next;//t保存要删除的第index个结点
        cur->next=t->next;//第index-1个结点的指针域next为第index个结点的指针域next,即删除第index个结点
        delete t;//释放第index个结点的空间
        len--;//链表长度减一
    }
};

反转链表 

 题目链接:206. 反转链表 - 力扣(LeetCode)

视频链接:帮你拿下反转链表 | LeetCode:206.反转链表

文章链接:代码随想录 (programmercarl.com)

对于反转链表,我的第一想法是,先将链表中元素的值依次存于数组中,在进行遍历,将数组中的元素逆向赋值给链表中的元素,达到反转的目的,代码如下: 

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        int size=0,i=0;
        ListNode* cur=head;
        while(cur!=NULL)//计算链表节点个数
        {
            cur=cur->next;
            size++;
        }
        vector<int> a(size,0);
        cur=head;
        while(cur!=NULL)
        {
            a[i++]=cur->val;//将元素存入数组
            cur=cur->next;//遍历
        }
        cur=head;//让cur重新指向头结点
        while(cur!=NULL)
        {
            cur->val=a[--i];//逆向赋值
            cur=cur->next;
        }
        return head;
    }
};

当然,此题也可以使用双指针法

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* cur=head;
        ListNode* pre=NULL;
        ListNode* tmp;
        while(cur!=NULL)
        {
            tmp=cur->next;//tmp用来保存cur后一位节点位置
            cur->next=pre;//改变链表方向
            pre=cur;
            cur=tmp;
        }
        return pre;
    }
};

双指针关键部分就是通过,cur->next=pre来改变链表连接方向

第三种方法:递归

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

递归的思路和双指针其实一模一样,递归终点就是cur==NULL时,与双指针while(cur!=NULL)类似。

共耗时3.5小时,对于链表,并不是非常熟练,再接再厉。

Day 3打卡成功!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我知道了。以下是对您的问题的回答: a. 动态分配内存的方法有很多,可以使用C语言的malloc函数,也可以使用C++中的new操作符,具体方法如下: C语言的malloc函数: ```c struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode)); ``` C++中的new操作符: ```c++ ListNode* node = new ListNode(); ``` b. 初始化单链表需要对链表节点的成员变量进行赋值,以下是一个示例: ```c++ struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(NULL) {} }; ListNode* head = NULL; ``` c. 头插法创建单链表的示例代码如下: ```c++ ListNode* head = new ListNode(); head->next = NULL; for (int i = 0; i < n; i++) { int x; cin >> x; ListNode* node = new ListNode(x); node->next = head->next; head->next = node; } ``` 尾插法创建单链表的示例代码如下: ```c++ ListNode* head = new ListNode(); head->next = NULL; ListNode* tail = head; for (int i = 0; i < n; i++) { int x; cin >> x; ListNode* node = new ListNode(x); node->next = NULL; tail->next = node; tail = node; } ``` d. 取链表某一位的值可以通过遍历链表来实现,以下是一个示例: ```c++ ListNode* p = head->next; int pos = 3; for (int i = 1; i < pos && p != NULL; i++) { p = p->next; } if (p == NULL) { cout << "链表长度不足" << endl; } else { cout << p->val << endl; } ``` e. 在链表的某一位进行插入操作可以通过遍历链表找到插入位置,然后插入一个新节点来实现,以下是一个示例: ```c++ ListNode* p = head; int pos = 3; for (int i = 1; i < pos && p != NULL; i++) { p = p->next; } if (p == NULL) { cout << "链表长度不足" << endl; } else { int x = 100; ListNode* node = new ListNode(x); node->next = p->next; p->next = node; } ``` f. 遍历链表可以通过循环遍历链表节点来实现,以下是一个示例: ```c++ ListNode* p = head->next; while (p != NULL) { cout << p->val << " "; p = p->next; } cout << endl; ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值