Leetcode链表刷题集合

news2024/12/23 19:34:53

链表

对链表类算法题做个小集合,题解基本来LeetCode题解与labuladong的算法网站,自己加以理解写注释。代码都是测试跑通的。

下面使用的链表结构:

class ListNode{
    public ListNode next;
    public int val;

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

leetcode 141. 判断链表是否有环

判断链表是否有环

使用快慢指针进行求解。

public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode fast = head,slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;

            if(slow == fast) return true;
        }
        return false;
    }
}

leetcode 142. 环形链表 II

142. 环形链表 II:判断链表是否有环的同时,返回链表中环的起始位置节点

// leetcode 142 环形链表 II
// 判断链表是否有环的同时,返回链表中环的起始位置节点
public ListNode detectCycle(ListNode head) {
    // 此处若不判断,当链表只有一个节点时(且无环时),会导致下面代码不正确。
    if(head == null || head.next == null){
        return null;
    }

    ListNode fast = head, slow = head;
    while(fast != null && fast.next != null){
        fast = fast.next.next;
        slow = slow.next;

        if(fast == slow) break;
    }
    if(fast != slow){
        return null; // 无环
    }
	
    
    slow = head;
    while(fast != slow){
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
}

通过下面两张《labuladong的算法小抄》图可以明白为什么可以找到环的起始位置。

环形链表 II

环形链表 II

寻找无环单链表的中点

/*
寻找无环单链表的中点
快慢指针,一个块一个慢,块的走到头,慢的刚好在中间
如果是奇数:slow 刚好在中间  ceil(n/2)
如果是奇数:slow 刚好在     n/2
*/
public static ListNode findLinkedListMid(ListNode head){
    ListNode fast=head,slow=head;
    while(fast != null && fast.next != null){
        fast = fast.next.next;
        slow = slow.next;
    }
    return slow;
}

寻找无环单链表的倒数第k个节点

/*
    寻找无环单链表的倒数第k个节点
思路:
    1. 先让快指针走 k 次
    2. 之后快指针和慢指针一步一步next。
    3. 当快指针到达尾部时,慢指针指向倒数第k个元素。

 */
public static ListNode findLinkedListEndk(ListNode head,int k){
    ListNode fast=head,slow=head;
    while(fast != null && k != 0){
        fast = fast.next;
        k--;
    }
    if(fast == null){
        return slow; // 说明链表的长度 刚好为 k。倒数第k个元素,也就刚好是第一个元素
    }
    while(fast != null){
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
}

leetcode 19 删除链表的倒数第 n 个结点

leetcode19题 删除链表的倒数第 n 个结点

  • 解法一
/**
* leetcode 19题: 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
* 假设链表有K个节点
* 第一个while循环 n 次,
* 第二个while循环 k-n 次
* 时间复杂度为 n+k-n = k
* @param head
* @param n
* @return
*/
public ListNode removeNthFromEnd(ListNode head, int n) {
    // 链表为空, 或者只有一个元素且删除当前节点的情况。
    if(head == null || (head.next == null && n == 1)) {
        head = null;
        return head;
    }

    ListNode fast = head, slow = head;
    while(fast != null && n != 0){
        fast = fast.next;
        n--;
    }

    // 这个条件成立说明,要删除的倒数第n个节点,就是head节点本身,这里直接返回,可以防止后面的程序空指针。
    if(fast == null){
        return head.next;
    }

    // 加上 fast.next != null 是为了将 slow 定位到倒数第 n+1 位置的节点,这样方便单链表删除倒数第n个节点。
    while(fast != null && fast.next != null){
        slow = slow.next;
        fast = fast.next;
    }
    // 删除 slow.next(倒数第n个节点)
    slow.next = slow.next.next;
    return head;
}
  • 解法二
public static ListNode findLinkedListEndk(ListNode head,int k){
    ListNode fast=head,slow=head;
    while(fast != null && k != 0){
        fast = fast.next;
        k--;
    }
    if(fast == null){
        return slow; // 说明链表的长度 刚好为 k。倒数第k个元素,也就刚好是第一个元素
    }
    while(fast != null){
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
}

/**
 * leetcode 19题 :解法二,利用上面定义的 findLinkedListEndk 函数 + 虚拟节点(防止空指针)
 */
public ListNode removeNthFromEnd(ListNode head, int n) {
    ListNode dummy = new ListNode(-1);
    dummy.next = head;
    // 获取倒数第n+1个节点
    ListNode x = findLinkedListEndk(dummy,n+1);
    x.next = x.next.next;
    return dummy.next;
}

leetcode 21 合并两个有序链表

leetcode21: 合并两个有序链表

保证合并后依然是升序的

// leetcode 21 合并两个有序链表, 保证合并后依然是升序的。
public ListNode margeTwoLists(ListNode n1 , ListNode n2){
    if(n1 == null || n2 == null){
        return n1 != null ? n1 : n2;
    }

    ListNode temp = new ListNode(-1);
    ListNode p1 = temp;
    while (n1 != null && n2 != null){
        if (n1.val < n2.val){
            p1.next = n1;
            n1 = n1.next;
        }else {
            p1.next = n2;
            n2 = n2.next;
        }
        p1 = p1.next;
    }

    p1.next = (n1 != null ? n1 : n2);
    return temp.next;
}

leetcode 23.合并 k 个有序链表

leetcode23: 合并 k 个有序链表

/**
     * leetcode 23 合并 k 个有序链表
     * @param lists
     * @return
     *
     * 输入:lists = [[1,4,5],[1,3,4],[2,6]]
     * 输出:[1,1,2,3,4,4,5,6]
     * 解释:链表数组如下:
     * [
     *   1->4->5,
     *   1->3->4,
     *   2->6
     * ]
     * 将它们合并到一个有序链表中得到。
     * 1->1->2->3->4->4->5->6
     */
public ListNode mergeKLists(ListNode[] lists) {
    ListNode ans = null;
    // 先写一个合并两个链表的方法, 将两个链表传入,返回合并后的链表 mlist
    // 利用合并后的 mlist 在和 下一个链表, 在进行两个链表的合并。
    // 将问题拆分为多个合并两个链表的问题。
    for (ListNode l:lists) {
        ans = margeTwoLists(ans,l);
    }
    return ans;
}

leetcode 23.合并 k 个有序链表(通过优先级队列)

/*
leetcode 23 合并 k 个有序链表。通过优先级队列:最小堆、小根堆实现合并。
 */
public ListNode margeKListsUsingPriorityQueue(ListNode[] lists){
    if(lists.length == 0) return null;
    // 虚拟头结点
    ListNode dummy = new ListNode(-1);
    ListNode p = dummy;

    // 优先级队列:最小堆、小根堆
    PriorityQueue<ListNode> pq = new PriorityQueue<>(lists.length,(a,b)-> a.val - b.val);
    // 将每个链表的第一个结点加入小根堆。那么堆顶的节点就是值最小的
    for (ListNode l:lists) {
        if(l != null)  pq.add(l);
    }
    while(!pq.isEmpty()){
        // 取出堆顶元素
        ListNode minNode = pq.poll();
        // 加入最终链表
        p.next = minNode;

        // 将当前 minNode 的下一个元素加入 小根堆(也就是k个链表中每次取出的最小值节点 minNode 的下一个节点)
        // 这一步为何要添加,可以通过下面的注释理解
        if(minNode.next != null){
            pq.add(minNode.next);
        }
        p = p.next;
    }
    return dummy.next;
}

/*
具体流程:
二维链表:[[1,4,5],[1,3,4],[2,6]]
第一轮循环 pq 中的元素 1 1 2,利用pq的特性取出 1,加入下一个元素 3
第二轮循环 pq 中的元素 3 1 2,利用pq的特性取出 1,加入下一个元素 4
第三轮循环 pq 中的元素 3 4 2,利用pq的特性取出 2,加入下一个元素 6
第四轮循环 pq 中的元素 3 4 6,利用pq的特性取出 3,加入下一个元素 4
第五轮循环 pq 中的元素 4 4 6,利用pq的特性取出 4,加入下一个元素 5
第六轮循环 pq 中的元素 5 4 6,利用pq的特性取出 4,加入下一个元素:null(不做添加)
第七轮循环 pq 中的元素 5 6,  利用pq的特性取出 5,加入下一个元素:null(不做添加)
第八轮循环 pq 中的元素 6,    利用pq的特性取出 6,加入下一个元素:null(不做添加)
*/

leetcode 86 题分隔链表

分隔链表

/**
 * leetcode 86题分隔链表:
 * 链表中的一个值。要求使用这个值将链表分成两个链表,
 * 所有小于 x 的节点都出现在 大于或等于 x 的节点之前。你应当 保留 两个分区中每个节点的初始相对位置。
 * @param head 单链表
 * @param x 目标值
 * @return
 *
 * 示例:
 * 输入:head = [1,4,3,2,5,2], x = 3
 * 输出:[1,2,2,4,3,5]
 *
 * 输入:head = [2,1], x = 2
 * 输出:[1,2]
 *
 * 思路:
 * 0. 创建两个链表:A与B
 * 1. A 保存值小于 x 的节点(且不改变相对顺序)
 * 2. B 保存大于等于 x 的节点(且不改变相对顺序)
 * 3. A 的尾部链接上B的头部即可
 */
public ListNode partition(ListNode head, int x) {
    // tempLeft 用于存放小于 x 的节点,tempRIght 用于存放大于 x 的节点
    ListNode tempLeft = new ListNode(-1),tempRight = new ListNode(-1);

    ListNode p1 = tempLeft, p2 = tempRight;
    ListNode p = head;
    while(p != null){
        // 注意:这里不能使用 p.val <= x。只保留小于 x 的。
        if(p.val < x){
            p1.next = p;  // 这里赋值为 p 后面需要断开 p.next.
            p1 = p1.next;
        }else {
            p2.next = p;
            p2 = p2.next;
        }
        ListNode temp = p.next;
        p.next = null;
        p = temp;
    }
    // 用 p1 的尾结点,链接tempRight的首节点
    p1.next = tempRight.next;
//        返回tempLeft首节点即可
    return tempLeft.next;
}

Leetcode160 两个链表是否相交

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

题目数据 保证 整个链式结构中不存在环

在这里插入图片描述

解法一:这种解法每次都定位到倒数第k个节点,逐步向前比较,只要节点不相等就返回下一个节点(可能是相交的节点,也可能是null代表两个链表不相交)

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

public static ListNode findLinkedListEndk(ListNode head,int k){
    ListNode fast=head,slow=head;
    while(fast != null && k != 0){
        fast = fast.next;
        k--;
    }
    if(fast == null){
        return slow; // 说明链表的长度 刚好为 k。倒数第k个元素,也就刚好是第一个元素
    }
    while(fast != null){
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
}

/**
     * Leetcode160 两个链表是否相交
     * @param headA  headB
     */
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    int i = 1;
    ListNode dummy01 = new ListNode(-1);
    ListNode dummy02 = new ListNode(-1);
    dummy01.next = headA;
    dummy02.next = headB;

    ListNode temp01 , temp02;
    while(true){
        // 从倒数第一个开始,逐步向前找节点
        temp01 = findLinkedListEndk(dummy01,i);
        temp02 = findLinkedListEndk(dummy02,i);

        // 节点只要有一个为空, 就说明已经遍历完了,两个链表没有交点
        if(temp01 == null || temp02 == null){
            break;
        }

        // 如果节点不相等,那么他们的下一个就是交点。
        if(temp01 != temp02){
            // 如果没有交点,在倒数第一个节点的 next 就为 null,结果依旧是正确的。
            return temp02.next;
        }
        // 用于控制倒数第i个节点的变量
        i++;
    }
    return null;
}

解法二:

/**
思路:
比如:list1 : [1,2, 3, 8, 4, 6]  len1 = 6 
     list2 : [9,15,	  8, 4, 6]  len2 = 5
二者在8的位置相交
1. 求出len1 与 len2。
2. 	if len1 > len2
		then list1 = list1.next // 执行 len1-len2 步
	if len1 < len2
		then list2 = list2.next // 执行 len2-len1 步
	
	通过上面的两步可以将不同长度的链表指针修改为
	list1 : [1,2, 3, 8, 4, 6]  list1:从2开始
	list2 : [  9,15, 8, 4, 6]  list2:从9开始
	这样二者只需要一起next就可以同时到达 8 ,得到节点有交点,如果没有交点,那就是同时到达 null。
	
	if len1 == len2
		then 两个链表的长度一致,
	
	loop list1 != list2
	 	list1 = list1.next; 
	 	list2 = list2.next;
*/
public ListNode getIntersectionNode(ListNode headA, ListNode headB){
    int lenA = 0, lenB = 0;
    // 计算两条链表的长度
    for (ListNode p1 = headA; p1 != null; p1 = p1.next) {
        lenA++;
    }
    for (ListNode p2 = headB; p2 != null; p2 = p2.next) {
        lenB++;
    }
    // 让较长的链表向前走 |lenA - lenB| 步,与较短的链表到达尾部节点的距离一样长。
    ListNode p1 = headA, p2 = headB;
    if (lenA > lenB) {
        for (int i = 0; i < lenA - lenB; i++) {
            p1 = p1.next;
        }
    } else {
        for (int i = 0; i < lenB - lenA; i++) {
            p2 = p2.next;
        }
    }
    // 看两个指针是否会相同,p1 == p2 时有两种情况:
    // 1、要么是两条链表不相交,他俩同时走到尾部空指针
    // 2、要么是两条链表相交,他俩走到两条链表的相交点
    while (p1 != p2) {
        p1 = p1.next;
        p2 = p2.next;
    }
    return p1;
}

参考

https://labuladong.gitee.io/algo/di-ling-zh-bfe1b/shuang-zhi-0f7cc/
https://leetcode.cn/problemset/all/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/706576.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

threejs光源

个人博客地址: https://cxx001.gitee.io 前言 没有光源&#xff0c;渲染场景将不可见。threejs中已经帮我们实现了大量的光源&#xff0c;我们可以直接使用&#xff0c;主要分为两类&#xff1a;基础光源和特殊光源&#xff0c;下面将依次详细介绍。 基础光源 1. THREE.Ambi…

单向/双向V2G环境下分布式电源与电动汽车充电站联合配置方法(matlab代码)

目录 1 主要内容 目标函数 电动汽车负荷建模 算例系统图 程序亮点 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序复现博士文章《互动环境下分布式电源与电动汽车充电站的优化配置方法研究》第五章《单向/双向V2G环境下分布式电源与电动汽车充电站联合配置方法》…

13 | 代码模型(上):如何使用DDD设计微服务代码模型?

目录 DDD 分层架构与微服务代码模型 微服务代码模型 微服务一级目录结构 各层目录结构 1. 用户接口层 2. 应用层 3. 领域层 4. 基础层 代码模型总目录结构 总结 上一篇文章中完成了领域模型的设计&#xff0c;接下来我们就要开始微服务的设计和落地了。那微服务落地时…

第十七章、Spring的事务处理

1.什么是事务&#xff1f; 保证业务操作完整性的一种数据库机制事务的特点&#xff1a;ACID 原子性 一致性 隔离性 持久性2.如何控制事务 JDBC:Connection.setAutoCommit(false);事务开启Connection.commit();Connection.rollback(); Mybatis:Mybatis自动开启事务sqlSession(…

Revit中桩的绘制及CAD生成桩

一、Revit如何用体量来绘制一个桩基础 如何用体量来绘制一个桩基础呢?这里采用BIM等级考试一级第十期的第三题来教大家 新建体量样板&#xff0c;选择公制体量来绘制 按题目要求先复制4个参照标高平面&#xff0c;同时按住ctrlshift拖动标高再修改高度就可以 开始绘制基础的最…

Axure教程——模糊搜索(中继器 )

本文介绍的是用Axure中的中继器制作模糊搜索 效果 预览地址&#xff1a;https://f16g7e.axshare.com 功能 输入关键字&#xff0c;可查询出相应的结果 制作 一、需要元件 矩形、中继器 二、制作过程 1、搜索框 拖入一个矩形元件&#xff0c;设置大小为21530,在矩形中加入一个…

多元分类预测 | Matlab麻雀算法(SSA)优化混合核极限学习机(HKELM)分类预测,多特征输入模型,SSA-HKELM分类预测

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元分类预测 | Matlab麻雀算法(SSA)优化混合核极限学习机(HKELM)分类预测,多特征输入模型,SSA-HKELM分类预测 多特征输入单输出的二分类及多分类模型。程序内注释详细,直接替换数据就可以用。程序语言为matlab…

内网IP怎么用域名让外网访问,域名动态解析和静态区别?

域名解析是将域名与公网IP进行对应关系&#xff0c;实现访问域名即访问到对应IP应用的方式。域名解析分静态域名解析和动态域名解析的区别&#xff0c;它们的区别在哪&#xff1f;内网IP服务器怎么用域名让外网连接访问&#xff1f;这些都是需要我们有所了解掌握的。 这里不但…

ShardingSphere 5.3 整合 Seata 分布式事务 | Spring Cloud 61

一、前言 通过以下系列章节&#xff1a; docker-compose 实现Seata Server高可用部署 | Spring Cloud 51 Seata AT 模式理论学习、事务隔离及部分源码解析 | Spring Cloud 52 Spring Boot集成Seata利用AT模式分布式事务示例 | Spring Cloud 53 Seata XA 模式理论学习、使用…

word去掉页眉横线方法

最简单的方法&#xff1a;双击页眉全选文字&#xff0c;然后点清除样式即可。 清除样式的图标&#xff1a; 参考的是下面这篇文章&#xff0c;介绍得很详细&#xff0c;讲了三种方法&#xff0c;如果上面的方法行不通可以试试其他的方法&#xff1a; Word页眉横线怎么去掉

2023年第二届能源与环境工程国际会议(CFEEE 2023)

会议简介 Brief Introduction 2023年第二届能源与环境工程国际会议(CFEEE 2023) 会议时间&#xff1a;2023年9月1日-3日 召开地点&#xff1a;中国三亚 大会官网&#xff1a;CFEEE 2023-2023 International Conference on Frontiers of Energy and Environment Engineering 由I…

编译linux内核(一)

关于linux启动流程 1. 第一阶段&#xff1a;BIOS1.1 硬件自检1.2 启动顺序 2. 第二阶段&#xff1a;主引导记录2.1 主引导记录的结构2.2 分区表 3. 第三阶段&#xff1a;硬盘启动3.1 情况A&#xff1a;卷引导记录3.2 情况B&#xff1a;扩展分区和逻辑分区3.3 情况C&#xff1a;…

chatgpt赋能python:免费的Python编程软件:开发者必备工具!

免费的Python编程软件&#xff1a;开发者必备工具&#xff01; Python是一门广受欢迎的编程语言&#xff0c;它已经成为了很多公司和开发者的首选语言。Python的出现改变了编程的方式&#xff0c;它具有简单、易懂、易读、易写、易拓展等特点&#xff0c;因此成为了很多新手入…

使用 Pod 网络策略保护 Kubernetes 集群流量

Kubernetes Pod 默认可以自由地相互通信。当您的集群用于多个应用程序或团队时,这会带来安全风险。一个 Pod 中的错误行为或恶意访问可能会将流量引导至集群中的其他 Pod。 本文将教您如何通过设置网络策略来避免这种情况。这些规则可让您在 IP 地址级别( OSI第 3 层或第 4 …

二进制表示整数及运算

现代计算机存储和处理信息以二值信号表示&#xff0c;二值信号能够很容易地被表示、存储和传输。例如穿孔卡片上有洞或无洞、电压的高低或顺时针、及顺时针或逆时针的磁场。 图 二进制与电压的关系 1 二进制 大多数计算机使用8位作为一个字节&#xff0c;是最小的可寻址的内存…

【vue】vue高性能虚拟滚动列表【vue2和vue3版组件封装】

项目场景&#xff1a; 当前页显示10w多条数据&#xff0c;不做分页的情况进行渲染。加载和渲染页面会非常慢&#xff0c;滚动也非常卡顿 解决方案&#xff1a; 之渲染可视窗口的列表&#xff0c;其他列表不进行渲染。通过修改偏移量高度进行滚动列表。 vue2版本 virtualLi…

k8s安装环境准备:Virtualbox安装CentOS;复制多个CentOS虚拟机

1.安装virtualbox 下载virtualbox https://www.virtualbox.org/wiki/Downloads 安装&#xff08;windows&#xff09; 双击VirtualBox-7.0.8-156879-Win.exe 选择安装目录 安装完成后&#xff0c;打开virtualbox 2.下载CentOS 下载CentOS-7-x86_64-DVD-2009.iso http://isoredi…

做项目去实习到底做的什么?

300万字&#xff01;全网最全大数据学习面试社区等你来&#xff01; 今天是手机编辑的文章&#xff0c;说说做项目/实习这回事。 我之前发过一些视频&#xff0c;讲校招四要素的&#xff0c;其中一个很重要的部分就是实习。 对社招同学来说&#xff0c;就简单了&#xff0c;面试…

解决磁盘占用率过高100%问题

方法一&#xff1a;关闭程序 首先打开任务管理器&#xff0c;单击磁盘占用率一栏进行排序&#xff0c;查看占用磁盘最高的应用。若占用率最高的始终是同一个三方程序&#xff0c;可尝试卸载。 注&#xff1a;开机时由于频繁读写磁盘&#xff0c;磁盘占用率会很高&#xff0c;等…

vue内嵌原生前端三件套(html+CSS+JavaScript)

问题 vue内嵌原生前端三件套&#xff08;htmlCSSJavaScript&#xff09;&#xff0c;运行后前端页面无响应 详细问题 笔者使用vue框架进行开发&#xff0c; 对于可视化大屏采用echarts实现&#xff0c;但是网上所提供的echarts可视化大屏模板多采用原生前端三件套&#xff0…