关于Java链表的一些操作以及力扣原题刷刷刷——反转链表、删除链表的倒数第N个节点

1、反转链表

1.1 环境准备,可以自己先尝试实现

/**
 * @Author Miku
 * @Date 2024/09/02 09:54
 * @Description TODO
 * @Version 1.0
 */
public class Solution {
    static class ListNode {
        int val;
        ListNode next;

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

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

    public ListNode reverseList(ListNode head) {
        // 补充实现代码

        return null;
    }

    public static void main(String[] args) {
        ListNode head = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5)))));
        Solution solution = new Solution();
        ListNode result = solution.reverseList(head);
        printList(result);  // 输出:5 -> 4 -> 3 -> 2 -> 1
    }

    private static void printList(ListNode head) {
        while (head != null) {
            System.out.print(head.val);
            head = head.next;
            if (head != null) System.out.print(" -> ");
        }
        System.out.println();
    }

}

1.2 实现思路

在这里插入图片描述
实际我们看到的这个链表并不完整,完整的链表结尾应该有null,我们遍历链表的时候也是判断当前指针是否是null,为null时代表结束了,所以我们补充完整上图得到:
在这里插入图片描述

得到完整之后我们再来想如何反转

a. 了解两个节点之间的关系

如图所示:
反转前:节点1指向节点2
反转后:节点1指向null,节点2指向节点1

现在我们只能操作一个节点,一般都是操作head(头节点),我们需要思考一件事,就是怎么凑齐反转后的那些变量
在这里插入图片描述

首先:head头节点可以看作是节点1,当时null和节点2是我们需要新建的变量,为了方便理解,
我们可以用新变量 cur 来表示当前的节点,即在一开始 ListNode cur = head;
所以我们还需要添加两个变量,分别是 pre(上一个) 和 next(下一个),其中 pre = null,next = cur.next
public ListNode reverseList(ListNode head) {
        // 补充实现代码
        ListNode pre = null;
        ListNode cur = head;
        ListNode next = cur.next;

        return null;
}

b. 扩展一个节点

在这里插入图片描述

如图可以知道,一开始 cur 的上一个节点为null,但是当 cur 为节点2时,他的上一个节点就变成了节点1,
所以 pre 和 next 需要跟着我们的 cur 改变而改变,初始化为 pre,next = cur.next
public ListNode reverseList(ListNode head) {
    // 补充实现代码
    ListNode pre = null;
    ListNode cur = head;
    while(cur != null) {
        // next 需要随着 cur 的变化而变化,所以放循环里面
        ListNode next = cur.next;
        
        // pre 需要随着 cur 的变化而变化,所以也要放循环里面
        pre = cur;
    }
    return null;
}

c. 执行反转操作

在这里插入图片描述

了解上述之后我们就可以来写反转操作了
执行反转操作无非就是将 cur 的下一个指针指向 pre 罢了
(原来指向下一个的变成指向上一个就是反转了)指向下一个就是cur.next,指向上一个前面保存在 pre 了
这一步操作需要在 pre 发生改变之前执行
// 将 cur 的下一个指针指向 pre,这一步操作需要在 pre 发生改变之前执行
cur.next = pre;
反转之后为了让循环继续,把当前指针变为 next ,注意这个应该在前面都完成的基础上最后一步执行
// 反转已经执行完毕了,该转移到下一个节点了,所以是循环的最后一步
cur = next;

完整代码

public ListNode reverseList(ListNode head) {
    // 补充实现代码
    ListNode pre = null;
    ListNode cur = head;
    while (cur != null) {
        // next 需要随着 cur 的变化而变化,所以放循环里面
        ListNode next = cur.next;

        // 将 cur 的下一个指针指向 pre,这一步操作需要在 pre 发生改变之前执行
        cur.next = pre;

        // pre 需要随着 cur 的变化而变化,所以也要放循环里面
        pre = cur;

        // 反转已经执行完毕了,该转移到下一个节点了,所以是循环的最后一步
        cur = next;
    }
    // 这一步纯粹是为了方便理解,形成闭环(大白话就是让我们理解最终 pre 就是 head ,因为 cur 为 null,结合图可理解)
    head = pre;
    return head;
}

在这里插入图片描述

2、删除链表的倒数第N个节点

力扣原题:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/

2.1 环境准备,可以自己先尝试实现

/**
 * @Author Miku
 * @Date 2024/09/02 09:54
 * @Description TODO
 * @Version 1.0
 */
public class Solution {
    static class ListNode {
        int val;
        ListNode next;

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

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

    public ListNode removeNthFromEnd(ListNode head, int n) {
        // 补充实现代码

        return null;
    }

    public static void main(String[] args) {
        ListNode head = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5)))));
        Solution solution = new Solution();
        ListNode result = solution.removeNthFromEnd(head, 2);
        printList(result);  // 输出:1 -> 2 -> 3 -> 5
    }

    private static void printList(ListNode head) {
        while (head != null) {
            System.out.print(head.val);
            head = head.next;
            if (head != null) System.out.print(" -> ");
        }
        System.out.println();
    }

}

2.2 实现思路

a. 扫描删除

扫描一遍链表,得到总长后,根据计算可以得出要删除的是正向的第几个元素,然后删除即可

// 计算总长度 len
int len = 0;
while(head != null) {
    ++ len;
    head = head.next;
}

在这里插入图片描述

// 遍历到第 n 个元素
for(int i = 1; i < len - n; i ++) {
    head = head.next;
}
// 删除(链表中的删除实际就是将连接的节点跳过一个即可达到删除目的)
head.next = head.next.next;

走到这里应该有兄弟发现有问题了

  1. 前面计算总长度的时候head节点就已经不在原来的位置了,这里的head都在结尾了,根本不能用head从头遍历,我后面还要拿着头指针从头遍历呢~
  2. 如果只有一个节点,然后又要删除倒数第一个,那head.next.next 就是 null.next(空指针异常)

解决:

  1. 添加一个新变量cur = head,然后操作 cur 就好啦,这样就不会影响 节点head
  2. 加一个空值判断就可以避免空指针了
// 保存head,以便返回需要
ListNode ans = head;
 ListNode cur = head;
 int len = 0;
 while(head != null) {
     ++ len;
     head = head.next;
 }
// 使用临时变量遍历
 for(int i = 1; i < len - n; i ++) {
     cur = cur.next;
 }
 if(cur.next != null){
     cur.next = cur.next.next;
 }
 
 return ans;

在这里插入图片描述
一执行发现当只有一个元素的时候出错了
错误原因:删除第一个元素的时候,由于我们是从head开始的,所以没办法跳过head这个元素。
解决方案:我们新添加一个助力节点(官方叫哑节点dummy),使用一个节点帮忙后我们既可以通过它跳过head元素,也可以避免空指针现象

// 助力节点,指向头指针
ListNode dummy = new ListNode(0, head);
// 从助力节点开始遍历,避免空指针
ListNode cur = dummy;
int len = 0;
while(head != null) {
    ++ len;
    head = head.next;
}
// 因为多了助力节点,所以要 + 1(加的就是助力节点)
for(int i = 1; i < len - n + 1; i ++) {
    cur = cur.next;
}
// 多了助力节点,不会报空啦
cur.next = cur.next.next;
return dummy.next;

在这里插入图片描述

完整代码:

/**
 * @Author Miku
 * @Date 2024/09/02 09:54
 * @Description TODO
 * @Version 1.0
 */
public class Solution {
    static class ListNode {
        int val;
        ListNode next;

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

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

    public ListNode removeNthFromEnd(ListNode head, int n) {
        // 补充实现代码
        ListNode dummy = new ListNode(0, head);
        ListNode cur = dummy;
        int len = 0;
        while(head != null) {
            ++ len;
            head = head.next;
        }

        for(int i = 1; i < len - n + 1; i ++) {
            cur = cur.next;
        }
        cur.next = cur.next.next;
        return dummy.next;
    }

    public static void main(String[] args) {
        ListNode head = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5)))));
        Solution solution = new Solution();
        ListNode result = solution.removeNthFromEnd(head, 2);
        printList(result);  // 输出:1 -> 2 -> 3 -> 5
    }

    private static void printList(ListNode head) {
        while (head != null) {
            System.out.print(head.val);
            head = head.next;
            if (head != null) System.out.print(" -> ");
        }
        System.out.println();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨崇M柒

网络乞讨一下,嘿嘿嘿~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值