链表高频面试题

1. 两个链表第一个公共子节点

LeetCode160

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

图示两个链表在节点 c1 开始相交

listA = [4,1,8,4,5], listB = [5,6,1,8,4,5]

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

注意,函数返回结果后,链表必须 保持其原始结构

1.1. 集合

    /*** 方法1:通过集合来辅助查找** @param headA* @param headB* @return*/public static ListNode findFirstCommonNodeBySet(ListNode headA, ListNode headB) {HashSet<ListNode> set = new HashSet<>();// 将第一个链表的每个节点放入set中while (headA != null) {set.add(headA);headA = headA.next;}// 遍历第二个链表中的每个节点,判断是否存在于set中while (headB != null) {if (set.contains(headB))return headB;headB = headB.next;}return null;}

1.2. 哈希

    /*** 方法2:通过Hash辅助查找** @param pHead1* @param pHead2* @return*/public static ListNode findFirstCommonNodeByMap(ListNode pHead1, ListNode pHead2) {// 两个链表的头结点不能为空if (pHead1 == null || pHead2 == null) {return null;}ListNode current1 = pHead1;ListNode current2 = pHead2;HashMap<ListNode, Integer> hashMap = new HashMap<>();// 将第一个链表中的每个节点存入 hashmap,没有用到hashmap的value,只用到了key,所以本题使用hashset结构更合适while (current1 != null) {hashMap.put(current1,null);current1 = current1.next;}// 遍历第二个链表,判断key是否存在hashmap中while (current2 != null) {if (hashMap.containsKey(current2)) {return current2;}current2 = current2.next;}return null;}

1.3. 栈

    /*** 方法3:通过栈*/public static ListNode findFirstCommonNodeByStack(ListNode headA, ListNode headB) {// 定义两个栈 将两个链表的节点存放到两个栈中Stack<ListNode> stackA = new Stack<>();Stack<ListNode> stackB = new Stack<>();// 将两个链表的节点存放到两个栈中while (headA != null) {stackA.push(headA);headA = headA.next;}while (headB != null) {stackB.push(headB);headB = headB.next;}// 比较两个栈 栈顶的值 若相等则出栈,直至找到最后一组相等的结点ListNode pNode = null;while (stackA.size() > 0 && stackB.size() > 0) {if (stackA.peek() == stackB.peek()) {pNode = stackA.pop();stackB.pop();}else {break;}}return pNode;}

2. 回文串

LeetCode234

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

示例 1:

输入:head = [1,2,2,1] 输出:true

示例 2:

输入:head = [1,2] 输出:false

2.1. 全部压栈

    /*** 方法2:全部压栈** @param head* @return*/public static boolean isPalindromeByAllStack(ListNode head) {// 定义栈 将链表中结点的值依次压入栈Stack<Integer> stack = new Stack<>();ListNode temp = head;while (temp != null) {stack.push(temp.val);temp = temp.next;}// 栈中的值依次出栈,与链表中的值依次比较,若有一个不一致,则不是回文串// 栈中的值出栈顺序为原顺序逆序,head从头遍历,正好符合回文串定义while (head != null) {if (head.val != stack.pop()){return false;}head = head.next;}return true;}

2.2. 将所有数据压栈,仅弹出一半数据进行比较

    /*** 方法3:将所有数据压栈,仅弹出一半数据进行比较* 因为回文串,前半部分的逆序就是后半部分,所以只需比较一半数据即可* @param head* @return*/public static boolean isPalindromeByHalfStack(ListNode head) {if (head == null)return true;ListNode temp = head;Stack<Integer> stack = new Stack<>();//链表的长度int len = 0;//把链表节点的值存放到栈中while (temp != null) {stack.push(temp.val);temp = temp.next;len++;}//len长度除以2,右移1位,左边加0,相当于除以2len >>= 1;//然后再出栈while (len-- >= 0) {if (head.val != stack.pop())return false;head = head.next;}return true;}

3. 合并有序链表

3.1. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

输入:L1 = [1,2,4], L2 = [1,3,4] 输出:[1,1,2,3,4,4]

示例 2:

输入:L1 = [], L2 = [] 输出:[]

示例 3:

输入:L1 = [], L2 = [0] 输出:[0]

3.1.1. 基本思路

新建一个链表,然后分别遍历两个链表,每次都选择最小的结点连接到新链表上。

  /*** 方法1:最基础的做法* 新建一个链表,然后分别遍历两个链表,每次都选择最小的结点连接到新链表上。* @param list1* @param list2* @return*/public static ListNode mergeTwoLists(ListNode list1, ListNode list2) {// 新建一个链表,这个新链表有一个虚拟头结点,便于处理ListNode newNode = new ListNode(-1);// 因为newNode需要移动,所以先赋值给res备份一下ListNode res = newNode;while (list1 != null || list2 != null) {if (list1 != null && list2 != null) {// 如果第一个链表的值大于第二个链表的值,则将第二个链表的值加到新链表newNode上if (list1.val > list2.val) {newNode.next = list2;list2 = list2.next;// 将list2的头指针往后移动一个位置} else if (list1.val < list2.val) {newNode.next = list1;list1 = list1.next;} else {newNode.next = list2;list2 = list2.next;newNode = newNode.next;newNode.next = list1;list1 = list1.next;}newNode = newNode.next; // 将newNode的头指针往后移动一个位置}if (list1 != null && list2 == null) { // 此时list2已经为空,只需连接list1的值newNode.next = list1;list1 = list1.next;newNode = newNode.next;}if (list1 == null && list2 != null) {newNode.next = list2;list2 = list2.next;newNode = newNode.next;}}return res.next;}
    /*** 方法2:比方法1更加精简的实现方法** @param l1* @param l2* @return*/public static ListNode mergeTwoListsMoreSimple(ListNode l1, ListNode l2) {ListNode prehead = new ListNode(-1);ListNode prev = prehead;while (l1 != null && l2 != null) {if (l1.val <= l2.val) {prev.next = l1;l1 = l1.next;} else {prev.next = l2;l2 = l2.next;}prev = prev.next;}// 最多只有一个还未被合并完,直接接上去就行了,这是链表合并比数组合并方便的地方prev.next = l1 == null ? l2 : l1;return prehead.next;}

3.2. 合并K个链表

    /*** 合并K个链表** @param lists* @return*/public static ListNode mergeKLists(ListNode[] lists) {ListNode res = null;for (ListNode list : lists) {res = mergeTwoListsMoreSimple(res, list);}return res;}

3.3. 合并两个链表

LeetCode1669

给你两个链表 list1 和 list2 ,它们包含的元素分别为 n 个和 m 个。

请你将 list1 中下标从 a 到 b 的全部节点都删除,并将list2 接在被删除节点的位置。

下图中蓝色边和节点展示了操作后的结果:

请你返回结果链表的头指针。

示例 :

输入:list1 = [0,1,2,3,4,5], a = 3, b = 4, list2 = [1000000,1000001,1000002] 输出:[0,1,2,1000000,1000001,1000002,5] 解释:我们删除 list1 中下标为 3 和 4 的两个节点,并将 list2 接在该位置。上图中蓝色的边和节点为答案链表。

思路:找到链表list1保留部分的尾结点和链表list2的尾结点,将两链表连接起来就行了。

 public static ListNode mergeInBetween(ListNode list1, ListNode list2, int a, int b) {ListNode pre1 = list1,post1 = list1, post2 = list2;int i = 0, j = 0;while (pre1 != null && post1 != null && j < b + 1) {// 找到链表1,位置a的前一个位置if (i < a - 1) {pre1 = pre1.next;i++;}// 找到链表1,位置b的后一个位置if (j < b + 1) {post1 = post1.next;j++;}}// 寻找list2的尾结点while (post2.next != null){post2 = post2.next;}// 链表1尾巴连接链表2头部,链2尾部连接链1后半部分的头pre1.next = list2;post2.next = post1;return list1;}

4. 双指针

4.1. 寻找中间结点

LeetCode876

给你单链表的头结点 head ,请你找出并返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:head = [1,2,3,4,5] 输出:[3,4,5] 解释:链表只有一个中间结点,值为 3 。

示例 2:

输入:head = [1,2,3,4,5,6] 输出:[4,5,6] 解释:该链表有两个中间结点,值分别为 3 和 4 ,返回第二个结点。

思路:
慢指针一次移动一个结点,快指针一次移动两个结点

public ListNode middleNode(ListNode head) {ListNode slow = head;ListNode fast = head;if (head == null) {return head;}while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;}return slow;}

4.2. 寻找链表的倒数第K个元素

输入一个链表,输出该链表中倒数第k个节点。本题从1开始计数,即链表的尾节点是倒数第1个节点。

示例 给定一个链表: 1->2->3->4->5, 和 k = 2。

输出: 4。

    /***  给出k,找出该链表倒数第K个结点*  思路:fast指针先走到第K个结点,然后fast和slow 同时走,当fast指向null的时候,slow指向倒数第K个结点* @param head* @param k* @return*/public static ListNode getKthFromEnd(ListNode head, int k) {ListNode fast = head;ListNode slow = head;// 让fast指向第K个结点while (fast != null && k > 0){fast = fast.next;k--;}// 让fast 和 slow同步移动while (fast != null) {fast = fast.next;slow = slow.next;}return slow;}

4.3. 旋转链表

LeetCode61

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

示例 1:

输入:head = [1,2,3,4,5], k = 2 输出:[4,5,1,2,3]

思路:

先用双指针策略找到倒数K的位置,也就是{1,2,3}和{4,5}两个序列,之后再将两个链表拼接成{4,5,1,2,3}就行了。具体思路是: 因为k有可能大于链表长度,所以首先获取一下链表长度len,如果然后k=k % len,如果k == 0,则不用旋转,直接返回头结点。否则:

  1. 快指针先走k步。
  2. 慢指针和快指针一起走。
  3. 快指针走到链表尾部时,慢指针所在位置刚好是要断开的地方。把快指针指向的节点连到原链表头部,慢指针指向的节点断开和下一节点的联系。
  4. 返回结束时慢指针指向节点的下一节点。
public ListNode rotateRight(ListNode head, int k) {// 当K为0或者头结点为空的时候,直接返回头节点if (k == 0 || head == null) {return head;}ListNode temp = head;// 备份头结点 ListNode fast = head;ListNode slow = head;int len = 0;// 拿到链表长度lenwhile(head != null) {head = head.next;len++;}if (k % len == 0) {return temp;}// 用快指针找到倒数第K+1个节点while((k % len) >0) {k--;fast = fast.next;}// 快慢指针同时移动,快指针移动到最后一个元素时,慢指针指向倒数第K+1个节点while(fast.next != null) {fast = fast.next;slow = slow.next;}ListNode res = slow.next;slow.next = null;fast.next = temp;return res;}

5. 删除结点

5.1. 删除特定结点

LeetCode203

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

示例 1:

输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]

思路:

  1. 我们创建一个虚拟链表头dummyHead,使其next指向head。
  2. 开始循环链表寻找目标元素,注意这里是通过cur.next.val来判断的。
  3. 如果找到目标元素,就使用cur.next = cur.next.next;来删除。
  4. 注意最后返回的时候要用dummyHead.next,而不是dummyHead。
    public ListNode removeElements(ListNode head, int val) {ListNode dummyHead = new ListNode(0);dummyHead.next = head;ListNode cur = dummyHead;while (cur.next != null) {if (cur.next.val == val) {cur.next = cur.next.next;} else {cur = cur.next;}}return dummyHead.next;}

5.2. 删除倒数第n个结点

LeetCode19

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]

    public static ListNode removeNthFromEndByTwoPoints(ListNode head, int n) {ListNode dummyHead = new ListNode(0);dummyHead.next = head;ListNode fast = head;ListNode slow = dummyHead;for (int i = 0; i < n; ++i) {fast = fast.next;}while (fast != null) {fast = fast.next;slow = slow.next;}assert slow.next != null;slow.next = slow.next.next;return dummyHead.next;}

5.3. 删除重复元素

5.3.1. 保留一个重复元素

LeetCode83

给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

示例 1:

输入:head = [1,1,2] 输出:[1,2]

思路:

由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。具体地,我们从指针 cur 指向链表的头节点,随后开始对链表进行遍历。如果当前 cur 与cur.next 对应的元素相同,那么我们就将cur.next 从链表中移除;否则说明链表中已经不存在其它与cur 对应的元素相同的节点,因此可以将 cur 指向 cur.next。当遍历完整个链表之后,我们返回链表的头节点即可。 另外要注意的是 当我们遍历到链表的最后一个节点时,cur.next 为空节点,此时要加以判断。

    public ListNode deleteDuplicates(ListNode head) {if (head == null) {return head;}ListNode cur = head;while (cur.next != null) {if (cur.val == cur.next.val) {cur.next = cur.next.next;} else {cur = cur.next;}}return head;}

5.3.2. 删除所有重复元素

LeetCode82

给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

示例 1:

输入:head = [1,2,3,3,4,4,5] 输出:[1,2,5]

为了防止开头就有重复的元素,这个题搞一个虚拟头结点

 public ListNode deleteDuplicates(ListNode head) {if (head == null) {return head;}// 使用第三个构造函数,直接将虚拟节点连接到头结点headListNode dummyHead = new ListNode(0, head);ListNode cur = dummyHead;while (cur.next != null && cur.next.next != null) {if (cur.next.val == cur.next.next.val) {int x = cur.next.val;// 实在是巧妙while (cur.next != null && cur.next.val == x) {cur.next = cur.next.next;}} else {cur = cur.next;}}return dummyHead.next;}

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

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

相关文章

CompletableFuture使用

一、核心API public static CompletableFuture<Void> runAsync(Runnable runnable)public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)public static <U> CompletableFuture<U> supplyAsync(Supplier<U> su…

【智能家居】二、添加火灾检测模块(烟雾报警功能点)

可燃气体传感器 MQ-2 和 蜂鸣器 代码段 controlDevice.h&#xff08;设备控制&#xff09;smokeAlarm.c&#xff08;烟雾报警器&#xff09;buzzer.c&#xff08;蜂鸣器&#xff09;mainPro.c&#xff08;主函数&#xff09;运行结果 可燃气体传感器 MQ-2 和 蜂鸣器 代码段 …

Pycharm配置jupyter使用notebook详细指南(可换行conda环节)

本教程为事后记录&#xff0c;部分图片非实操图片。 详细记录了pycharm配置jupyter的方法&#xff0c;jupyter添加其他conda环境的方法&#xff0c;远程密码调用jupyter的方法&#xff0c;修改jupyter工作目录的方法。 文章目录 一、入门级配置1. Pycharm配置Conda自带的jupyt…

华为云cce负载配置时间同步

华为云cce将负载配置好之后&#xff0c;发现里面的时间与真实时间不同步&#xff0c;差了12小时&#xff0c;怎么办&#xff1f; 这时候就需要配置时间同步了。 华为云cce里面通过配置数据存储的路径来解决这个问题的&#xff0c;配置后&#xff0c;需要重启负载。 新建负载…

三、shell - 变量

目录 1、简介 1.1 变量的定义语法: 1.2 变量的定义需遵循的规则 1.3 变量的作用域 2、用户变量 2.1 定义变量 2.2 访问变量 2.3 变量的其他赋值方式 2.4 只读变量 2.5 删除变量 ​​​​​​​3、环境变量 ​​​​​​​3.1 常见的环境变量 ​​​​​​​3.2 自…

030 - STM32学习笔记 - ADC(四) 独立模式多通道DMA采集

030 - STM32学习笔记 - ADC&#xff08;四&#xff09; 独立模式多通道DMA采集 中断模式和DMA模式进行单通道模拟量采集&#xff0c;这节继续学习独立模式多通道DMA采集&#xff0c;使用到的引脚有之前使用的PC3&#xff08;电位器&#xff09;&#xff0c;PA4&#xff08;光敏…

【刷题笔记】串联所有单词的子串||暴力通过||滑动窗口

串联所有单词的子串 1 题目描述 https://leetcode.cn/problems/substring-with-concatenation-of-all-words/ 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 …

基于STC12C5A60S2系列1T 8051单片机的液晶显示器LCD1602显示整数、小数应用

基于STC12C5A60S2系列1T 8051单片机的液晶显示器LCD1602显示整数、小数应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍液晶显示器LCD1602简单介绍IIC通信简单介绍…

【每日一题】1657. 确定两个字符串是否接近-2023.11.30

题目&#xff1a; 1657. 确定两个字符串是否接近 如果可以使用以下操作从一个字符串得到另一个字符串&#xff0c;则认为两个字符串 接近 &#xff1a; 操作 1&#xff1a;交换任意两个 现有 字符。 例如&#xff0c;abcde -> aecdb操作 2&#xff1a;将一个 现有 字符的…

解决plot画图中文乱码问题(macbook上 family ‘sans-serif‘ not found)

一、matplotlib画图中文乱码问题 使用matplotlib.pyplot画图&#xff0c;有中文字体会显示乱码问题&#xff0c;这时需要添加如下代码&#xff1a; import matplotlib.pyplot as pltplt.rcParams["font.sans-serif"] ["SimHei"]二、macbook没有SimHei的…

分布式仿真SNN的思考

我之前实现的仿真完全基于如下图设计的 将整体的网络构成见一个邻接表&#xff0c;突触和神经元作为类分别存储&#xff0c;所以当一个神经元发射脉冲时&#xff0c;很容易的将脉冲传输到突触指向的后神经元。但是在分布式方丈中&#xff0c;由多个进程仿真整体的网络&#xff…

WPS导出的PDF比较糊,和原始的不太一样,将带有SVG的文档输出为PDF

一、在WPS的PPT中 你直接输出PDF可能会导致一些问题&#xff08;比如照片比原来糊&#xff09;/ 或者你复制PPT中的图片到AI中类似的操作&#xff0c;得到的照片比原来糊&#xff0c;所以应该选择打印-->高级打印 然后再另存为PDF 最后再使用AI打开PDF文件再复制到你想用…

基于单片机的排队叫号系统设计

1&#xff0e;设计任务 利用AT89C51单片机为核心控制元件,设计一个节日彩灯门&#xff0c;设计的系统实用性强、操作简单&#xff0c;实现了智能化、数字化。 基本要求&#xff1a;利用单片机AT89C51设计排队叫号机&#xff0c;能实现叫号功能。 创新&#xff1a;能显示叫号…

猫头虎分享ubuntu20.04下VSCode无法输入中文解决方法

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

【EasyExcel】导出excel并支持自定义设置数据行背景颜色等

需求背景&#xff1a; 根据查询条件将列表数据导出&#xff0c;并筛选出满足某个条件的数据&#xff0c;将满足条件的数据的背景颜色设置成黄色。 &#xff08;本文例子如&#xff1a;name出现的次数大于等于2&#xff0c;将相关数据背景颜色都设置为黄色&#xff09; …

算法通关村第一关—链表高频面试题(白银)

链表高频面试题 一、五种方法解决两个链表的第一个公共子节点的问题 面试 02.07.链表相交1.首先想到的是暴力解&#xff0c;将第一个链表中的每一个结点依次与第二个链表的进行比较&#xff0c;当出现相等的结点指针时&#xff0c;即为相交结点。虽然简单&#xff0c;但是时间…

常见智力题汇总

常见智力题汇总 扔瓶子问题扑克牌问题出队问题烧绳子问题赛马问题求出前三名求出前五名 接水问题种树问题硬币问题宝石问题核酸检测问题 笔者最近面试遇到了好几道智力题&#xff0c;这些题目特点就是如果没有见过&#xff0c;很难第一时间思考得到答案&#xff0c;因此笔者面试…

VUE2+THREE.JS项目搭建

THREE项目搭建 简介学习文档推荐搭建1.下载three.js2.新建3DWorkShop.vue文件3.创建utils/three/tool.js4.创建components/three/draw.vue[重点]4.1 引入文件4.2 初始化场景4.3 初始化渲染器4.4 初始化光源4.5 初始化相机(人眼模式)4.6 初始化控制器4.7 初始化动画4.8 添加全局…

功率信号源简介及其应用有哪些内容

功率信号源是一种能够提供稳定输出功率信号的设备或电路。它在许多领域中都有广泛的应用。以下是一些关于功率信号源的内容&#xff1a; 功率信号源简介&#xff1a;功率信号源是一种电子设备或电路&#xff0c;它能够提供稳定的输出功率信号。功率信号源通常由放大器、稳压器、…

rest_framework_django学习笔记一(序列化器)

rest_framework_django学习笔记一(序列化器) 一、引入Django Rest Framework 1、安装 pip install djangorestframework2、引入 INSTALLED_APPS [...rest_framework, ]3、原始RESTful接口写法 models.py from django.db import models 测试数据 仅供参考 INSERT INTO de…