【代码随想录Day3】移除链表元素、设计链表、反转链表

链表理论基础

文章链接:链表理论基础

203.移除链表元素

题目链接/文章讲解/视频讲解::203.移除链表元素

方法一:在链表前面加一个虚拟头节点,方便处理 删除头结点 的操作。


注意:最后 return 头结点的时候,是 return dummyNode->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();
        dummy.next = head;
        ListNode cur = dummy;
        while (cur.next != null) {
            if (cur.next.val == val) {
                cur.next = cur.next.next;
            } else {
                cur = cur.next;
            }
        }
        return dummy.next;
    }
}

方法二:对删除头节点的情况单独处理

先处理头节点,再处理其他节点


注意:记得判断 head 和 cur 是否为 null

/**
 * 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;
        }
        ListNode cur = head;
        while (cur != null && cur.next != null) {
            if (cur.next.val == val) {
                cur.next = cur.next.next;
            } else {
                cur = cur.next;
            }
        }
        return head;
    }
}

707.设计链表

题目链接/文章讲解/视频讲解:707.设计链表
插入、删除的操作本身不难,难的是准确找到指针要移动到的位置。

// 节点类,用于表示链表中的节点
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); // 虚拟头节点,不存储有效数据
    }

    // 获取链表中第index个节点的值
    public int get(int index) {
        // 如果索引无效,返回-1
        if (index < 0 || index >= size) {
            return -1;
        }
        ListNode cur = head.next; // 从虚拟头节点的下一个节点开始
        // 遍历链表直到第index个节点
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        return cur.val; // 返回第index个节点的值
    }

    // 在链表头添加一个值为val的节点
    public void addAtHead(int val) {
        ListNode temp = new ListNode(val); // 创建新节点
        temp.next = head.next; // 新节点指向原先的第一个节点
        head.next = temp; // 虚拟头节点指向新节点
        size++; // 链表大小增加1
    }

    // 在链表尾添加一个值为val的节点
    public void addAtTail(int val) {
        ListNode cur = head; // 从虚拟头节点开始
        // 遍历到最后一个节点
        while (cur.next != null) {
            cur = cur.next;
        }
        ListNode temp = new ListNode(val); // 创建新节点
        cur.next = temp; // 最后一个节点指向新节点
        size++; // 链表大小增加1
    }

    // 在链表的第index个位置插入值为val的节点
    public void addAtIndex(int index, int val) {
        // 如果index小于0或大于链表长度,直接返回
        if (index < 0 || index > size) {
            return;
        } 
        if (index == 0) { // 如果index为0,在头部插入
            addAtHead(val);
        } else if (index == size) { // 如果index等于长度,在尾部插入
            addAtTail(val);
        } else { // 在链表中间插入
            ListNode pre = head; // 从虚拟头节点开始
            // 遍历到第index-1个节点
            for (int i = 0; i < index; i++) {
                pre = pre.next;
            }
            ListNode temp = new ListNode(val); // 创建新节点
            temp.next = pre.next; // 新节点指向第index个节点
            pre.next = temp; // 第index-1个节点指向新节点
            size++; // 链表大小增加1
        }
    }

    // 删除链表中第index个节点
    public void deleteAtIndex(int index) {
        // 如果索引无效,直接返回
        if (index < 0 || index >= size) {
            return;
        }
        ListNode pre = head; // 从虚拟头节点开始
        // 遍历到第index个节点的前一个节点
        for (int i = 0; i < index; i++) {
            pre = pre.next;
        }
        pre.next = pre.next.next; // 前一个节点指向第index+1个节点
        size--; // 链表大小减少1
    }
}

/**
 * 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.反转链表

题目链接/文章讲解/视频讲解:206.反转链表

方法一:使用头插法,将链表元素依次插入到新链表中。

/**
 * 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) {
        if (head == null) {
            return head;
        }
        ListNode dummy = new ListNode();
        ListNode cur = head;
        while (cur != null) {
            ListNode temp = new ListNode();
            temp.val = cur.val;
            temp.next = dummy.next;
            dummy.next = temp;
            cur = cur.next;
        }
        return dummy.next;
    }
}

我自己写的方法,正确,询问 GPT 后提出了一些问题:

  1. 使用多余的节点创建:每次循环都新建一个 ListNode,这并没有必要,并且会导致额外的空间复杂度。
  2. 无须使用哑结点:dummy 节点在这里并不必要,因为我们可以直接改变指针来达成反转链表的效果。
  3. 时间复杂度增大:新建节点和拷贝数据导致的开销,可以更简单直接地通过更改指针来实现。

GPT 建议用双指针法:

方法二:双指针

要用 temp 保存 curr.next 的位置,否则无法更新 curr 的位置。

/**
 * 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 prev = null;
        ListNode curr = head;
        while (curr != null) {
            ListNode nextTemp = curr.next; // 暂存当前节点的下一个节点
            curr.next = prev; // 将当前节点的next指向前一个节点
            prev = curr; // 将prev指针移动到当前节点
            curr = nextTemp; // 将curr指针移动到下一个节点
        }
        return prev; // 返回反转后的头结点
    }
}

方法三:递归

/**
 * 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) {
        // 基本情况:如果链表为空或只有一个节点,直接返回头节点
        if (head == null || head.next == null) {
            return head;
        }

        // 递归反转剩余的链表
        ListNode newHead = reverseList(head.next);

        // 处理当前节点的反转
        head.next.next = head;  // 例如:head -> node2, 变成 node2 -> head
        head.next = null;

        // 返回新的头节点
        return newHead;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值