算法修炼Day03|● 203.移除链表元素 ● 707.设计链表 ● 206.反转链表

56 阅读4分钟

链表理论基础

这段代码定义了一个名为ListNode的公共类。该类具有三个成员变量:val(表示节点的值)、next(表示指向下一个节点的指针)和构造函数。

  1. int val;:这是一个整型变量,用于存储节点的值。
  2. ListNode next;:这是一个指向下一个节点的指针。它是一个ListNode类型的变量,用于构建链表结构。
  3. ListNode() {}:这是一个无参构造函数,用于创建一个空的ListNode对象。
  4. ListNode(int val):这是一个带有一个参数的构造函数,用于创建一个具有指定值的ListNode对象。
  5. ListNode(int val, ListNode next):这是一个带有两个参数的构造函数,用于创建一个具有指定值和指向下一个节点的指针的ListNode对象。

在这段代码中,它定义了一个基本的链表节点类,用于构建链表数据结构。每个节点都包含一个值和一个指向下一个节点的指针。这个类可以用来创建链表,并进行节点的插入、删除和遍历等操作。

// 定义单链表
public class ListNode {
    int val; // 当前节点值
    ListNode next; // 下一个节点
    public ListNode() {} // 无参构造函数,创建一个空的ListNode对象
    public ListNode(int val) { // 有一个参数的构造函数,创建一个指定值为val的ListNode对象
        this.val = val; 
    }
    public ListNode(int val, ListNode next) { // 有两个参数的构造函数,创建一个具有指定值val和指向下一个节点的指针的ListNode对象
        this.val = val;
        this.next = next;
    }
}

// 又敲了几遍
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;
    }
}

题目:203. 移除链表元素 - 力扣(LeetCode)

思考:

方法一: 老久不做好陌生,先循环处理头节点,后定义临时节点进行循环遍历判断处理,这个过程操作的就是目标链表!!

方法二: 直接设置虚拟头节点忽略单个头节点的影响,后续循环遍历、判断是否相等、更换节点指针指向操作和方法一一致。

代码实现:

/**
 * 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) {
        while (head != null && head.val == val) {
            head = head.next;
        }
        if (head == null) {
            return head;
        }
        // 以上逻辑处理头节点内容
        
        // 以下逻辑处理值不为val头节点后的节点
        ListNode prev = head;
        while (prev.next != null) {
            if (prev.next.val == val) {
                prev.next = prev.next.next;
            } else {
                prev = prev.next;
            }
        }
        return head;
    }
}

// 方法二:虚拟头节点
// class Solution {
//     public ListNode removeElements(ListNode head, int val) {
//         ListNode dummyHead = new ListNode(0, head); // 创建一个指向链表头节点的虚拟头节点 dummyHead,并赋值为0
//         ListNode cur = dummyHead; // 临时节点指向虚拟头节点进行遍历,同时改变链表内的指针
//         while (cur.next != null) {
//             if (cur.next.val == val) {
//                 cur.next = cur.next.next;
//             } else {
//                 cur = cur.next;
//             }
//         }
//         return dummyHead.next;
//     }
// }

题目:707. 设计链表 - 力扣(LeetCode)

代码实现:

// 定义一个单链表
class ListNode {
    int val;
    ListNode next;
    ListNode() {

    }
    ListNode(int val) {
        this.val = val;
    }
}

class MyLinkedList {
    int size; // 存储链表元素的个数
    ListNode head; // 虚拟头节点
    
    public MyLinkedList() { // 构造方法,初始化链表
        size = 0; // 将链表大小初始化为 0
        head = new ListNode(0); // 创建一个带有 0 值的头节点
    }
    
    public int get(int index) { // 获取下标为 index 的节点的数值,注意 index 是从 0 开始的,第 0 个节点就是头节点
        if (index < 0 || index >= size) {
            return -1;
        }
        ListNode currentNode = head; // 包含一个虚拟头节点
        for (int i = 0; i <= index; i++) { // 所以查找下标为 index 的节点
            currentNode = currentNode.next;
        }
        return currentNode.val;
    }
    
    public void addAtHead(int val) { // 在链表的最前面插入一个节点,等价于在第 0 个元素前添加
        addAtIndex(0, val);
    }
    
    public void addAtTail(int val) { // 在链表的尾部添加一个节点,等价于在(末尾+1)个元素前添加
        addAtIndex(size, val);
    }
    
    public void addAtIndex(int index, int val) {
        if (index > size) {
            return;
        }
        if (index < 0) {
            index = 0;
        }
        size++;
        ListNode prev = head; // 前驱节点用于遍历
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }
        ListNode toAdd = new ListNode(val); // 插入节点的值赋予 val 值
        toAdd.next = prev.next;
        prev.next = toAdd;
    }
    
    public void deleteAtIndex(int index) { // 删除指定下标 index 位置的节点
        if (index < 0 || index >= size) { // 如果下标不合法,直接返回
            return;
        }
        size--; // 链表大小减 1
        if (index == 0) { // 要删除的是头节点
            head = head.next; // 将头节点指向下一个节点
            return;
        }
        ListNode prev = head; // 删除非头节点的前驱节点
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }
        prev.next = prev.next.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. 反转链表 - 力扣(LeetCode)

代码实现

// 双指针法
class Solution {
    public ListNode reverseList(ListNode head) {
        // 双指针
        ListNode cur = head; 
        ListNode prev = null;
        while (cur != null) {
            ListNode temp = cur.next;
            cur.next = prev;
            prev = cur;
            cur = temp;
        }
        return prev;
    }
}

// 递归法
class Solution {
    public ListNode reverseList(ListNode head) {
        return reverse(head, null);
    }
    private ListNode reverse(ListNode cur, ListNode prev) {
        if (cur == null) return prev;
        ListNode temp = cur.next;
        ListNode res = reverse(temp, cur);
        cur.next = prev;
        return res;
    }
}