【LeetCode】链表精选12题

目录

快慢指针:

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. 重排链表(中等)

3. 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. 随机链表的复制(中等)

class Solution {
public:Node* copyRandomList(Node* head) {if (head == nullptr)return nullptr;// A->B->C->null --> A->A'->B->B'->C->C'->nullNode* cur = head;while (cur){Node* copy = new Node(cur->val); // 拷贝结点copy->next = cur->next;cur->next = copy;cur = cur->next->next;}// 设置拷贝结点的random,假如A的random域指向C,就让A'的random域指向C'cur = head;while (cur){Node* copy = cur->next;if (cur->random == nullptr){copy->random = nullptr;}else{copy->random = cur->random->next;}cur = cur->next->next;}// 将A'、B'、C'链接在一起,并且还原原链表Node* preHead = new Node(0); // 哨兵节点Node* tail = preHead;cur = head;while (cur){tail->next = cur->next;tail = tail->next;cur->next = cur->next->next;cur = cur->next;}return preHead->next;}
};

2. 重排链表(中等)

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

链表长度是偶数: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;}}
};

3. 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/529118.shtml

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

相关文章

python类的属性和对象属性_python 类属性、对象属性-阿里云开发者社区

类的普通属性:  dir(Myclass), 返回一个key列表&#xff1b; Myclass.__dir__,返回一个字典&#xff1b; 1、类的数据属性&#xff1b; 2、类的方法&#xff1b; 类的特殊属性&#xff1a; 1、Myclass.__name__  类的名字 2、Myclass.__doc__   类的文档字符串 3、Mycla…

击鼓传花c语言编程题,c语言-第5章 循环程序设计.ppt

《c语言-第5章 循环程序设计.ppt》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《c语言-第5章 循环程序设计.ppt(83页珍藏版)》请在人人文库网上搜索。1、第5章 循环程序设计,管理学院 电子商务系,2,第5章 循环程序设计,5.1 概述 5.2 while和do while循环 5.3 for循环…

python快速检测视频跳过帧_python实现视频分帧效果

本文实例为大家分享了python实现视频分帧的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下 import cv2 vidcap cv2.VideoCapture(005.avi) success,image vidcap.read() count 0 success True while success: success,image vidcap.read() cv2.imwrite("fr…

最大素数c语言,for语句计算输出10000以内最大素数怎么搞最简单??各位大神们...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼#include #include int* pt NULL; // primes_tableint pt_size 0; // primes_table 数量大小int init_primes_table(void){FILE* pFile;pFile fopen("primes_table.bin", "rb");if (pFile NULL) {fputs(&q…

python去重保留唯一一个值_Python DataFrame使用drop_duplicates()函数去重(保留重复值,取重复值)...

摘要 在进行数据分析时&#xff0c;我们经常需要对DataFrame去重&#xff0c;但有时候也会需要只保留重复值。 这里就简单的介绍一下对于DataFrame去重和取重复值的操作。 创建DataFrame 这里首先创建一个包含一行重复值的DataFrame。2.DataFrame去重&#xff0c;可以选择是否保…

自定义日历控android,Android 一个日历控件的实现小记

先看几张动态的效果图吧&#xff01;这里主要记录一下在编写日历控件过程中一些主要的点&#xff1a;一、主要功能1、支持农历、节气、常用节假日2、日期范围设置&#xff0c;默认支持的最大日期范围[1900.1~2049.12]3、禁用日期范围设置4、初始化选中单个或多个日期5、单选、多…

python先返回再处理_python xpath解析返回对象怎么处理

3 4 5 text 6 7 ... 8 ... 9 ......10 11 12 ...13 ...14 ......15 16 17 18

android文件读取工具类,Android 下读取Assets Properties操作封装工具类

Android 下读取Assets Properties操作封装工具类发布时间&#xff1a;2018-06-03作者&#xff1a;laosun阅读(2081)为了方便使用&#xff0c;首先创建BaseApplication类&#xff0c;如下所示&#xff1a;import android.app.Application;import android.content.Context;/*** C…

python粘性拓展_如何将tkinter小部件置于粘性框架中

在google中使用“如何使tkinter网格扩展”&#xff0c;我遇到了这个问题。 引用布莱恩奥克利的话Rows and columns have "weight" which describes how they grow or shrink to fill extra space >in the master. By default a row or column has a weight of zer…

android 固件 编辑器,RK3288编译 Android 5.1 固件

1 准备工作编译 Android 对机器的配置要求较高&#xff1a;64 位 CPU16GB 物理内存交换内存30GB 空闲的磁盘空间用于构建&#xff0c;源码树另外占用大约 25GBUbuntu 14.04 操作系统八核i7&#xff0c;编译完成需要一个半小时安装 JDK 7:sudo apt-get install openjdk-7-jdkUbu…

python解压到指定文件夹_在Python中压缩和解压文件

Python部落(python.freelycode.com)组织翻译&#xff0c;禁止转载&#xff0c;欢迎转发。 如果你已经使用计算机一段时间&#xff0c;你可能遇到了.zip扩展名的文件。它们是可以保存许多其他文件&#xff0c;文件夹和子文件夹的压缩内容的特殊文件。这种类型的文件在使用互联网…

android bar布局,Android学习路线(十)如何将Action Bar叠放在你的布局上

默认状况下&#xff0c;action bar出如今activity窗口的顶部&#xff0c;略微减小了activity布局的总空间。若是你想隐藏或者显示action bar&#xff0c;在这堂用户体验的课程中&#xff0c;你能够经过调用htmlFigure 1. Gallerys action bar in overlay mode.android为了不在a…

geant4运行例子_Geant4--一次编译,运行多个Run,极大提升模拟效率

文|梁佐佐应唐光毅博士/后之约&#xff0c;对于Geant4模拟&#xff0c;我们看是否能解决这么一个问题&#xff1a;我现在想模拟探测器不同角度下的响应&#xff0c;每次模拟需要/run/beamOn 100&#xff0c; 可是我真的不想一遍一遍的去http://DetectorConstruction.cc中修改几…

python3.7基础教程_关于本教程 |《Python 官方文档:入门教程 3.7.0》| Python 技术论坛...

本文档最新版为 3.8&#xff0c;旧版本可能放弃维护&#xff0c;推荐阅读最新版&#xff01; Python 入门教程 Python 是一门简单易学且功能强大的编程语言。它拥有高效的高级数据结构&#xff0c;并能够用简单又有效的方式进行面向对象编程。Python 优雅的语法和动态类型&…

android listview countdowntimer,Android-ListView中的CountDownTimer随机闪烁

我正在使用计时器制作列表视图&#xff0c;每个计时器都有不同的截止日期&#xff0c;具体取决于数据库(类似于拍卖)Time now new Time();now.setToNow();now.normalize(true);nowMillis now.toMillis(true);..String endtime a.get(position).get(TAG_ENDTIME);Integer tim…

echart实现3d地图_3D飞线效果——让线“飞”起来的秘密

在城市规划、统计、交通等行业&#xff0c;地图可视化已成为最直接也最吸引眼球的一种表达方式。例如人群迁徙、人口流动、OD出行、职住分析、客流来源等众多场景都需要用到飞线效果呈现。2D飞线效果图随着可视化技术的进一步发展&#xff0c;传统的2D飞线效果略显单调&#xf…

ad域管理与维护_在NAS SMB卷上使用VisualSVN Server维护代码库

VisualSVN Server[1] 是 Windows 平台上流行的 SVN 形式的代码管理工具。以下我们将介绍把 NAS SMB 卷作为 VisualSVN 代码库存储中心时会遇到的几个问题以及相应的解决方法。1. 安装错误的解决方法我们以 VisualSVN Server 3.3.1 版本为例&#xff0c;在安装 VisualSVN Server…

android 开发art,Android应用开发之Android 系统启动原理(art 虚拟机)

本文将带你了解Android应用开发之Android 系统启动原理(art 虚拟机)&#xff0c;希望本文对大家学Android有所帮助。Android 系统启动原理(art 虚拟机)一、虚拟机的启动Android 是一个 Linux 的虚拟机&#xff0c;当虚拟机启动的时候&#xff0c;会执行手机根目录下的 init.r…

电脑文件夹可以分屏的软件_电脑上什么便签软件可以添加音频?

提及便签&#xff0c;很多人都会很自然地想到手机便签。这是因为随着智能手机和移动互联网的发展&#xff0c;现在很多手机上都有了系统自带的便签app。其实&#xff0c;除了手机便签外&#xff0c;还有电脑便签呢&#xff01;这不&#xff0c;Win7及其以上版本的电脑上还有系统…

jsp form提交到后台中文乱码_JSP与servlet之间的数据传递

【51】Jsp与Servlet之间的传值有两种&#xff0c;一种是Jsp传值给Sevlet&#xff0c;另一种是Servlet传值给Jsp&#xff1b;使用request、response对象完成传值&#xff0c;具体实现如下&#xff1a;Jsp与Servlet之间的传值有两种&#xff0c;一种是Jsp传值给Sevlet&#xff0c…