随想录训练营Day3 | 链表基础, 707. 设计链表, 206. 反转链表,203. 移除链表元素

202 阅读4分钟

随想录训练营Day3 | 链表基础, 707. 设计链表, 206. 反转链表,203. 移除链表元素

标签: LeetCode闯关记


我的扫盲贴: 链表的理论基础 三种:单链表,双链表,循环链表; 链表定义:

public class ListNode {
    //结点的值
    int val;
    
    //下一个结点
    ListNode next;

    //节点的无参构造器
    public ListNode() {
    }
    
    //节点的构造器(一个参数)
    public ListNode(int val) {
        this.val = val;
    }
    
    //节点的构造器(两个参数)
    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

删除单链表元素: especially, 考虑头结点的特殊性,所以添加虚拟节点.

//添加虚拟节点以统一删除方式
public class ListNode {
    //结点的值
    int val;

    //下一个结点
    ListNode next;

    //节点的无参构造器
    public ListNode() {
    }

    //节点的构造器(一个参数)
    public ListNode(int val) {
        this.val = val;
    }

    //节点的构造器(两个参数)
    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
    public ListNode removeElement(ListNode head, int val){
        if(head == null){
         return head;
        }
        ListNode dummy = new ListNode(-1,head);
        ListNode pre = dummy;
        ListNode cur = head;
        while(cur != null){
            if(cur.val == val){
                pre.next = cur.next;
                pre = cur;
            }else{
                pre = cur;
            }
            cur = cur.next;
        }
        return dummy.next;
    }

**1. 707. 设计链表 ** 1.1 题目 相关链接: 707. 设计链表

坑点:假设链表中的所有节点下标从 0 开始。 因为一般,设置dummy节点的操作,ListNode dummy = new ListNode(-1, head);

###1.2 解题思路 链表的基础操作:增删查;

  • 增: 先寻找第n-1个结点,再插入,重点:注意插入顺序;
  • 删: 先寻找第n-1个结点,再;是删除,直接将指针指向后一个结点;
  • 查: ①在头结点之前;②在尾结点;③在链表的任何位置; 通过构造虚拟头结点,使所有情况统一运用一种方法:即寻找第n-1个结点,以此访问第n个结点;

###1.3 遇到问题 问题很多,特别是for循环的循环条件的边界值,很痛苦 问题代码如下:

//单链表
class ListNode{
    int val;
    ListNode next;
    ListNode(){};
    ListNode(int val){
        this.val =val;
    }
}

class MyLinkedList {
    int size;
    ListNode dummyHead;
    
    //初始化链表
    public MyLinkedList(){
        size = 0;
        dummyHead = new ListNode(0);//我看卡哥解题思路的话,这里的index=0时,是找的虚拟头结点;
    }


    public int get(int index) {//链表有下标? 没得,但是可以用for循环得到第index个
        //合法性检验
        if(index < 0 || index >= size){
            return  -1;
        }
        ListNode cur = dummyHead;

        //搞清楚所有节点下标从"0"开始,有虚拟头结点,如何找到第n个结点,很重要!
        //取值问题,考虑极端情况,如,index = 0时,其实是找除虚拟头结点外第一个结点;
        //以下的取值范围满足极端情况,则通过检验;
        for(int i = 0; i<= index; i++){
            cur =cur.next;
        }
        return cur.val;
    }
    //在链表的头结点前(注意,不是虚拟头结点)插入一个节点
    public void addAtHead(int val) {
        ListNode p = new ListNode(val);
        
        //插入顺序很重要,详见讲解视频
        p.next = dummyHead.next;
        dummyHead.next = p;
        size++;
    }
    //在链表的最后插入一个节点
    public void addAtTail(int val) {
        ListNode cur = dummyHead;
        ListNode q = new ListNode(val);
        for(int j = 0; j <= size - 1; j++){ //??? 
            cur = cur.next;
        }//使cur指向链表的最后一个元素
        cur.next = q;
        size++;
    }


    //区分:(???????????)
    //index:从真实头结点开始算,以0开始;(不算虚拟头结点);
    //size:从真实头结点开始算,以1开始;
    //所以,index = size -1;
    //但是在真实头结点之前,还有一个虚拟头结点;
    public void addAtIndex(int index, int val) {
        if(index < 0 || index > size - 1){//不懂这里index > ?  → 懂了
            return;
        }
        ListNode p = new ListNode(val);
        ListNode cur = dummyHead;
        //找到第index-1的元素
        for(int i = 0; i <= index - 1; i++){
            cur = cur.next;
        }
        //顺序很重要,详见讲解视频
        p.next = cur.next;
        cur.next = p;
        size++;
    }

    public void deleteAtIndex(int index) {
        ListNode cur = dummyHead;
        //找到第index-1的元素
        for(int i = 0; i <= index - 1; i++){
            cur = cur.next; 
        }
        cur.next =cur.next.next;
        size--;

    }
}

报错: 结果错误(没找到原因) (暂时贴不出来图,待更新) ###1.4 算法实现 算法分析 待修改后上传;

注意点: 虚拟头结点引入的原因: 单链表的特殊性_____只能指向下一个节点,需要考虑删除的是头结点这种特殊情况.

###1.5 题目总结 解题耗时:1.5h + 无穷无尽的看边界条件 ###1.6 相关题目


##2. 206. 反转链表 ###2.1 题目 题目:206. 反转链表 讲解视频: www.bilibili.com/video/BV1nB… ###2.2 解题思路 ###2.3 遇到问题 无 ###2.4 实现代码 法1; 双指针法

class Solution {
    public ListNode reverseList(ListNode head) {
       //初始化:注意为什么pre得是null;
       ListNode pre = null;
       ListNode cur = head;
       ListNode temp = null;
       //双指针
       while(cur != null){//注意终止条件
            temp = cur.next; //交换的顺序很重要,忘记的话看讲解视频
            cur.next = pre;
            pre = cur;
            cur = temp;
       }
       return pre;
    }
}

注意点:

法2:根据双指针法抽象得到的递归法

//对照双指针方法写下的递归,感觉跟我遇到的普通递归很不一样,这里的递归边界条件是cur == null;递归体是指针指向位置的变化,递归函数的代入值的改变完成了双指针方法中两个指针的前进.
class Solution {
    public ListNode reverseList(ListNode head) {
        return reverse(null, head);
    }
    private ListNode reverse(ListNode pre, ListNode cur){
        ListNode temp = null;
        if(cur == null){return pre;}//递归边界条件
        temp = cur.next;// 先保存下一个节点
        cur.next = prev;// 反转
        // 更新prev、cur位置
        // prev = cur;
        // cur = temp;
        return reverse(cur, temp);//递归函数的代入值的改变完成了双指针方法中两个指针的前进
    }
}

###2.5 题目总结 双指针!

###2.6 相关题目


##**3.203. 移除链表元素 ** ###3.1 题目 相关链接 203. 移除链表元素

###3.2 解题思路 永远记住,链表相关问题,永远是找到index - 1 的元素,以此操作index元素 用pre指针来找index-1 元素; ###3.3 遇到问题 无 ###3.4 算法实现

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode dummy = new ListNode(-1, head);
        ListNode pre = dummy;
        ListNode cur = head;
        while(cur != null){
            if(cur.val == val){
                pre.next = cur.next;//永远记住,链表相关问题,永远是找到index - 1 的元素,以此操作index元素
            }else{
                pre = cur;
            }
            cur = cur.next;
        }
        return dummy.next;

    }
}

注意点:待更新

###3.5 题目总结

###3.6 相关题目


##4. 今日心得 设计链表令人疲惫..... 这格式.......emmmmm找个时间全部更新一下,太恶心额.