代码随想录 Day3

题目列表

  • 203.移除链表元素(LeetCode)
  • 707.设计链表(LeetCode)
  • 206.反转链表(LeetCode)

解题过程

203.移除链表元素

题目描述

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

解题思路

链表在内存中不连续分布,依靠指针“连”在一起,删除也就相当于改变前一个结点的指针的指向为当前节点的后一个。

注意事项

1.设置虚拟头节点的作用

头节点没有前一个节点,为了统一使用一种处理方法,可以设置一个虚拟头节点,设置虚拟头节点的下一个为真实的头节点,这样即使要删除的是头节点,也可以和其它节点一样进行操作。

设置了虚拟头节点,循环条件可以简化为 current.next != null ,因为虚拟头节点是自己创建的,一定不为空。

2.什么时候结束寻找

从头节点一直检查到尾节点,尾节点的下一个节点是 null,也就是说 current.next == 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) {
        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;
    }
}

707.设计链表

题目描述

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。

如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。

实现 MyLinkedList 类:

MyLinkedList() 初始化 MyLinkedList 对象。
int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。

解题思路

  • get(index):需要根据index值设置循环条件,然后从头节点开始遍历找到对应的节点的val值。
  • addAtHead(int val):在头节点前插入一个节点。
  • addAtTail(int val):找到最后一个节点然后进行操作。
  • addAtIndex(int index, int val):在指定index前插入一个节点,考虑到index = 0, 可以借助虚拟头节点来让操作统一。
  • deleteAtIndex(int index):借助虚拟头节点来实现移除元素,这个要删除的节点是靠下标指定的。

注意事项

自定义链表要有 size 属性

为了检查边界值,插入指定位置的方法也需要用到,插入和删除之后要记得做 size 的变动。

要注意循环条件和 index 的关系

不同的方法中while循环的条件不同:

  • 比如get方法中是 index>=0,也就是说当index = 0 时也要执行一次 cur = cur.next,因为虚拟头节点的缘故,也因为要返回的是当前节点的 val 值。
  • 而 addAtIndex 和 deleteAtIndex 方法中则是 index > 0,也就是说,如果index = 0 就直接跳过循环(cur = cur.next),cur 就是 dummy。因为执行“在节点前插入”和“删除节点”都需要知道当前节点的前一个节点,这样才能维护链表。

代码展示

class MyLinkedList {

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

    private int size;
    private ListNode dummy;

    public MyLinkedList() {
        this.size = 0;
        this.dummy = new ListNode(-1);
    }
    
    public int get(int index) {
        if(index < 0 || index >=size){
            return -1;
        }
        ListNode cur = dummy;
        while(index >= 0){
            cur = cur.next;
            index--;
        }
        return cur.val;
    }
    
    public void addAtHead(int val) {
        ListNode newOne = new ListNode(val);
        //先维护链表
        newOne.next = dummy.next;
        dummy.next = newOne;   
        size++;
    }
    
    public void addAtTail(int val) {
        ListNode newOne = new ListNode(val);
        ListNode cur = dummy;
        while(cur.next != null){
            cur = cur.next;
        }
        //找到原始尾节点,插入
        cur.next = newOne;
        size++;
    }
    
    public void addAtIndex(int index, int val) {
    
        if(index > size){
            return;
        }
        if(index == size){
            addAtTail(val);
            return;
        }
        
        ListNode newOne = new ListNode(val);
        ListNode cur = dummy;
        while(index > 0){
            cur = cur.next;
            index--;
        }
        newOne.next = cur.next;
        cur.next = newOne;
        size++;
    }
    
    public void deleteAtIndex(int index) {
        if(index < 0 || index >=size){
            return;
        }
        ListNode cur = dummy;
        while(index > 0){
            cur = cur.next;
            index--;
        }
        cur.next = cur.next.next;
        size--;
    }
}

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

题目描述

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

解题思路

双指针,直接在原链表上操作。

注意事项

改变next前注意要维护好后面的节点

链表在内存中不连续,如果不先保存后面的节点,改变 next 后就无法找到原先的 next 了。

注意边界

只要cur不是null,就一直要反转,因此循环和递归的结束条件都是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 reverseList(ListNode head) {
        ListNode cur = head;
        ListNode pre = null;  //cur的前一个
        while(cur != null){
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
}*/
//递归法
class Solution {
    public ListNode reverseList(ListNode head) {
     
        return reverse(head,null);
    }
    public ListNode reverse(ListNode cur, ListNode pre){
        if(cur == null){
            return pre;
        }else{
            ListNode temp = cur.next;
            cur.next = pre;
            return reverse(temp,cur);
        }
    }
}

参考资料

代码随想录

posted @ 2025-04-25 18:31  cbdsszycfs  阅读(477)  评论(0)    收藏  举报