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

203.移除链表元素

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

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

在这里插入图片描述

示例 2:
输入:head = [], val = 1
输出:[]

示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]

看到题目第一想法

最直观的想法,分情况讨论是否是头节点。是头节点,直接改变头结点位置。不是头结点,就将应删除节点上一个节点直接指向下下个节点。
注意:操作节点前要判断是否为空。

//伪代码
//移除头节点
//判断头节点不为空,不然就是操纵空指针了,编译报错
//if(head!=null&&head->val==target)不对头节点应持续移除,例如1 1 1 1 1 100 移除1
while(head!=null&&head->val==target)
  head=head->next
cur=head;//临时指针指向头节点  定义临时指针用来遍历,不能用头结点遍历,最后返回不了应该返回的头节点
while(cur!=null &&cur->next!=null )//cur不能为空 cur->next也不为空 因为我们要取值和target对比,不然就是操纵空指针了,编译报错
    if(cur->next==target)  //cur的下一个节点应删除
       cur->next=cur->next->next//cur的下一个节点为当前cur的下一个下一个节点
    else cur=cur->next
    //必须放else  cur当前节点前面判断过不是val;只有cur下一个经过if判断也不是才能将临时指针后移
    /* 若 if(cur->next==target) 
       cur->next=cur->next->next/
     cur=cur->next         则未判断下一个的下一个要不要删除*/
return head

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
     //不采用虚拟头节点
     while(head!=null && head.val==val){
         head=head.next;
     }
    ListNode cur=head;
    while(cur!=null&&cur.next!=null){//cur!=null要加上 不判断的话 cru=null那么不存在next
        if(cur.next.val==val){
            cur.next=cur.next.next;
        }else{cur=cur.next;}
    }
    return head;
    }
}


学习之后

发现通过引入虚拟节点可以不用单独分出头节点来讨论。
在这里插入图片描述
这里来给链表添加一个虚拟头结点为新的头结点,此时要移除这个旧头结点元素1。
这样就可以使用和移除链表其他节点的方式了,最后在题目中,return 头结点的时候,别忘了 return dummyNode->next;, 这才是新的头结点。

//伪代码
//虚拟头节点dummy head  统一链表操作操作
dummy head=new();
dummyhead->next=head;
cur=dummyhead//不等于dummyhead->next 因为要删的是cur->next,当前是head
while(cur->next!=null)
    if (cur->next->val==target)
        cur->next=cur->next->next;
    else cur=cur->next
return dammyhead->next

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        //虚拟头节点
        ListNode dummyhead= new ListNode (1,head);
        ListNode cur = dummyhead;
        while(cur.next!=null){
            if(cur.next.val==val){
                cur.next=cur.next.next;
            }else {cur=cur.next;}
        }
    return dummyhead.next; }

虚拟头节点,cur临时指针=虚拟头节点。

707.设计链表

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

在这里插入图片描述

看到题目第一想法

采用虚拟头节点来对五种操作进行编码。

//伪代码

 /**链表常见操作 
1获取第n各节点的值(节点地址从0开始)
2头部插入节点
3尾部插入节点
4第n个节点前插入节点
5删第n个节点*/
//本题均采用虚拟头节点
//1
cur =dummyhead.next//遍历链表一定新建临时指针不能直接操作头指针 因为最后要返回头指针
while(n){//循环条件带入边界验证是否满足  n=0头节点 跳出循环; n=1第二个节点 
    cur=cur.next
    n--
}
return cur.val
//2
ListNode newNode= new ListNode();
newNode.next=dummyhead.next;//先后后前 
 dummyhead.next=newNode;
return dummyhead.next
//3
cur=dummyhead
while(cur.next!=null){
    cur=cur.next
}
ListNode newNode =new ListNode()
cur.next=newnode
//4  在第n个节点前插入  即 cur n-1    cur.next n 这两个节点之间操作  第n个节点为cur.next
cur=dummyhead
while(n--){//举极端例子验证
    cur=cur.next
}
newNode.next=cur.next;
cur.next=newNode
size++
//5  n为cur.next  才能删掉  直接cur.next=cur.next.next; 写之前先想明白 n是cur.next
cur=dummyhead 
while(n){//极端例子 只有一个节点 删除第0个节点 
    cur=cur.next
    n--
}
cur.next=c
size--

 //写了之后发现报错查了半天发现忘了更新size大小了
class ListNode{    
    int val ;
    ListNode next;
    public ListNode(){};
    public ListNode(int val){
        this.val=val;
    }}
class MyLinkedList {
    int size;//记录链表大小          
    ListNode dummyHead;//虚拟头节点
    
    public MyLinkedList() {//初始化
      dummyHead=new ListNode(-1);
      size=0;//只有虚拟头节点所以大小为0
        }

    public int get(int index) {
        if(index<0||index>=size){
            return -1;
        }else{
            ListNode cur=dummyHead;//所有临时指针均指向虚拟头节点
            while(index!=0){
                cur=cur.next;
                index--; }
            return cur.next.val;
        }
    }
    
    public void addAtHead(int val) {
        ListNode newNode=new ListNode(val);
        newNode.next=dummyHead.next;
        dummyHead.next=newNode;
        size++;
        return ;
    }
    
    public void addAtTail(int val) {
        ListNode newNode= new ListNode(val);
        ListNode cur=dummyHead;
        while(cur.next!=null){
            cur=cur.next;
        }
        cur.next=newNode;
        size++;
        return;
    }
    
    public void addAtIndex(int index, int val) {
        if(index>size){return;}
        ListNode newNode=new ListNode(val);
        ListNode cur= dummyHead;
        if(index<0){
            addAtHead(val);
            return ;
        }        
        while(index--!=0){
            cur=cur.next;
        }
        newNode.next=cur.next;
        cur.next=newNode;
        size++;
        return;
    }
    
    public void deleteAtIndex(int index) {
        if(index<0||index>=size){//地址0开始最多size-1
        return ;}
        ListNode cur=dummyHead;
        while(index--!=0){//JAVA不能直接写while(index)
            cur=cur.next;
        }
        cur.next=cur.next.next;
        size--;
        return ;}
}

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList obj = new MyLinkedList();
 * int param_1 = obj.get(index);
 * obj.addAtHead(val);
 * obj.addAtTail(val);
 * obj.addAtIndex(index,val);
 * obj.deleteAtIndex(index);
 */

针对第n个数进行操作,让cur.next=该数。

206.反转链表

题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

在这里插入图片描述
在这里插入图片描述

看到题目第一想法

只需要改变链表的next指针的指向,直接将链表反转,无需增加删除节点。
在这里插入图片描述
针对此题需要两个指针一个指当前节点,一个指当前节点的前节点,用来转换指针方向,还需要一个临时节点来寄存原来节点的下一个节点,用来移动前面两个定位的节点。
首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。
然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。
为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。
在这里插入图片描述

//伪代码
//双指针pre改方向 cur 遍历
//初始化  共需要三个临时指针
cur=head 
pre=null
while(cur==null){//终止条件cur=null 不能是null 空指针异常 也不能是cur.next=null 此时pre不是头节点,寻找不变量 
//一开始 pre=null cur=head,最后pre=head,cur=null
 temp=cur.next;//先保存原来下一个节点
 cur.next=pre;
 pre=cur;
 cur=temp;}
return pre;

//双指针
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode cur=head;
        ListNode pre=null;
        ListNode temp;
        while(cur!=null){
            temp=cur.next;
            cur.next=pre;
            pre=cur;
            cur=temp;
        }
        return pre;
    }
}

递归法

递归法相对抽象一些,但是其实和双指针法是一样的逻辑,同样是当cur为空的时候循环结束,不断将cur指向pre的过程。
关键是初始化的地方,可能有的同学会不理解, 可以看到双指针法中初始化 cur = head,pre = NULL,在递归法中可以从如下代码看出初始化的逻辑也是一样的,只不过写法变了。
递归法本质就是自己调用自己的方法。

//递归与双指针接法一一对应
//自定义reverse函数
reverse(cur,pre)
if(cur==null){
    return pre;
}
temp=cur.next;//先保存下一个节点的位置
cur.next=pre;//然后改变方向
reverse(temp,cur);//递归调用自己方法;

//输入函数reverseList(head)
reverseList(head){
   return  reverse(head,null)
}
//递归报错代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */

class Solution {
    public ListNode reverseList(ListNode head) {
     
        return reverse(head,null);}

    public ListNode reverse(ListNode cur,ListNode pre){
    ListNode temp=cur.next; //报错[]不符,要先判断 cur是否为[]
    cur.next=pre;
    if(temp!=null){
        return reverse(temp,cur);
    }
     return cur;}}


```//递归
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */

class Solution {
    public ListNode reverseList(ListNode head) {
     
        return reverse(head,null);}

    public ListNode reverse(ListNode cur,ListNode pre){
   if(cur==null){
       return pre;}
    ListNode temp=cur.next;
    cur.next=pre;
     return reverse(temp,cur);} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值