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

目录

今日内容:

203.移除链表元素

206.反转链表

707.设计链表


今天进入到“链表”算法内容,首先简单复习一下链表的基础内容:

  1. 链表分类:单链表(自己常用)、双链表、环形链表:关于双链表还有环形链表明白定义,但是不经常使用,常用且熟悉单链表。
  2. 单链表:链表里面的结点都由两部分组成,一个是存放数据的数据域,另外一个是存放指向下一个结点的指针,特别需要注意:单链表是单向的。
  3. *虚拟头结点的使用:对于在链表中涉及有可能会对头结点发生改变的操作,例如:删除结点还有插入结点,删除可能会删除头结点,插入也可能会从头结点前插入,那这样的话头结点会发生改变;删除结点和插入结点都要依靠此结点的前一个结点来帮助操作,因此遇到链表的相关题目首先思考一下是否需要使用“虚拟头结点”。

203.移除链表元素

题目链接:https://leetcode.cn/problems/remove-linked-list-elements/

思路:链表涉及到移除,那么需要设置虚拟头结点使过程具有一般化---由于头结点可能符合移除条件,那么就需要另外对头结点进行移除操作;在添加虚拟头结点后,就不需要另外进行判断,头结点与其他结点都一样的判断;虚拟头结点添加在头结点之前。

实现过程:由于移除结点需要借助此结点的前一个结点,那么设置头结点为当前(cur)结点,虚拟头结点为前一个结点(pre),对链表遍历,遍历结束条件是当cur指向为null时,判断是否移除的条件是看cur指向结点值与目标值是否相同,若同进行移除(pre.next=cur.next);若不同首先将pre移动到cur位置(pre=cur),这两种情况无论符合哪种都需要在进行以上操作后继续遍历(cur=cur.next),最后返回新的头结点,无论头结点是否改变都会是虚拟头结点的下一个结点,因此返回dummy.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 dummy=new ListNode(0,head);
        ListNode pre=dummy;
        ListNode cur=head;
        while(cur!=null){
        if(cur.val!=val){
            pre=cur;
        }else{
            pre.next=cur.next;
        }
        cur=cur.next;
        }
        return dummy.next;
    }
}

206.反转链表

题目链接:https://leetcode.cn/problems/reverse-linked-list/

第一想法:按照我看此题视角的链表是从左往右指向的,链表最后结点指向null,按题目要求要整个结点反转,那么就要改变指针指向,变成只是知道这个思路,但是具体如何实现是不知道的。

学习随想录后思路:

双指针,此题还可以用递归,但是我较能理解双指针所以选择。

反转链表改变指向,首先设置当前结点为头结点(cur),想到需要将cur指向结点的方向进行反转,从往右指变成往左指。但是改变方向后需要指向什么?所以需要(设一个在cur前的结点给改变方向进行操作,其实想想当cur到了其他结点位置时,改变方向后需要将指针变为往前指,那该如何表示),所以设置一个cur的前结点(pre),然后需要将cur指向pre,指向之后那第一个cur后就断了没有指针指向了,所以还需要设置一个结点temp用来存放cur.next;这个过程的结束条件是cur遍历到为null时,cur会遍历链表中每一个结点然后进行反转;

接下来是关键值的变化,首先要保存cur下一个结点和指向,用temp来存值,然后指向pre,开始疑惑为什么是这个指向?是因为后面pre变化到temp前一个结点,所以如此。然后就是pre和cur的变化,先要处理pre,因为若先处理cur那么cur=cur.next,那么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=null;
        
        while(cur!=null){
            temp=cur.next;
            cur.next=pre;
            pre=cur;
            cur=temp;
        }
        return pre;

    }
}

707.设计链表

题目链接:https://leetcode.cn/problems/design-linked-list/

这道题基本包括了链表的几个操作,是非常必要掌握的,其中包括:按照索引对链表进行索引值获取、删除、按索引插入。

思路:添加虚拟头结点;

了解到此题需要对链表结点进行删除和插入,所以添加虚拟头结点,首先除去从头尾插入(可以调用按照所以插入的方法);现在就按照索引对链表进行获取值、删除、插入值,三者共同点都是要按照索引并且是同一链表,所以假设在这条链表添上虚拟头结点,在添上后等于将这条链表整体往后移动了一个,所以在找到索引位置的判断条件也需要结合此来设定,那么需要考虑到这个索引被推后一个;获取值最需要注意的就是索引被推后一个的问题;删除在考虑索引需要遍历到要删结点的上一个结点,然后借助这个结点删除下一结点;在某个索引结点前插入结点,也要注意遍历到需要进行操作结点的前一个结点,然后借助这前一个结点插入新结点。这所有都要注意判断size的大小,还要记得链表具有容错性,因此在插入时,若size小于0也是可以的,会将它按照在等于0的位置操作。

class MyLinkedList {
    //定义链表中的属性
    int size;
    ListNode head;

    public MyLinkedList() {
        //初始化链表
        head=new ListNode(0);
        size=0;
    }
    
    public int get(int index) {
        if(index<0||index>=size){
            return -1;
        }
        ListNode cur=head;
        for(int i=0;i<=index;i++){
            cur=cur.next;
        }
        return cur.val;
    }
    
    public void addAtHead(int val) {
        addAtIndex(0,val);
    }
    
    public void addAtTail(int val) {
        addAtIndex(size,val);
    }
    
    public void addAtIndex(int index, int val) {
        ListNode pre=head;
        if(index<0){
            index=0;
        }
        if(index>=size){
            return;
        }
        size++;
        for(int i=0;i<index;i++){
            pre=pre.next;
        }
        ListNode newnode=new ListNode(val);
        newnode.next=pre.next;
        pre.next=newnode;
    }
    
    public void deleteAtIndex(int index) {
        ListNode pre=head;
        if(index>=size||index<0){
            return;
        }
        if(index==0){
            head=head.next;
            return;
        }
        size--;//删除结点总长就减小
        //因为添加了一个虚拟头结点,所以整个链表整体往后推一个,要注意索引
        for(int i=0;i<index;i++){
            pre=pre.next;
        }
        pre.next=pre.next.next;
    }
}

// class ListNode{
//     int val;
//     ListNode next;
//     void ListNode(){}
//     void ListNode(int val){
//         this.val=val;
//     }
//     void ListNode(int val,ListNode next){
//         this.val=val;
//         this.next=next;
//     }
// }


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

总结:

这三题总体算二刷,但是花了挺长时间,基本上又再理解一遍,特别是206.反转链表,此题的思路是理解的,但是在实现过程还是对于指向的代码编写不流畅,这方面的能力有待加强。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值