代码随想录第三天(链表开始~新的挑战)

目录

1 今日学习的文章

链表理论基础 

移除链表元素  

看到题目的第一想法

看到代码随想录之后的想法

        1 不带虚拟头节点的做法(使用pre指针)

        2 带虚拟头节点的做法(使用pre指针)

        3 不带虚拟头节点的做法(不使用pre指针)

自己实现过程中遇到的困难        

设计链表

看到题目的第一想法

看到代码随想录之后的想法

自己实现过程中遇到的困难(待补充,代码注释里有标注)

   1单向     

   2 双向

反转链表

看到题目的第一想法

看到代码随想录之后的想法

自己实现过程中遇到的困难(待补充,代码注释里有标注)


1 今日学习的文章

写代码的时候最好对照图像的逻辑来写,可以画个图,模拟,不会太乱

链表理论基础 

建议:了解一下链接基础,以及链表和数组的区别 

文章链接:代码随想录

学习收获
重点关注了下链表的存储方式

移除链表元素  

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

题目链接/文章讲解/视频讲解::代码随想录

看到题目的第一想法


这不就是正常的链表遍历然后删除么,很简单的题目,但是实现起来还是有不少坑。。

看到代码随想录之后的想法


代码随想录提出了三种办法

        1 不带虚拟头节点的做法(使用pre指针)

        1,先要处理头节点相关的与val相同的数据,记住这里是while循环来处理,当head不为val时再考虑之后的数据

        2,使用一个pre指向该节点的前置节点,当该节点删除时,pre指向该节点的后置节点,记住这个时候pre是不需要向后移的

        3,如果该节点不为val,则pre和该节点一起向后移动

        2 带虚拟头节点的做法(使用pre指针)
        

        1,如果有虚拟头节点,那么就不需要额外处理head节点了,但是得注意最后的返回应该是虚拟头节点的next节点

        2,处理逻辑和上述类似

        3 不带虚拟头节点的做法(不使用pre指针)

        1,需要额外处理头节点

        2,没有pre 那么得做判断,删除的是该节点的next节点,连接的是该节点的next.next,可以把while循环里的当前节点视为pre节点

自己实现过程中遇到的困难
        

        1,不加上head节点,操作会比较麻烦,比如我在不带虚拟头节点的做法中处理head节点的val元素时,使用的时if而不是while导致报错

        2,处理单链表的删除,要记住该节点的上一个节点的位置

        3,while循环中,当该节点删除时,pre指向该节点的后置节点,记住这个时候pre是不需要向后移的和pre=pre.next为if else的关系

       

/**
 * 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; }
 * }
 */
 /*
 未添加虚拟节点的做法 需要单独处理head
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        //删除列表中的val []-->[] -->NULL 如果该节点存在val值,则该节点的上一个节点应该指向该节点的下一个节点
        //该节点
         //处理头节点(记住这里是while循环)
        while(head!=null&&head.val == val){
            head = head.next;
        }
        if(head == null){
            return head;
        }
      
        ListNode listNode = head.next;
        //上一个节点
        ListNode preNode = head;
        while(listNode!=null){
            if(listNode.val==val){
                //让前一个节点指向该节点的下一个节点
                preNode.next = listNode.next;
            }else{
                //之前这里忘记加else而是统一在while循环之后preNode=preNode.next是错误的,此时preNode应该不变
                preNode = preNode.next;
            }
            listNode = listNode.next;
        }
        
        return head;
    }
}*/
//添加虚拟节点的做法
/*class Solution {
    public ListNode removeElements(ListNode head, int val) {
            ListNode newHead = new ListNode();
            newHead.next = head;
            ListNode listNode = newHead.next;
            ListNode preNode = newHead;
            while(listNode!=null){
                if(listNode.val==val){
                    preNode.next = listNode.next;
                }else{
                    preNode = preNode.next;
                }
                listNode = listNode.next;
            }
            return newHead.next;
    }
}*/
//不添加虚拟节点和pre的做法
class Solution {
    public ListNode removeElements(ListNode head, int val) {
          while(head!=null&&head.val == val){
            head = head.next;
         }
         if(head == null){
            return head;
        }
            ListNode listNode = head;
            while(listNode!=null){
                //这里是使用while循环
                while(listNode.next!=null&&listNode.next.val==val){
                    listNode.next = listNode.next.next;
                }
                listNode = listNode.next;
            }
            return head;
    }
}

设计链表

建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点

题目链接/文章讲解/视频讲解:代码随想录

看到题目的第一想法


感觉实现比较简单,结果有点无从下笔

看到代码随想录之后的想法

        1 需要自己定义一个ListNode节点的数据结构

        2 在LinkList中需要添加size变量且初始化为0,来控制节点数的数量,方便操作,同时定义一个head变量为虚拟头节点

        3 构造函数需要让head初始化以免出现空指针

        4 双向链表需要定义head和tail两个节点控制头尾,方便查找,在构造函数中需要用head.next = tail.prev,tail.next = head.prev 以免出现空指针

  
自己实现过程中遇到的困难(待补充,代码注释里有标注)
        
   1单向

        1 get是如何实现的

        我的思路是一个for循环,到index位置(但是需要考虑index的初始值,不能>=元素的个数,以及不能小于0)

        2 addAtHead

        直接头插法,记得要size++

        3 addAtTail

        走到最后,然后再插入,记得size++

        4addAtIndex

        记得讨论index的大小,由于是插入,index的大小可以=元素的个数,相当于在最后插入,不能>元素的个数,找到index所在位置的前置,直接进行插入,记得size++

        5deleteAtIndex

        记得讨论index的大小,不能大于等于size,我的思路找到前置进行删除,记得size--

        

class ListNode{
    public ListNode next;
    public int val;
    public ListNode(){

    }
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class MyLinkedList {
    private ListNode head;
    private int val ;
    private int size = 0;
    public MyLinkedList() {
        head = new ListNode(0,null);
    }

    //获取下标为index节点的值
    public int get(int index) {

        //listNode为虚拟头节点
        if(index>=size){
            return -1;
        }
        
        ListNode list = head.next;
        for(int i=0;i<index;i++){
            list = list.next;
        }
        return list.val;

    }
    
    public void addAtHead(int val) {
        ListNode addHead = new ListNode(val);
        if(head.next!=null){
            addHead.next=head.next;
        }
        head.next = addHead;
        size++;
    }
    //遍历到最后
    public void addAtTail(int val) {
        ListNode list = new ListNode(val,null);
        ListNode listNode = head;
        while(listNode.next!=null){
            listNode=listNode.next;
        }
        //最后一个的下一个指向该节点
        listNode.next = list;
        size++;
    }
    //插到index节点之前(其实就是插入到index位置)
    public void addAtIndex(int index, int val) {
        if(index<0||index>size){
            return;
        }
        if(index==size){
            this.addAtTail(val);
            return;
        }
        //找到index节点直接插入到index即可
        ListNode list = new ListNode(val,null);
        ListNode listNode = head.next;
        ListNode preNode = head;
        for(int i =0;i<index;i++){
            //找到前一个位置
            preNode = preNode.next;
            //找到index
            listNode = listNode.next;
        }
        preNode.next = list;
        list.next = listNode;
        size++;
        

    }
    
    public void deleteAtIndex(int index) {
        //这里要设为>=size
        if(index<0||index>=size){
            return;
        }
        if(size==0){
            return;
        }
         //找到index节点直接删除到index即可
        ListNode listNode = head.next;
        ListNode preNode = head;
        for(int i =0;i<index;i++){
            //找到前一个位置
            preNode = listNode;
            //找到index
            listNode = listNode.next;
        }
        preNode.next = listNode.next;
        size--;
    }
}

/**
 * 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);
 */

        
   2 双向

        1 get是如何实现的

        我的思路是一个for循环,到index位置(但是需要考虑index的初始值,不能>=元素的个数,以及不能小于0)

        2 addAtHead

        直接addAtIndex(0)

        3 addAtTail

       直接addAtIndex(size)

        4addAtIndex

        记得讨论index的大小,由于是插入,index的大小可以=元素的个数,相当于在最后插入,不能>元素的个数,找到index所在位置的前置,直接进行插入,记得size++

        5deleteAtIndex

        记得讨论index的大小,不能大于等于size,我的思路找到前置进行删除,记得size--

//使用双链表模式实现
class ListNode{
    ListNode next,prev;
    int val;
    public ListNode(){

    }
    public ListNode(int val){
        this.val = val;
    }
}
class MyLinkedList {
    private ListNode head,tail;
    int size = 0;
    public MyLinkedList() {
        head = new ListNode();
        tail = new ListNode();
        head.next = tail;
        tail.prev = head;
    }
    //获取index下表个元素(看哪个半边比较快)
    public int get(int index) {
        if(index<0||index>=size){
            return -1;
        }
        //从头到尾 查index下 
        ListNode node = head.next;
        for(int i=0;i<index;i++){
            node = node.next;
        }
        return node.val;
    }
    //头插,都可以用addAtIndex来确定
    public void addAtHead(int val) {
         //等价于在第0个元素前添加
        addAtIndex(0,val);
        
    }
    
    public void addAtTail(int val) {
        //等价于在第size个元素上添加 之前写的size-1有问题,因为是添加在第size位置
        addAtIndex(size,val);
        
    }
    
    public void addAtIndex(int index, int val) {
        ListNode node = new ListNode(val);
        //这里index>=size有bug,index是可以=size的
        if(index>size||index<0){
            return;
        }
        size++;
        ListNode oldNode = head.next;
        for(int i=0;i<index;i++){
            oldNode = oldNode.next;
        }
        node.prev = oldNode.prev;
        node.next = oldNode;
        oldNode.prev.next = node;
        oldNode.prev = node;
        
    }
    
    public void deleteAtIndex(int index) {
        if(index>=size||index<0){
            return;
        }
        ListNode list = head.next;
        for(int i=0;i<index;i++){
            list = list.next;
        }
        //到达要删除的目标
        list.prev.next = list.next;
        list.next.prev = list.prev;
        size--;

    }
}

/**
 * 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);
 */

反转链表

建议先看我的视频讲解,视频讲解中对 反转链表需要注意的点讲的很清晰了,看完之后大家的疑惑基本都解决了。

题目链接/文章讲解/视频讲解:代码随想录

看到题目的第一想法

以前接触过,用头插法

看到代码随想录之后的想法

       卡哥提出了双指针

  
自己实现过程中遇到的困难(待补充,代码注释里有标注)

         建议结合图像来实现,比较清晰

        1 头插法出现的问题        卡顿的原因把题目想复杂了本来是想着用newHead先指向head,再头插(newHead.next=head)这样实现困难,其实只需要把newHead=null视为一个新的头节点的空链表,然后再对它挨个进行头插就行了

        2 卡哥的方法,双指针,要记得head元素的pre应该指向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) {
        //对每个节点使用头插法最后返回原head节点
        ListNode newHead = new ListNode();
        //添加一个虚拟头节点
        //对这个虚拟节点进行头插,先用一个节点定位前一个节点,
        //不需要用前一个节点连接后续节点,只需要一个指针找到旧的节点就行了
        //卡顿的原因把题目想复杂了本来是想着用newHead先指向head,再头插(newHead.next=head)这样实现困难
        newHead.next = null;
        ListNode node = head;
        ListNode temp = new ListNode();
        while(node!=null){
            temp = node.next;
            node.next = newHead.next;
            newHead.next = node;
            node = temp;
        }
        return newHead.next;

    }
}*/
//卡哥书上的思路实现
class Solution {
    public ListNode reverseList(ListNode head) {
        //双指针,当往前走时,每次用后一个指针指向前一个指针
        //建议画一张草图,根据草图的思路来判断要怎么走
        ListNode pre = null;
        ListNode cur = head;
        ListNode temp;
        while(cur!=null){
            temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;

    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值