《代码随想录》刷题笔记——链表篇【java实现】

简介: 《代码随想录》刷题笔记——链表篇【java实现】

链表节点定义

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;
    }
}

*移除链表节点

题目链接:https://leetcode.cn/problems/reverse-linked-list/

【思路】

有时候增加一个虚拟节点,可以让后面的操作都一致

public static ListNode removeElements(ListNode head, int val) {
    if (head == null) {
        return null;
    }
    // 增加一个虚拟节点
    ListNode virtualNode = new ListNode();
    virtualNode.next = head;
    ListNode cur = virtualNode;
    ListNode next = cur.next;
    while (next != null) {
        if (next.val == val) {
            cur.next = next.next;
        } else {
            cur = next;
        }
        next = cur.next;
    }
    return virtualNode.next;
}

*反转链表

双指针

  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

public static ListNode reverseList(ListNode head) {
    ListNode cur = head;
    ListNode pre = null;
    ListNode temp = null;
    while (cur != null) {
        temp = cur.next;
        cur.next = pre;
        pre = cur;
        cur = temp;
    }
    return pre;
}

递归法

  • 时间复杂度: O(n)
  • 空间复杂度: O(n)

这里的逻辑和双指针法的逻辑是一样的,只是实现方式是递归,那为什么空间复杂度更高呢?

答:因为递归本质上是一种函数调用,在每一次递归调用时,都会在函数栈中分配一段内存空间来保存函数的局部变量、返回地址以及参数等信息。因此,递归实现相对于循环实现的空间复杂度更高,会占用更多的内存空间。尤其是在递归深度较大的情况下,可能会导致栈溢出等问题。而使用循环实现通常不需要使用额外的栈空间,因此循环实现的空间复杂度比递归实现要低

public static ListNode reverseList1(ListNode head) {
    return reverse(null, head);
}
public static ListNode reverse(ListNode pre, ListNode cur) {
    if (cur != null) {
        ListNode temp = cur.next;
        cur.next = pre;
        pre = reverse(cur, temp);
    }
    return pre;
}

*两两交换链表中的节点

https://leetcode.cn/problems/swap-nodes-in-pairs/

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

public static ListNode swapPairs(ListNode head) {
    // 如果链表中没有元素 或者 只有一个元素,直接返回就行
    if (head == null || head.next == null) {
        return head;
    }
    // 增加一个虚拟头节点
    ListNode virtualNode = new ListNode();
    virtualNode.next = head;
    ListNode last = virtualNode;
    ListNode cur = head;
    ListNode next = cur.next;
    ListNode temp = null;
    while (next != null) {
        // 交换
        temp = next.next;
        cur.next = next.next;
        next.next = cur;
        last.next = next;
        // 指针移动到新的位置
        last = cur;
        cur = temp;
        if (cur != null) {
            next = cur.next;
        } else {
            break;
        }
    }
    return virtualNode.next;
}

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

https://leetcode.cn/problems/remove-nth-node-from-end-of-list/submissions/

快慢指针法

/**
 * 让快指针比慢指针先移动 n 步
 *
 * @param head
 * @param n
 * @return
 */
public static ListNode removeNthFromEnd(ListNode head, int n) {
    ListNode virtual = new ListNode();
    virtual.next = head;
    // 让快指针先移动到n个节点
    ListNode fast = virtual;
    for (int i = 0; i < n; i++) {
        if (fast.next != null) {
            fast = fast.next;
        }
    }
    // slow和fast一起移动,等fast到达最后一个节点的时候,slow也就到达了要删除的节点前面的节点
    ListNode slow = virtual;
    while (fast.next != null) {
        slow = slow.next;
        fast = fast.next;
    }
    // 执行删除操作
    slow.next = slow.next.next;
    return virtual.next;
}
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

*链表相交

https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/

注意点

并不是判断val相等,而是hashcode相等

暴力求解

直接两重循环,循环两个链表

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

进阶解法

利用题目中的信息,已经是链表的尾部才有可能相交,可以先让一条链表前进 两条链表长度差值 的位置,最后再两条链表一起前进

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    ListNode cur1 = headA;
    ListNode cur2 = headB;
    int sizeA = 0;
    int sizeB = 0;
    // 统计链表A的长度
    while (cur1 != null) {
        sizeA += 1;
        cur1 = cur1.next;
    }
    // 统计链表B的长度
    while (cur2 != null) {
        sizeB += 1;
        cur2 = cur2.next;
    }
    // 回到初始位置
    cur1 = headA;
    cur2 = headB;
    // 先让元素较多的链表的指针移动一段距离
    if (sizeA > sizeB) {
        // 链表A的元素较多,先走一段距离
        for (int i = 0; i < (sizeA - sizeB); i++) {
            cur1 = cur1.next;
        }
    } else if (sizeA < sizeB) {
        for (int i = 0; i < (sizeB - sizeA); i++) {
            cur2 = cur2.next;
        }
    }
    // 两个链表同时走动
    while (cur1 != null && cur2 != null) {
        // 注意,需要的是hash值
        if (cur1.hashCode() == cur2.hashCode()) {
            return cur1;
        }
        cur1 = cur1.next;
        cur2 = cur2.next;
    }
    return null;
}
  • 时间复杂度:O(n + m)
  • 空间复杂度:O(1)

*环形链表

https://leetcode.cn/problems/linked-list-cycle-ii/

public static ListNode detectCycle1(ListNode head) {
    if (head == null) {
        return null;
    }
    ListNode slow = head;
    ListNode fast = head;
    ListNode node2 = null;
    // 先找到相交点
    while (slow != null && fast.next != null) {
        // slow移动一步
        slow = slow.next;
        // fast移动两步
        fast = fast.next.next;
        if (slow.hashCode() == fast.hashCode()) {
            // slow 和 fast相交
            node2 = slow;
            // 让node1从起点出发,node2从相交点出发,当node1和node2相交的时候,相交时的节点即环的入口
            ListNode node1 = head;
            while (true) {
                if (node1.hashCode() == node2.hashCode()) {
                    return node1;
                } else {
                    node1 = node1.next;
                    node2 = node2.next;
                }
            }
        }
    }
    return null;
}
  • 时间复杂度: O(n)。因为快慢指针相遇前,指针走的次数小于链表长度;快慢指针相遇后,两个node指针走的次数也小于链表长度,因此走的总次数小于 2n
  • 空间复杂度: O(1)
目录
相关文章
|
1天前
|
存储 安全 Java
掌握8条泛型规则,打造优雅通用的Java代码
掌握8条泛型规则,打造优雅通用的Java代码
掌握8条泛型规则,打造优雅通用的Java代码
|
1天前
|
Java 程序员 图形学
程序员教你用代码制作飞翔的小鸟--Java小游戏,正好拿去和给女神一起玩
《飞扬的小鸟》Java实现摘要:使用IntelliJ IDEA和JDK 16开发,包含小鸟类`Bird`,处理小鸟的位置、速度和碰撞检测。代码示例展示小鸟图像的加载、绘制与旋转。同时有`Music`类用于循环播放背景音乐。游戏运行时检查小鸟是否撞到地面、柱子或星星,并实现翅膀煽动效果。简单易懂,可直接复制使用。
|
2天前
|
数据库连接
java+ssm+vue代码视频学习讲解
java+ssm+vue代码视频学习讲解
5 0
|
2天前
|
SQL 缓存 算法
优化你的Java代码:性能调优技巧
优化你的Java代码:性能调优技巧
8 0
|
2天前
leetcode代码记录(移除链表元素
leetcode代码记录(移除链表元素
6 0
|
3天前
|
Java 编译器 程序员
Java一分钟之第一行Java代码:输出"Hello, World!"
【5月更文挑战第7天】本文引导初学者编写运行第一个Java程序——打印&quot;Hello, World!&quot;,介绍基本代码结构及常见问题。包括语法错误(如缺少分号、缩进不规范)、编译运行问题(忘记编译、运行错误)和环境配置问题(JDK未安装、环境变量未设置)。建议检查语法、熟悉编译运行流程并正确安装配置JDK。通过实战演练,从编写到运行,迈出Java编程第一步。
13 0
|
3天前
|
Java
接口在增强Java代码的灵活性方面起着关键作用
Java接口增强代码灵活性,实现多态性、解耦、多继承和扩展性。通过接口,类可隐藏实现细节,实现抽象化,促进模块化和维护性。接口定义方法,允许不同类实现,减少依赖,便于测试和修改。同时,接口提供多继承解决方案,使代码更具扩展性,易于添加新功能。
22 4
|
4天前
|
搜索推荐 Java Shell
8大Java排序方法(由简入繁),有代码详解和原理指导
8大Java排序方法(由简入繁),有代码详解和原理指导
22 0
|
4天前
|
Java Apache
Java代码使用POI导出的单元格加上边框和背景色
【5月更文挑战第3天】Java代码使用POI导出的单元格加上边框和背景色
20 0
|
4天前
|
Java Apache
Java代码使用POI导出的单元格的字体加粗设置
【5月更文挑战第3天】Java代码使用POI导出的单元格的字体加粗设置
22 1