训练营第三天|力扣203、移除链表元素(虚拟头节点) 707、设计链表 (链表的常见操作) 206、反转链表(双指针)

移除链表元素(单链表)

思路:要想移除链表元素,就要找到该元素的前一个节点的位置,为了统一头节点和其余节点的移除方式,通过添加虚拟头节点的方式来解决这个问题。

KEY:
1、虚拟头节点是一个工具节点,它并不是真正的头节点,该节点的next指向真正的头节点。
2、while循环当cur为空时说明链表不存在所要移除的元素,若cur的next是目标元素,那么就要执行移除操作了,值得注意的是,如果使用的是C++的语法,那么为了避免内存泄露,需要手动释放逻辑上被删除的元素,这就需要事先用一个临时指针来保存该元素的位置,这点不要忘了。

总结:移除链表元素时,以及对链表的其他操作中,坚持虚拟头节点的方式,可以让节点的处理得到统一。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
           //用虚拟头节点来解决,可以统一代码逻辑
         ListNode* dummyhead=new ListNode(0);
          dummyhead->next=head;
        ListNode*  cur=dummyhead;
          while(cur->next!=NULL)
          {
              if(cur->next->val==val)
              {
                  ListNode* temp=cur->next;
                  cur->next=cur->next->next;
                  delete temp;
              }
              else
              {
                  cur=cur->next;
              }
          }
          head=dummyhead->next;
          delete dummyhead;
          return head;
    }
};

设计链表(链表的常见操作)

分析:
本题包括获取元素,头插,尾插,在某个节点前插入,删除某个节点,是一道考察基本功的题目,这里统一用虚拟头节点的方式写代码。

思路:
获取元素,让cur做临时指针找到对应值返回即可
头插,用虚拟头节点的方式插入,其实本质跟在其余节点插入没有区别。
尾插,由于新节点在有参构造中已经初始化为NULL,所以这里不用让新节点再指向NULL
在某个节点前插入,删除某个节点,注意对边界条件的处理,另外它们同样都是要找到该节点(index)的前一个位置进行操作。

注意:
输入位置时,往往要事先对该位置的合法性做判断

总结:题目难度不大,但细节不少,例如对size要及时增减,明确节点的操作顺序(很重要)等,搭配草图可以更好的理解。

代码:
class MyLinkedList{
public:
//定义链表节点结构体
struct LinkedNode{
int val;
LinkedNode* next;
LinkedNode(int val):val(val), next(nullptr) {}
};

//初始化链表
MyLinkedList(){
_size=0;
_dummyhead=new LinkedNode(0);
}

//获取第index个节点的值
int get(int index){
if(index>(_size-1)||index<0){
return -1;//如果输入的index非法,返回-1
}
LinkedNode* cur=_dummyhead->next;//临时指针让它指向真正的头节点
while(index–){
cur=cur->next;//index从0开始
}
return cur->val;
}
//头插
void addAtHead(int val){
LinkedNode* newnode=new LinkedNode(val);
newnode->next=_dummyhead->next;
_dummyhead->next=newnode;
_size++;
}
//尾插
void addAtTail(int val){
LinkedNode* newnode=new LinkedNode(val);
LinkedNode* cur=_dummyhead;
while(cur->next){
cur=cur->next;
}
cur->next=newnode;
_size++;
}
//在第index节点前插入
void addAtIndex(int index,int val){
if(index>_size){return;}
if(index<0)index=0;
LinkedNode* newnode=new LinkedNode(val);
LinkedNode* cur=_dummyhead;
while(index–){
cur=cur->next;
}
newnode->next=cur->next;
cur->next=newnode;
_size++;
}
//删除第index个节点
void deleteAtIndex(int index){
if(index>=_size||index<0){
return;
}
LinkedNode* cur=_dummyhead;
while(index–){
cur=cur->next;
}
LinkedNode* temp=cur->next;
cur->next=cur->next->next;
delete temp;
_size–;
}
private:
int _size;
LinkedNode* _dummyhead;
};

反转链表(双指针)

思路:就像昨天评论里AI说的那样,双指针的应用很广泛,而且往往具有较好的性能。
这里并不需要重新建立一个链表,只需要对现有链表的指针方向进行反转即可。

KEY:指针的反转需要两个指针来配合前移,一定要注意的是,在反转之前,先保存住cur的后继节点的位置,一旦指针反转,该位置不保存的话就再也找不到了。

细节:反转何时结束,当cur为空时,说明反转链表已经完成

总结:在题目涉及前后节点的交互,左右两端的对比等情况时,可以考虑使用双指针。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* temp;
        ListNode* cur=head;
        ListNode* pre=nullptr;
        //反转链表并不需要这个重新定义一个链表,只需要将原来的指针它的方向反转即可
        while(cur){
            temp=cur->next;
            cur->next=pre;//此时指针已经反转
            pre=cur;//我要先让pre往前移动,才能够避免cur被temp赋值后无法前移
            cur=temp;
        }
        //当cur==nullptr的时候,循环结束,此时,pre就是新链表的头节点
        return pre;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值