【LeetCode】链表精选11题

目录

快慢指针:

1. 相交链表(简单)

2. 环形链表(简单)

3. 快乐数(简单)

4. 环形链表 II(中等)

5. 删除链表的倒数第 N 个节点(中等)

递归迭代双解法:

1. 合并两个有序链表(简单)

1.1 递归求解

1.2 迭代求解

2. 反转链表(简单)

2.1 递归求解

2.2 迭代求解

3. 两两交换链表中的节点(中等)

3.1 递归求解

3.2 迭代求解

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

4.1 递归解法

4.2 迭代解法

综合题:

1. 重排链表(中等)

2. K个一组翻转链表(困难)


快慢指针:

1. 相交链表(简单)

找两个链表的尾结点,尾结点不相同则不相交。假设相交,长短链表之间差距gap步。假设i指向长链表的头节点,j指向短链表的头节点,i先走gap步,然后ij同时走,每次走1步。当ij相遇时,相遇点就是相交点。

class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {// 找两个链表的尾结点,尾结点不相同则不相交ListNode* tailA = headA;ListNode* tailB = headB;int lenA = 0;int lenB = 0;while (tailA->next){++lenA;tailA = tailA->next;}while (tailB->next){++lenB;tailB = tailB->next;}if (tailA != tailB)return nullptr;// 判断长短链表ListNode* longList = headA;ListNode* shortList = headB;if (lenB > lenA){longList = headB;shortList = headA;}// 长链表先走gap步int gap = abs(lenA - lenB);while (gap--){longList = longList->next;}// 同时走,找交点while (longList != shortList){longList = longList->next;shortList = shortList->next;}return longList;}
};

2. 环形链表(简单)

慢指针每次走1步,快指针每次走2步,慢指针进环后,快指针一定能追上慢指针,它们会在环中某点相遇。

为什么慢指针每次走1步,快指针要每次走2步,它们才能相遇?

假设慢指针进环时,快慢指针之间差距gap步。

如果快指针每次走2步,每走一次,它们之间的差距减1,gap一定会减到0。

如果快指针每次走3步,每走一次,它们之间的差距减2。如果gap为偶数,gap一定会减到0。如果gap为奇数,gap会减到-1,表示它们之间的距离变成C - 1(C是环的周长),如果C - 1是偶数,它们会相遇,如果C - 1是奇数,它们永远不会相遇。

class Solution {
public:bool hasCycle(ListNode *head) {ListNode* slow = head;ListNode* fast = head;while (fast && fast->next){slow = slow->next;fast = fast->next->next;if (slow == fast)return true;}return false;}
};

3. 快乐数(简单)

不是链表题,但是和上一题“环形链表”类似,慢指针每次走1步,快指针每次走2步,慢指针进环后,快指针一定能追上慢指针,它们会在环中某点相遇。如果相遇点为1,则为快乐数,否则不是快乐数。这里的指针表示的是值本身。

class Solution {
public:bool isHappy(int n) {int slow = n;int fast = bitSquareSum(n);while (slow != fast){slow = bitSquareSum(slow);fast = bitSquareSum(bitSquareSum(fast));}return slow == 1;}private:// 计算n的每一位的平方和int bitSquareSum(int n){int sum = 0;while (n){int tmp = n % 10;sum += tmp * tmp;n /= 10;}return sum;}
};

4. 环形链表 II(中等)

慢指针每次走1步,快指针每次走2步,慢指针进环后,快指针一定能追上慢指针,它们会在环中某点相遇。

假设在相遇点,慢指针一共走了k步,那么快指针一定一共走了2k步,所以快指针比慢指针多走了k步。另外,在相遇点,快指针一定比慢指针在环中多走了若干圈。所以,k一定是环的周长(环中节点个数)的整数倍。

此时,让i指向相遇点,j指向链表头节点,它们之间差距k步(慢指针走过的步数),如果i到达了环的入口,j也一定到达了环的入口,因为它们之间差距k步,k一定是环的周长的整数倍。

class Solution {
public:ListNode *detectCycle(ListNode *head) {ListNode* fast = head;ListNode* slow = head;while (fast && fast->next){fast = fast->next->next;slow = slow->next;if (fast == slow) // 相遇{ListNode* i = slow; // 相遇点ListNode* j = head;while (i != j){i = i->next;j = j->next;}return i;}}return nullptr;}
};

5. 删除链表的倒数第 N 个节点(中等)

快指针先走n步,然后快慢指针同时走,每次走1步。当快指针指向最后一个节点时,慢指针指向倒数第n + 1个节点。

例如,删除链表的倒数第2个节点:

class Solution {
public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* preHead = new ListNode(0, head); // 哨兵节点ListNode* fast = preHead; // 快指针ListNode* slow = preHead; // 慢指针// 快指针先走n步while (n--){fast = fast->next;}// 快慢指针同时走,每次走1步,直到快指针走到最后一个节点停止while (fast->next){fast = fast->next;slow = slow->next;}// 此时慢指针指向倒数第n+1个节点// 让倒数第n+1个节点的next域直接指向倒数第n-1个节点slow->next = slow->next->next;return preHead->next;}
};

递归迭代双解法:

1. 合并两个有序链表(简单)

1.1 递归求解

重复的子问题——函数头设计

ListNode* mergeTwoLists(ListNode* list1, ListNode* list2)

子问题在做什么——函数体设计

选择两个链表的头节点中值较小的那一个作为最终合并的新链表的头节点,然后将剩下的链表交给递归函数去处理。

  1. 比较list1->val和list2->val的大小(假设list1->val较小)
  2. list1->next = mergeTwoLists(list1->next, list2);
  3. return list1;

递归出口

当某一个链表为空的时候,返回另外一个链表。

class Solution {
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {if (list1 == nullptr)return list2;if (list2 == nullptr)return list1;if (list1->val < list2->val){list1->next = mergeTwoLists(list1->next, list2);return list1;}else{list2->next = mergeTwoLists(list1, list2->next);return list2;}}
};

1.2 迭代求解

class Solution {
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {ListNode* preHead = new ListNode; // 哨兵节点ListNode* tail = preHead;// 取小的尾插while (list1 && list2){if (list1->val < list2->val){tail->next = list1;tail = tail->next;list1 = list1->next;}else{tail->next = list2;tail = tail->next;list2 = list2->next;}}if (list1){tail->next = list1;}if (list2){tail->next = list2;}return preHead->next;}
};

2. 反转链表(简单)

2.1 递归求解

重复的子问题——函数头设计

ListNode* reverseList(ListNode* head)

子问题在做什么——函数体设计

将当前结点之后的链表反转,然后把当前结点添加到反转后的链表后面即可,返回反转后的头节点。

  1. ListNode* newHead = reverseList(head->next);
  2. head->next->next = head;    head->next = nullptr;
  3. return newHead;

递归出口

当前结点为空或者当前只有一个结点的时候,不用反转,直接返回。

class Solution {
public:ListNode* reverseList(ListNode* head) {if (head == nullptr || head->next == nullptr)return head;ListNode* newHead = reverseList(head->next);head->next->next = head;head->next = nullptr;return newHead;}
};

2.2 迭代求解

class Solution {
public:ListNode* reverseList(ListNode* head) {ListNode* pre = nullptr;ListNode* cur = head;while (cur){ListNode* next = cur->next;cur->next = pre;pre = cur;cur = next;}return pre;}
};

3. 两两交换链表中的节点(中等)

3.1 递归求解

重复的子问题——函数头设计

ListNode* swapPairs(ListNode* head)

子问题在做什么——函数体设计

将从第三个节点开始的链表两两交换节点,然后再把前两个节点交换一下,链接上刚才处理过的链表,并返回。

  1. ListNode* tmp = swapPairs(head->next->next);
  2. ListNode* newHead = head->next;    newHead->next = head;
  3. head->next = tmp;
  4. return newHead;

递归出口

当前结点为空或者当前只有一个结点的时候,不用两两交换,直接返回。

class Solution {
public:ListNode* swapPairs(ListNode* head) {if (head == nullptr || head->next == nullptr)return head;ListNode* tmp = swapPairs(head->next->next);ListNode* newHead = head->next;newHead->next = head;head->next = tmp;return newHead;}
};

3.2 迭代求解

class Solution {
public:ListNode* swapPairs(ListNode* head) {ListNode* preHead = new ListNode(0, head); // 哨兵节点ListNode* cur = preHead;// cur后面的两个节点交换while (cur->next && cur->next->next){ListNode* node1 = cur->next;ListNode* node2 = cur->next->next;cur->next = node2;node1->next = node2->next;node2->next = node1;cur = node1;}return preHead->next;}
};

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

4.1 递归解法

分治的思想,类似归并排序:

  1. 划分两个子区间

  2. 分别对两个子区间的链表进行合并,形成两个有序链表

  3. 合并两个有序链表

重复的子问题——函数头设计

ListNode* merge(vector<ListNode*>& lists, int begin, int end)

子问题在做什么——函数体设计

  1. 划分两个子区间:int mid = (begin + end) / 2;
  2. 递归合并两个子区间:
    ListNode* l1 = merge(lists, begin, mid);
    ListNode* l2 = merge(lists, mid + 1, end);
  3. 合并两个有序链表:return mergeTowList(l1, l2);

递归出口

当区间只有一个链表时,不合并。另外,当题目给出空链表时,不合并。

class Solution {
public:ListNode* mergeKLists(vector<ListNode*>& lists) {return merge(lists, 0, lists.size() - 1);}private:ListNode* merge(vector<ListNode*>& lists, int begin, int end){if (begin > end)return nullptr;if (begin == end)return lists[begin];int mid = (begin + end) / 2;ListNode* l1 = merge(lists, begin, mid);ListNode* l2 = merge(lists, mid + 1, end);return mergeTwoLists(l1, l2);}ListNode* mergeTwoLists(ListNode* list1, ListNode* list2){ListNode* preHead = new ListNode; // 哨兵节点ListNode* tail = preHead;// 取小的尾插while (list1 && list2){if (list1->val < list2->val){tail->next = list1;tail = tail->next;list1 = list1->next;}else{tail->next = list2;tail = tail->next;list2 = list2->next;}}if (list1){tail->next = list1;}if (list2){tail->next = list2;}return preHead->next;}
};

4.2 迭代解法

和“合并两个有序链表”类似,就是取小的尾插。怎么判断K个链表未合并的头节点中最小的那个?利用堆这个数据结构即可。把K个链表未合并的头节点放进一个小根堆,堆顶就是最小的那个。

class Solution {
public:ListNode* mergeKLists(vector<ListNode*>& lists) {// 创建小根堆priority_queue<ListNode*, vector<ListNode*>, cmp> heap;// 将所有头节点放进小根堆for (auto& l : lists){if (l){heap.push(l);}}// 合并链表ListNode* preHead = new ListNode; // 哨兵节点ListNode* tail = preHead;while (!heap.empty()){// 取堆顶节点尾插tail->next = heap.top();heap.pop();tail = tail->next;// 将刚才合并的节点的下一个节点补充进堆if (tail->next){heap.push(tail->next);}}return preHead->next;}private:struct cmp{bool operator()(ListNode* n1, ListNode* n2){return n1->val > n2->val;}};
};

综合题:

1. 重排链表(中等)

把链表后半段反转,再合并起来:

链表长度是偶数:1 2 3 4    (2是中间节点)

1 2

4 3

合并起来:1 4 2 3

链表长度是奇数:1 2 3 4 5    (3是中间节点)

1 2 3

5 4(4 5反转)

合并起来:1 5 2 4 3

class Solution {
public:void reorderList(ListNode* head) {ListNode* mid = midNode(head);ListNode* l2 = reverseList(mid->next);mid->next = nullptr;ListNode* l1 = head;mergeLists(l1, l2);}private:// 快慢指针找链表的中间节点,如果节点个数为偶数,取靠左的ListNode* midNode(ListNode* head){ListNode* fast = head;ListNode* slow = head;// 慢指针每次走1步,快指针每次走2步// 如果节点个数为奇数,当快指针指向最后一个节点时,慢指针指向中间节点// 如果节点个数为奇数,当快指针指向倒数第二个节点时,慢指针指向靠左的中间节点while (fast->next && fast->next->next){fast = fast->next->next;slow = slow->next;}return slow;}// 反转链表ListNode* reverseList(ListNode* head) {ListNode* pre = nullptr;ListNode* cur = head;while (cur){ListNode* next = cur->next;cur->next = pre;pre = cur;cur = next;}return pre;}// 合并链表void mergeLists(ListNode* l1, ListNode* l2){ListNode* cur1 = l1;ListNode* cur2 = l2;while (cur1 && cur2){ListNode* next1 = cur1->next;ListNode* next2 = cur2->next;cur1->next = cur2;cur2->next = next1;cur1 = next1;cur2 = next2;}}
};

2. K个一组翻转链表(困难)

头插法。

class Solution {
public:ListNode* reverseKGroup(ListNode* head, int k) {// 求出需要翻转多少组int n = 0;ListNode* cur = head;while (cur){cur = cur->next;n++;}n /= k;// 重复n次:长度为k的链表翻转ListNode* preHead = new ListNode; // 哨兵节点ListNode* pre = preHead;cur = head;for (int i = 0; i < n; i++){ListNode* tmp = cur;for (int j = 0; j < k; j++){ListNode* next = cur->next;cur->next = pre->next;pre->next = cur;cur = next;}pre = tmp;}// 把不需要翻转的部分接上pre->next = cur;return preHead->next;}
};

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

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

相关文章

20172304 2017-2018-2 《程序设计与数据结构》第六周学习总结

20172304 2017-2018-2 《程序设计与数据结构》第六周学习总结 教材学习内容总结 本周学习了数组。 首先是数组元素&#xff0c;数组具有优越性因为它可以声明一个能容纳多个可访问值的变量。数组的数据具有索引而且是从零开始的。 其次是声明和使用数组&#xff0c;可以用“…

使用 K8spacket 和 Grafana 对 K8S 的 TCP 数据包流量可视化

前言如何知道 K8S 集群内 Pod 之间建立了哪些 TCP 连接&#xff1f;集群之间存在哪些调用关系&#xff1f;使用 k8spacket 和Grafana&#xff0c;你可以可视化集群中的 TCP 流量。了解工作负载如何相互通信&#xff0c;以及建立了多少连接&#xff0c;交换了多少字节&#xff0…

粒子系统(一):从零开始画一颗树

准备 IDE&#xff1a;VisualStudio 2017 Language&#xff1a;VB.NET / TypeScript 图形API&#xff1a;Win2D Github&#xff1a;[ UWP ] [ TypeScript ] 本文将向你介绍一种粒子系统&#xff08;Particle System&#xff09;模拟植物的简单方法。 第一节 移动 粒子按照某种规…

python 获取Dmidecode 输出的系统硬件信息

目的&#xff1a;熟悉利用python 分析文本的信息。分析的文件信息是通过dmidecode 工具抓取的系统硬件信息。本文结构&#xff1a;(1) 分析dmidecode 工具的输出信息结构(2) 分别用两种方式对dmidecode 输出的信息实现抓取&#xff0c;获取Manufacturer、Product Name和 Serial…

20165313 《Java程序设计》第七周学习总结

教材学习总结 1.下载安装MySQL数据库管理系统。 2.MySQL数据库基本操作。 3.利用JAVA程序对MySQL数据库系统进行查找&#xff0c;更新&#xff0c;添加和删除操作。 学习中的问题与解决方案 1.运行书上安装MySQL命令后命令提示行显示系统错误5 解决方案 以管理员身份运行 2.运行…

五:CentOS7安装出现Warning

U盘安装CentOS 7提示 “Warning: /dev/root does not exist, could not boot” 解决办法 想将旧电脑安装CentOS7系统以作学习之用&#xff0c;奈何安装时出现错误&#xff0c;错误图示如下&#xff1a; 经多方查找、分析得知可能是启动引导不正确。 用usb writer重新制作了系统…

微软和Canonical宣布适用于Ubuntu 22.04 LTS的原生.NET 6

微软和 Canonical 达成新的合作伙伴关系&#xff0c;宣布了 Ubuntu 22.04 LTS 主机和容器的原生 .NET 可用性。.NET 开发人员现在可以通过一个 “apt install” 命令从 Ubuntu 22.04 LTS 安装 ASP.NET 和 .NET SDK 和运行时Canonical 为 .NET 6 LTS 和 ASP.NET 运行时发布新的、…

TCP的连接状态标识 (SYN, FIN, ACK, PSH, RST, URG)

一、TCP的状态 在TCP层&#xff0c;有个FLAGS字段&#xff0c;这个字段有以下几个标识&#xff1a;SYN, FIN, ACK, PSH, RST, URG。 其中&#xff0c;对于我们日常的分析有用的就是前面的五个字段。 它们的含义是&#xff1a; SYN 表示建立连接&#xff0c;FIN 表示关闭连接…

MySQL性能优化总结

一、MySQL的主要适用场景 1、Web网站系统 2、日志记录系统 3、数据仓库系统 4、嵌入式系统 二、MySQL架构图 三、MySQL存储引擎概述 1&#xff09;MyISAM存储引擎 MyISAM存储引擎的表在数据库中&#xff0c;每一个表都被存放为三个以表名命名的物理文件。首先肯定会有任何存储引…

Blazor University (45)依赖注入 —— 将依赖项注入 Blazor 组件

原文链接&#xff1a;https://blazor-university.com/dependency-injection/injecting-dependencies-into-blazor-components/将依赖项注入 Blazor 组件源代码[1]定义我们的依赖在注入依赖之前&#xff0c;我们需要创建一个。我们将使用古老的 ToDo 示例&#xff0c;但请放心&a…

顾小清:人工智能何以促进未来教育发展

自工业革命以来&#xff0c;人类社会的发展总是在技术与教育的角逐互动中前行。技术作为推动人类历史发展的核心推进力&#xff0c;与教育这一“人力资本发动机”竞相成为推动经济社会发展的主力。人工智能作为第四次工业革命的显著标签&#xff0c;其飞速发展正在逐步塑造社会…

server 2008R2 AD域环境中DHCP服务器的授权步骤

百度了下&#xff0c;没有详细的授权步骤&#xff0c;找了好久才找到&#xff0c;拿出来分享下环境&#xff1a;dhcp服务器在ad服务器中登陆dhcp服务器&#xff0c;管理工具-dhcp&#xff0c;打开dhcp&#xff0c;点击操作&#xff0c;管理授权&#xff08;终于找到了&#xff…

基于scikit-learn机器学习库的分类预测

一旦你在scikit-learn中选择好机器学习模型&#xff0c;就可以用它来预测新的数据实例。初学者经常会有这样的疑问&#xff1a; 如何在scikit-learn中用我自己的模型进行预测&#xff1f; 在本教程中&#xff0c;你将会发现如何在Python的机器学习库scikit-learn 中使用机器学习…

.NET性能优化-快速遍历List集合

简介System.Collections.Generic.List<T>是.NET中的泛型集合类&#xff0c;可以存储任何类型的数据&#xff0c;因为它的便利和丰富的API&#xff0c;在我们平时会广泛的使用到它&#xff0c;可以说是使用最多的集合类。在代码编写中&#xff0c;我们经常需要遍历一个Lis…

Thread、Runnable、Callable、Future ... 的关系?

Thread、Runnable、Callable、Future、FutureTask&#xff0c;你能详细讲出他们的内部关系么&#xff1f;这也是面试经常问到的问题。 1. Thread 和 Runnable 1.1 Thread 我们先看一下 Thread 最简单的使用姿势&#xff1a; public class MyThread extends Thread {public M…

EntityFramework6.X 之 Fulent

Fulent Fulent是配置领域模型类的另一个方法&#xff0c;它比DataAnnotations提供更多的配置&#xff0c;提供以下三种方法映射 Mappings To Database Model-Wide Mapping 设置默认架构&#xff0c;设置经典约束 Entity Mapping 映射单个或多个表格或架构&#xff0c;映射…

Visual Studio 2022 正式支持 .NET MAUI 开发

点击上方蓝字关注我们&#xff08;本文阅读时间&#xff1a;5分钟)我们很高兴地宣布 Visual Studio 2022 正式支持 .NET MAUI 开发。现在&#xff0c;您可以使用 .NET 更快地构建跨平台原生客户端应用程序&#xff0c;并将它们从单个代码库发布到 Android、iOS、macOS 和 Windo…

python访问数据库

1. python DB api简介 python DB api python访问数据库的统一接口规范&#xff0c;详细可参考https://www.python.org/dev/peps/pep-0249/python DB api中主要包括三个重要的对象 数据库连接对象 connection&#xff0c;数据库交互对象 cursor和数据库异常类 exceptions2. 使用…

错误:“filesystem“ 不是 “std“ 的成员

分析原因&#xff1a;应该项目是C版本问题 1、项目属性 → 配置属性 → 常规 → C语言标准 2、项目属性 → C/C → 语言 → C语言标准 3、项目属性 → C/C → 建议行 → 其它选项 → 添加&#xff1a;/Zc:__cplusplus

Blazor预研与实战

背景最近一直在搞一件事&#xff0c;就是熟悉Blazor&#xff0c;后期需要将Blazor真正运用到项目内。前期做了一些调研&#xff0c;包括但不限于Blazor知识学习组件库生态预研与现有SPA框架做比对与WebForm做比对自己动手做个演示项目最终的体验非常不错&#xff0c;功能全面。…