算法系列--链表刷题(二)

💕"轻舟已过万重山"💕
作者:Mylvzi
文章主要内容:算法系列–链表刷题(二)
在这里插入图片描述

今天为大家带来的是算法系列--链表刷题(二),带来了几道经典的有关链表的面试题(合并K个有序列表)

1.两数相加

https://leetcode.cn/problems/add-two-numbers/

模拟两数相加:

使用两个指针cur1和cur2来遍历两个链表,通过t记录每次相加的结果,最后创建出新的节点,尾插

注意:

  1. 每次相加时都需要更新t的值,但是不可以直接将t设置为0,因为存在进位的可能,比如t = 9 + 9 = 18,要插入节点的val = 8,还要记录进位1,所以:val = t % 10, t /= 10
  2. 循环的结束要同时满足三个条件,cur1 = null, cur2 = null, t = 0,其中最后t = 0这种情况最容易忘记,导致最后需要进位没进位成,结果相比于正确答案少了一位
    代码:
/**
 * 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 addTwoNumbers(ListNode l1, ListNode l2) {

        ListNode cur1 = l1;
        ListNode cur2 = l2;
        ListNode phead = new ListNode(0);
        ListNode cur = phead;// 用于尾插

        int t = 0;// 用于表示本次相加的结果  处理进位

        // 要注意t最后可能不为0 要进一位
        while(cur1 != null || cur2 != null || t != 0) {
            // 加上第一个节点
            if(cur1 != null) {
                t += cur1.val;
                cur1 = cur1.next;
            }

            // 加上第二个节点
            if(cur2 != null) {
                t += cur2.val;
                cur2 = cur2.next;
            }

            ListNode newNode = new ListNode(t % 10);
            t /= 10;

            // 尾插
            cur.next = newNode;
            cur = newNode;
        }
        return phead.next;
    }
}

2.两两交换链表中的节点

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

思路:

  1. 分别得到下标为奇数的所有节点的组合和下标为偶数的所有节点的组合(下标从1开始)
  2. 按照偶数节点在前,奇数节点在后的顺序合并两个链表
  3. 得到新的链表

代码实现:

/**
 * 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 swapPairs(ListNode head) {
        if(head == null || head.next == null) return head;
        // 定义两个虚拟节点 分别接收奇数和偶数的节点
        ListNode p1 = new ListNode(0);
        ListNode p2 = new ListNode(0);

        ListNode cur1 = p1;
        ListNode cur2 = p2;

        ListNode cur = head;
        int i = 1;

        // 分别得到奇数和偶数的链表组合
        while(cur != null) {
            ListNode newNode = new ListNode(cur.val);

            if(i % 2 != 0) {
                // 奇数
                cur1.next = newNode;
                cur1 = newNode;
            }else {
                // 偶数
                cur2.next = newNode;
                cur2 = newNode;
            }

            cur = cur.next;
            i++;
        }

        // 合并两个链表
        ListNode p3 = new ListNode(0);
        ListNode t1 = p1.next;
        ListNode t2 = p2.next;
        ListNode cur3 = p3;

        while(t1 != null || t2 != null) {
            if(t2 != null) {
                cur3.next =  t2;
                cur3 = t2;
                t2 = t2.next;
            }

            if(t1 != null) {
                cur3.next =  t1;
                cur3 = t1;
                t1 = t1.next;
            }
        }

        return p3.next;
    }
}

注:本题更加简洁的写法是通过递归实现的,感兴趣的可以去我的算法系列里查看

3.重排链表

https://leetcode.cn/problems/reorder-list/

思路:

  1. 首先获得中间节点,以中间节点为基准,分为两个不同的链表l1和l2
  2. 逆序l2
  3. 合并两个链表

在这里插入图片描述

代码:

/**
 * 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 void reorderList(ListNode head) {
        ListNode slow = head, fast = head;

        // 找到中间节点
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }

        ListNode l1 = head;
        ListNode l2 = slow.next;
        slow.next = null;

        // 逆序第二个节点
        l2 = reverseList(l2);

        ListNode phead = new ListNode(0);
        ListNode cur = phead;

        // 合并两个链表
        while(l1 != null || l2 != null) {
            if(l1 != null) {
                cur.next = l1;
                cur = l1;
                l1 = l1.next;
            }
            if(l2 != null) {
                cur.next = l2;
                cur = l2;
                l2 = l2.next;
            }
        }

        head = phead.next;

    }

    public ListNode reverseList(ListNode head) {
        if(head == null) return null;
        if(head.next == null) return head;

        ListNode pHead = new ListNode(0);

        ListNode cur = head;

        while(cur != null) {
            ListNode curNext = cur.next;
            
            cur.next = pHead.next;
            pHead.next = cur;

            cur = curNext;
        }
        return pHead.next;
    }
}

同样的本题也有更加简洁的递归写法

4.合并 K 个升序链表(hard)

链接:
https://leetcode.cn/problems/merge-k-sorted-lists/description/

分析:

思路一:利用优先级队列

其实本题和合并两个有序链表很相似,可以看做是上一题的plus版本

虽然这里是合并K个有序链表,但是我们可以借鉴合并两个有序链表的思路,`创建一个傀儡节点,找到两个链表节点的最小值,依次插入到傀儡节点之后

这里的难点在于如果只有两个节点,我们可以直接比较,但是现在有K个节点,如何快速地找到K个节点的最小值呢?使用优先级队列,利用优先级队列将K个链表的头结点存入到优先级队列之中,由于默认是小根堆,所以堆顶元素就是K个链表头结点的最大值,将其插入到傀儡节点之后,更新傀儡节点,然后插入堆顶节点的下一个节点,接着判断最小值,重复上述过程

手写:
在这里插入图片描述
代码:

/**
 * 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 mergeKLists(ListNode[] lists) {
        PriorityQueue<ListNode> heap = new PriorityQueue<>((o1,o2) ->o1.val - o2.val);
        ListNode ret = new ListNode(0);
        ListNode phead = ret;


        for(ListNode head : lists)// 将链表的头结点存入堆中
            if(head != null)
                heap.offer(head);

        while(!heap.isEmpty()) {
            ListNode tmp = heap.poll();
            phead.next = tmp;
            phead = tmp;

            if(tmp.next != null) heap.offer(tmp.next);// 注意下一个节点可能为空
        }

        return ret.next;
    }
}

思路二:利用归并的思想

其实这道题非常符合分治归并的思想,题目相当于给了我们一个比较特殊的数组(数组的元素是链表),实际上最重要完成的工作就是对这K个元素进行从小到大的排序,既然是排序我们就可以使用归并的思想,为什么想到归并呢?这是经过分析得到的,实际上我们是对一个一个的链表进行合并+排序操作的,这一步其实就是归并中分解到最小单元,开始回并的过程啊!所以可以使用归并的思想解决

代码:

/**
 * 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 mergeKLists(ListNode[] lists) {
        if(lists == null || lists.length == 0) return null;
        // 采用归并的思想解决
        return merge(lists,0,lists.length-1);
    }

    public ListNode merge(ListNode[] lists,int left,int right) {
        // 递归出口
        if(left >= right) return lists[left];

        int mid = (left + right) /2;
        ListNode l1 = merge(lists,left,mid);
        ListNode l2 = merge(lists,mid + 1,right);

        return mergeTwoLists(l1,l2);
    }

    // 合并两个有序链表
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        // 递归
        if(list1 == null) return list2;
        if(list2 == null) return list1;

        if(list1.val < list2.val) {
            list1.next = mergeTwoLists(list1.next,list2);
            return list1;
        }else {
            list2.next = mergeTwoLists(list2.next,list1);
            return list2;
        }
    }
}

5.K个⼀组翻转链表

链接:
https://leetcode.cn/problems/reverse-nodes-in-k-group/description/

分析:

本题采用模拟的思想,具体的模拟策略如下:

  1. 求出要反转多少个长度为K的链表–n
  2. 翻转n次即可

在这里插入图片描述
代码:

/**
 * 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 reverseKGroup(ListNode head, int k) {
        // 求需要翻转的组数n
        int cnt = 0;
        ListNode tmp = head;
        while(tmp != null) {
            cnt++;
            tmp = tmp.next;
        }

        int n = cnt / k;

        // 开始进行翻转
        ListNode ret = new ListNode(0);
        ListNode phead = ret;
        ListNode cur = head;
        for(int i = 0; i < n; i++) {
            ListNode prev = cur;// 保存当前翻转链表的头结点
            for(int j = 0; j < k; j++) {
                ListNode curnext = cur.next;
                cur.next = phead.next;
                phead.next = cur;
                cur = curnext;
            }
            phead = prev;// 每翻转完一组就更新phead
        }

        phead.next = cur;

        return ret.next;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

短视频素材网站去哪里找?

嘿&#xff0c;各位视频创作者们&#xff01;想知道短视频素材网站去哪里找&#xff1f;今天就来给大家介绍几个必备的视频素材网站&#xff0c;特别是对于入门新手和运营人员来说&#xff0c;这些网站可是必不可少的资源哦&#xff01; 首先&#xff0c;我们来看看那些提供可…

FreeRtos时间管理(一)

FreeRtos的时间管理包括相对延时vTaskDelay、绝对延时vTaskDelayUntil、系统时钟Systick 本篇主要分析相对延时vTaskDelay函数 调用vTaskDelay是一定会触发任务切换的&#xff0c;需要分析下PendSv中断触发的位置。 一、 函数流程 二 、prvAddCurrentTaskToDelayedList 注意&…

Redis中AOF、RDB和复制功能对过期键的处理

AOF、RDB和复制功能对过期键的处理 生成RDB文件 在执行SAVE命令或者BGSAVE命令创建一个新的RDB文件时&#xff0c;程序会对数据库中的键进行检查&#xff0c;已过期的键不会被保存到新创建的RDB文件中。 例子 举个例子&#xff0c;如果数据库中包含三个键k1、k2、k3&#x…

地宫取宝dfs

分析&#xff1a; 矩阵里的每一个位置都有标记&#xff0c;要求的问题是&#xff1a;有几种方法能完成这个规定。 那么&#xff0c;我们只需要计算从开始(1,1)到最后(n,m)的深度优先搜索中&#xff0c;有几个是满足要求的即为正确答案。 有个要求是&#xff0c;如果一个格子中…

删除单链表偶数节点

本题要求实现两个函数&#xff0c;分别将读入的数据存储为单链表、将链表中偶数值的结点删除。链表结点定义如下&#xff1a; struct ListNode {int data;struct ListNode *next; };函数接口定义&#xff1a; struct ListNode *createlist(); struct ListNode *deleteeven( s…

Linux hook系统调用使你文件无法删除

文章目录 前言一、什么是hook技术二、Linux hook种类三、系统调用表hook3.1 查看删除文件用到系统调用3.2 获取系统调用函数3.3 编写hook函数3.4 替换hook函数3.5 测试 参考资料 前言 hook技术在Linux系统安全领域有着广泛的应用&#xff0c;例如通过hook技术可以劫持删除文件…

xilinx的高速接口构成原理和连接结构

本文来源&#xff1a; V3学院 尤老师的培训班笔记【高速收发器】xilinx高速收发器学习记录Xilinx-7Series-FPGA高速收发器使用学习—概述与参考时钟GT Transceiver的总体架构梳理 文章目录 一、概述&#xff1a;二、高速收发器结构&#xff1a;2.1 QUAD2.1.1 时钟2.1.2 CHANNEL…

pytest之fixture结合conftest.py文件使用+断言实战

pytest之fixture结合conftest.py文件使用 conftest.py--存放固件固件的优先级pytest执行流程pytest之断言实战pytest结合allure-pytest插件生成美观的报告 conftest.py–存放固件 在一个项目的测试中&#xff0c;大多数情况下会有多个类、模块、或者包要使用相同的测试夹具。这…

【Node.js】全局变量和全局 API

node 环境中没有 dom 和 bom &#xff0c;此外 es 基本上都是可以正常使用的。 如果一定要使用 dom 和bom&#xff0c;可以借助第三方库 jsdom 帮助我们实现操作。npm i jsdom 实例&#xff1a; const fs require(node:fs) const {JSDOM} require(jsdom)const dom new JS…

命令执行漏洞

绕过技巧&#xff1a; cat 233.txt # 管道符号绕过 # 空格绕过 ${IFS} # %0a、%09 # 重定向绕过 < <> # 变量拼接绕过 kali:$ ac;bat;cfl;dag;$a$b $c$d # 单引号、双引号绕过 cat flag cat"" flag # 编码绕过 $(printf "\x63\x61\x74\x20\x2f\x…

前端学习之css 定位与浮动

定位 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>定位和浮动</title><style>*{/* 将模块紧紧贴着浏览器边框 */margin: 0;}.c{background-color: blueviolet;width: 100px;height: 1…

长安链团队论文入选国际顶会Usenix Security 2024

零知识证明是区块链扩容和隐私保护的关键前沿技术&#xff0c;其天然具备完备性、可靠性和零知识性的特点&#xff0c;是提升区块链交易吞吐量与可扩展性、在验证用户身份的同时保护用户数据隐私&#xff0c;实现复杂计算不可或缺的关键技术。基于零知识证明技术实现高兼容性、…

鸿蒙Harmony应用开发—ArkTS-像素单位

ArkUI为开发者提供4种像素单位&#xff0c;框架采用vp为基准数据单位。 说明&#xff1a; 本模块首批接口从API version 7开始支持&#xff0c;后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 名称描述px屏幕物理像素单位。vp屏幕密度相关像素&#xff0c;…

【机器学习】决策树学习下篇(详解)

引言 在当今数据驱动的时代&#xff0c;机器学习技术已成为解决复杂问题不可或缺的工具。其中&#xff0c;决策树学习作为一种基础且强大的算法&#xff0c;广泛应用于各种领域&#xff0c;包括但不限于金融风控、医疗诊断、客户关系管理等。决策树以其简单直观、易于理解和实…

Java八股文(秒杀)

Java八股文の秒杀 秒杀 秒杀 你对秒杀功能模块的理解是什么&#xff1f;你认为秒杀功能的关键点是什么&#xff1f; ○ 秒杀功能模块是指在一段时间内&#xff0c;将某个商品以非常优惠的价格或特殊活动进行销售&#xff0c;从而吸引大量用户抢购。其关键点是高并发的请求处理…

深层次理解拷贝构造函数

1.1 拷贝构造概念 在现实生活中&#xff0c;可能存在一个与你一样的自己&#xff0c;我们称其为双胞胎。 那在创建对象时&#xff0c;可否创建一个与已存在对象一某一样的新对象呢&#xff1f; 拷贝构造函数&#xff1a;只有单个形参&#xff0c;该形参是对本类类型对象的引…

提面 | 面试抽题

学习到更新日期面试抽题-1.2案例分析的思维本质2024-3-23 1提面抽屉论述问题的分类 1.1案例分析占总论 1.2案例分析的思维本质

权限提升-Windows权限提升篇溢出漏洞土豆家族通杀全系补丁对比EXP筛选

知识点 1、Web到Win-系统提权-土豆家族 2、Web到Win-系统提权-人工操作 章节点&#xff1a; 1、Web权限提升及转移 2、系统权限提升及转移 3、宿主权限提升及转移 4、域控权限提升及转移 基础点 0、为什么我们要学习权限提升转移技术&#xff1a; 简单来说就是达到目的过程…

IMU状态预积分的定义

IMU状态预积分的定义 IMU状态预积分的定义 在ESKF中&#xff0c;将两个GNSS观测之间的IMU数据进行积分&#xff0c;作为ESKF的预测过程。 这种做法把IMU数据看成某种一次性的使用方式&#xff1a;将它们积分到当前估计值上&#xff0c;然后用观测数据更新当时的估计值。 这种…

LeetCode每日一题——统计桌面上的不同数字

统计桌面上的不同数字OJ链接&#xff1a;2549. 统计桌面上的不同数字 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 思路&#xff1a; 这是一个很简单的数学问题&#xff1a; 当n 5时&#xff0c;因为n % 4 1&#xff0c;所以下一天4一定会被放上桌面 当n 4…
最新文章