day3| 203.移除链表元素、 707.设计链表、206.反转链表

203.移除链表元素

题目链接:https://leetcode.cn/problems/remove-linked-list-elements/discussion/
文章地址:https://programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE
视频地址:
https://www.bilibili.com/video/BV18B4y1s7R9/?spm_id_from=333.788&vd_source=721f65ae0501389782be0dcb48a2c421

public class RemoveElements {

	/**
	 * 添加虚节点方式
	 * 时间复杂度 O(n)
	 * 空间复杂度 O(1)
	 * @param head
	 * @param val
	 * @return
	 */
    public ListNode removeElements(ListNode head, int val) {
        if (head == null) {
            return head;
        }
        
        // 创建虚拟节点(dummy),将其 next 指针指向头节点
        ListNode dummy = new ListNode(-1, head);
        // 初始化前一个节点指针 pre 和当前节点指针 cur
        ListNode pre = dummy;
        ListNode cur = head;

        while (cur != null) {
            if (cur.val == val) {
                // 如果当前节点的值等于目标值,则将前一个节点的 next 指针跳过当前节点,指向当前节点的下一个节点,
                // 实现删除操作
                pre.next = cur.next;
            } else {
                // 如果当前节点的值不等于目标值,则更新前一个节点指针 pre 为当前节点
                pre = cur;
            }
            // 将当前节点指针 cur 指向下一个节点,继续遍历链表
            cur = cur.next;
        }
        
        // 返回虚拟节点 dummy 的下一个节点,即删除操作后的链表头节点
        return dummy.next;
    }
}

小结

从内存地址的角度来分析 ListNode pre = dummy; 和 ListNode cur = head; 代码:

ListNode pre = dummy;:这行代码创建了一个新的引用变量 pre,它被赋值为 dummy 的内存地址。dummy 是一个 ListNode 对象,它在内存中有自己的地址。通过将 dummy 的地址赋值给 pre,pre 指向了和 dummy 相同的内存位置。因此,pre 和 dummy 引用的是同一个 ListNode 对象。

ListNode cur = head;:这行代码创建了另一个引用变量 cur,它被赋值为 head 的内存地址。head 是一个 ListNode 对象,它在内存中也有自己的地址。通过将 head 的地址赋值给 cur,cur 指向了和 head 相同的内存位置。因此,cur 和 head 引用的是同一个 ListNode 对象。

总结来说,ListNode pre = dummy; 和 ListNode cur = head; 这两句代码都是创建了新的引用变量,并将其分别指向了原有对象的内存地址。这样做是为了能够在后续的操作中对链表进行遍历并删除元素。

在本地的测试用例需要自己创建单链表,进行创建链接添加元素后才能做这道移除元素的题。头结点的作用用来标识单链表

707.设计链表

题目链接:https://leetcode.cn/problems/design-linked-list/
文章链接:https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html添加链接描述
视频链接:https://www.bilibili.com/video/BV1FU4y1X7WD/?vd_source=721f65ae0501389782be0dcb48a2c421

1、单链表节点

//创建(单链表)节点
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;
    }
}

2、设计单链表


//单链表
public class MyLinkedList {
    //size:存储链表元素的个数
    int size;

    //虚拟头结点
    ListNode head;

    //初始化链表,虚拟头结点指向给链表
    public MyLinkedList(){
        this.size=0;
        this.head=new ListNode(0);
    }

    //1、获取第index个节点的数值,注意index是从0开始的,第0个节点就是头结点
    public int get(int index){
        //如果index非法,返回-1
        if (index < 0 || index >= size){
            return -1;
        }
        ListNode currentNode = head;
        //包含虚拟节点,所以查找第index+1个节点
        for (int i = 0; i <= index; i++) {
            currentNode = currentNode.next;
        }
        return currentNode.val;
    }


    // 2、在第 index 个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
    // 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
    // 如果 index 大于链表的长度,则返回空

    public void addAtIndex(int index,int val){

        //检验index的合法性
        if(index>size){
            return;
        }

        //如果小于0的索引默认添加到头部位置
        if (index<0){
            index = 0;
        }

        //链表长度加1
        size++;

        ListNode pre = head;

        //找到要插入index位置的前节点
        for (int i = 0; i < index; i++) {
            pre = pre.next;    //最后的pre存储的是要插入index位置前节点的内存地址
        }

        //创建要添加的节点
        ListNode toAdd = new ListNode(val);

        toAdd.next = pre.next;
        pre.next = toAdd;

    }

    //3、在链表最前面插入一个节点,等价于在第0个元素前添加
    public void addAtHead(int val){
        this.addAtIndex(0,val);
    }

    //4、在链表的最后插入一个节点,等价于在(末尾+1)个元素前添加
    public void addAtTail(int val){
        addAtIndex(size,val);
    }

    //删除第index个节点
    public void deleteAtIndex(int index){
        //验证index合法性
        if (index < 0 || index >= size) {
            return;
        }
        //减少链表的长度
        size--;

        if (index == 0){
            head = head.next;
            return;
        }

        ListNode pre = head;
        for (int i = 0; i < index; i++) {
            pre = pre.next;
        }

        pre.next = pre.next.next;
        
    }

}

3、双链表节点

//创建双链表节点
public class DoubleListNode {
    //data域
    int val;

    //前后两个指针,其实也就是两个node节点的变量,存储的是链接node的内存地址
    //pre:指向上一个节点
    //next:指向下一个节点
    DoubleListNode next,pre;

    public DoubleListNode() {
    }

    public DoubleListNode(int val) {
        this.val = val;
    }
}

4、设计双链表

//双链表
public class MyDoubleLinkedList {

    //记录链表中元素的数量
    int size;

    //记录链表的虚拟头结点和尾结点
    DoubleListNode head, tail;

    //1、初始化一个双链表
    public MyDoubleLinkedList() {
        size = 0;
        head = new DoubleListNode(0);
        tail = new DoubleListNode(0);
        head.next = tail;
        tail.pre = head;
    }

    //2、在index位置上添加元素
    public void addAtIndex(int index, int val) {
        //验证Index的合法性
        if (index > size) {
            return;
        }
        //如果小于0统一默认在第0个 位置插入
        if (index < 0) {
            index = 0;
        }

        //链表长度加1
        size++;

        DoubleListNode pre = head;

        //如果index = 0 的情况,不需要再寻找前驱节点,跳过for循环
        for (int i = 0; i < index; i++) {
            pre = pre.next;
        }

        //新建节点
        DoubleListNode newNode = new DoubleListNode(val);
        //链接总共需要操作4条线
        pre.next = newNode;
        newNode.pre = pre;
        newNode.next = pre.next.next;
        pre.next.pre = newNode;

    }

    //3、在第0个元素前添加
    public void addAtHead(int val) {
        this.addAtIndex(0, val);
    }
    //4、在最后一个元素(Null)前添加
    public void addAtTail(int val){
        this.addAtIndex(size,val);
    }

    //5、获取index位置上的值
    public int get(int index){
        //验证index的合法性,注意这是在查找的索引
        if (index<0 || index >= size){
            return -1;
        }

        DoubleListNode cur = head;
        //判断是哪一边遍历时间更短
        if (index >=size/2){
            //从尾指针开始
            cur = tail;
            for (int i = 0; i < size - index; i++) {
                cur = tail.pre;
            }
        }else {
            for (int i = 0; i <= index; i++) {
                cur = head.next;
            }
        }

        for (int i = 0; i < index+1; i++) {
            cur = head.next;
        }

        return cur.val;

    }

    //6、删除index位置的元素
    public void deleteAtIndex(int index){
        //验证index的合法性
        if (index <0 || index >=size){
            return;
        }

        //链表长度减1
        size--;

        DoubleListNode pre = head;
        for (int i = 0; i <index; i++) {
            pre = pre.next;
        }

        pre.next = pre.next.next;
        pre.next.next.pre = pre;


    }


}

206.反转链表

题目链接:https://leetcode.cn/problems/reverse-linked-list/
文字链接:https://programmercarl.com/0206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE
视频地址:https://www.bilibili.com/video/BV1nB4y1i7eL/?vd_source=721f65ae0501389782be0dcb48a2c421

双指针写法

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

    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; // 返回反转后的头节点
    }

}

递归写法

public class ReverseList {

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

    private ListNode reverse(ListNode prev, ListNode cur) {
        if (cur == null) {
            return prev;
        }
        ListNode temp = null;
        temp = cur.next;// 先保存下一个节点
        cur.next = prev;// 反转
        // 更新prev、cur位置
        // prev = cur;
        // cur = temp;
        return reverse(cur, temp);
    }
}
    


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值