代码随想录算法训练营第三天 | LeetCode203.移除链表元素、LeetCode707.设计链表、LeetCode206.反转链表

一、链表介绍

关于链表这块,其实个人我感觉如果是单纯从“面向对象”的思维去考虑的话,会容易把自己搞混(也有可能是因为当年学链表的时候看的是C的代码),所以其实这块可以去看一下C语言关于链表的介绍:

图源《2022年​​​​​王道数据结构考研复习指导》

简单来讲,一个节点其实分为两个部分:data和next。对于Java来说,我们将一个节点(Node)看作为一个对象,这个对象包括两个属性,一个属性是节点值(val),另一个属性是下一个节点(在Java中,称作“引用”,引用的数据类型也是Node(next))。

综上,其实我们可以将节点,定义为一个类,如下:

private static class Node {
    int value;
    Node next;

    public Node(int value, Node next) {
        this.value = value;
        this.next = next;
    }
}

而无论是单链表,还是双链表,我们可以将“链表(LinkedList)”视作一个对象,每一个链表的对象其实是由节点对象(Node)组成的。为了方便链表的操作,我们一般会给链表设置一个头节点,头节点对于链表来说就是一个成员变量。

综上,我们可以定义出链表(单向链表)对象:

public class LinkedList {
    private Node head;
}

这样或许会对 LeetCode707.设计链表 理解更加深刻。


其实我自己对于Java面向对象这块理解也不是很透彻。


二、LeetCode203.移除链表元素

阅题思路:思路来自 《2022年​​​​​王道数据结构考研复习指导》。

步骤拆解:

代码如下:

public static ListNode removeElements(ListNode head, int val) {
        // 构造虚拟头节点,将虚拟节点的下一个节点指向头节点
        ListNode dummy = new ListNode(-1, head);
        // 判断极限情况
        if (head == null) {
            return head;
        }
        // 定义节点指向当前的链表里的节点
        ListNode curr = head;
        // 定义当前节点的前驱节点
        ListNode pre = dummy;
        while (curr != null) {
            if (curr.val != val) {
                // 当前节点指向的节点值不等与目标值,向后移动
                curr = curr.next;
                pre = pre.next;
            } else {
                // 相等就移除当前cur指向的元素
                pre.next = curr.next;
                curr = curr.next;
            }
        }
        return dummy.next;
    }

三、LeetCode707.设计链表

阅题思路:这道题的关键在于对整个的Java面向对象的考察,怎么定义一个MyLinkedList对象,并为这个对象设置一些方法。其中最取巧的在于addAtHead和addAtTail这两个方法,相当于直接在指定的索引位置插入节点。(源于《代码随想录》介绍)

代码如下:

class MyLinkedList {
    // 初始化长度
    int size;
    // 初始化头节点
    ListNode head;

    /**
     * 初始化链表对象
     */
    public MyLinkedList() {
        size = 0;
        head = new ListNode(-1);
    }

    /**
     * 获取链表中指定索引的节点的值
     *
     * @param index
     * @return
     */
    public int get(int index) {
        // 校验索引合法性
        if (index < 0 || index >= size) {
            return -1;
        }
        // 定义一个移动节点,初始值为head(保障head永远指向构造出来的链表)
        ListNode pre = head;
        for (int i = -1; i < index; i++) {
            pre = pre.next;
        }
        return pre.val;
    }

    public void addAtHead(int val) {
        // 在头插入相当于在索引为0的位置插入
        addAtIndex(0, val);
    }

    public void addAtTail(int val) {
        // 在尾插入相当于在索引为size的位置插入
        addAtIndex(size, val);
    }

    /**
     * 在指定的索引位置插入节点
     * @param index
     * @param val
     */
    public void addAtIndex(int index, int val) {
        // 校验索引合法性
        if (index > size) {
            // 大于长度不插入
            return;
        }
        if (index < 0) {
            // 索引位置小于0,也不插入
            return;
        }
        size++;
        // 定义一个前驱节点
        ListNode pre = head;
        // 插入新的节点
        // 插入新的节点相当于在遍历到当前的节点后加了后继节点
        for (int i = 0; i < index; i++) {
            pre = pre.next;
        }
        // 插入新节点
        // 构造新的节点
        ListNode newNode = new ListNode(val);
        newNode.next = pre.next;
        pre.next = newNode;
    }

    /**
     * 删除指定索引的节点
     *
     * @param index
     */
    public void deleteAtIndex(int index) {
        // 校验索引合法性
        if (index < 0 || index >= size) {
            return;
        }
        size--;
        ListNode pre = head;
        for (int i = 0; i < index; i++) {
            pre = pre.next;
        }
        pre.next = pre.next.next;
    }
}

四、LeetCode206.反转链表

阅题思路:根据链表的特性,翻转一个链表其实是改变每一个节点后继节点的指向;

步骤拆解:

 代码如下:

public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while (cur != null) {
            // 开始翻转
            // 保存当前节点的后继
            ListNode temp = cur.next;
            // 翻转步骤
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }

  • 12
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值