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

203.移除链表元素

建议:本题最关键是要理解虚拟头结点的使用技巧,这个对链表题目很重要。

题目:删除链表中等于给定值 val 的所有节点。

示例 1 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]

思路

这里以链表 1 4 2 4 来举例,移除元素4

还要说明一下,就算使用C++来做leetcode,如果移除一个节点之后,没有手动在内存中删除这个节点,leetcode依然也是可以通过的,只不过,内存使用的空间大一些而已,但建议依然要养成手动清理内存的习惯。

这种情况下的移除操作,就是让节点next指针直接指向下下一个节点就可以了。那么因为单链表的特殊性,只能指向下一个节点,刚刚删除的是链表的中第二个,和第四个节点,那么如果删除的是头结点又该怎么办呢?

这里就涉及如下链表操作的两种方式:

  • 直接使用原来的链表来进行删除操作。
  • 设置一个虚拟头结点在进行删除操作。

来看第一种操作:直接使用原来的链表来进行移除。

移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点。

所以头结点如何移除呢,其实只要将头结点向后移动一位就可以,这样就从链表中移除了一个头结点

这样移除了一个头结点,是不是发现,在单链表中移除头结点 和 移除其他节点的操作方式是不一样,其实在写代码的时候也会发现,需要单独写一段逻辑来处理移除头结点的情况。

那么可不可以 以一种统一的逻辑来移除 链表的节点呢。

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

来看看如何设置一个虚拟头。依然还是在这个链表中,移除元素1

这里来给链表添加一个虚拟头结点为新的头结点,此时要移除这个旧头结点元素1。这样是不是就可以使用和移除链表其他节点的方式统一了呢?

最后呢在题目中,return 头结点的时候,别忘了 return dummyNode->next;,这才是新的头结点

C代码如下:

struct ListNode* removeElements(struct ListNode* head, int val) {

    typedef struct ListNode ListNode;

    //创建虚拟头结点

    ListNode* dummyHead=malloc(sizeof(ListNode));

    dummyHead->next=head;

    //创建一个curr节点

    ListNode* curr=dummyHead;

    while(curr->next){

        if(curr->next->val==val){

            //创建一个中间节点tmp

            ListNode* tmp=curr->next;

            curr->next=curr->next->next;

            free(tmp);

        }

        else{

            curr=curr->next;

        }

    }

head=dummyHead->next;

    free(dummyHead);

    return head;

}注意点:注意需要手动释放被删除的节点

707.设计链表

题目:

在链表类中实现这些功能:

  • get(index):获取链表中第index个节点的值。如果索引无效,则返回-1。
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

思路

这道题目设计链表的五个接口:

  • 获取链表第index个节点的数值
  • 在链表的最前面插入一个节点
  • 在链表的最后面插入一个节点
  • 在链表第index个节点前面插入一个节点
  • 删除链表的第index个节点

可以说这五个接口,已经覆盖了链表的常见操作,是练习链表操作非常好的一道题目

206.反转链表

题目:反转一个单链表。

示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

思路

如果再定义一个新的链表,实现链表元素的反转,其实这是对内存空间的浪费。

其实只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表,如图所示:

之前链表的头节点是元素1 反转之后头结点就是元素5 ,这里并没有添加或者删除节点,仅仅是改变next指针的方向。

那么接下来看一看是如何反转的呢?

双指针法

反转链表比较简单,很容易想到,从前向后遍历链表,然后把每个结点的 next 指向前一个结点就好。

具体实现中,首先需要两个指针,一个 cur 指针在遍历到每个结点进行操作,一个 pre 指针指向 cur 的前一个结点。其次,由于 cur->next 指向 pre,导致原来的 cur 的下一个结点失去引用,所以还需要一个指针 temp 用来保存后一个结点。

C代码如下:

struct ListNode* reverseList(struct ListNode* head) {

    struct ListNode* cur=head;

    struct ListNode* pre=NULL;

    while(cur){

        //保存下一个节点

        struct ListNode* tmp=cur->next;

        //反转结点

        cur->next=pre;

        //更新precur指针

        pre=cur;

        cur=tmp;

    }

    return pre;

   

}

注意点:想明白是先移动pre指针还是先移动cur指针。

写在最后:链表的第一天的题目还算比较简单,从代码中反映出的思路也很简洁。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值