hot100 - 链表(上)

目录

🌼相交链表

AC  哈希

AC  双指针

AC  截去较长 list

🌼反转链表

AC  迭代

AC  递归

🌼回文链表

AC  数组

AC  递归

AC  快慢指针

🌼环形链表

AC  哈希表

AC  快慢指针

🚩环形链表 II

AC  哈希表

AC  快慢指针

🌼合并两个有序链表

AC  递归

AC  迭代


🌼相交链表

160. 相交链表 - 力扣(LeetCode)

3 种方法 

注意:交叉不是值相等,直接两个结构体指针 == (判等)

第 1 种(哈希):时间 O(m + n),空间 O(m)

unordered_set 哈希表,先将 listA 所有元素加入哈希表,用 .count() 查询 listB 中的元素是否在 map 中

第 2 种(双指针):时间 O(m + n),空间 O(1)

官解解法二,双指针,特点是,listA,listB 中指针非空时,同时向后移动,直到一方为 nullptr,然后跳到另一个链表头指针位置,继续移动;当双方都跳到对方头部,且再次移动到 nullptr 时,还未 ==,意味着没有交叉点,否则中间必然出现 == 情况,return

第 3 种(截去较长的list):时间 O(m + n),空间 O(1)

类似官解二,但是,先找到长度 m, n 较小值,然后让较长的 list 的头指针,先往前移动 m - n 个位置(假设 m > n),然后可以开始同步移动判等 ==

AC  哈希

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {unordered_set<ListNode *> check_set;ListNode *temp = headA;// listA 元素插入哈希表while (temp != nullptr) {check_set.insert(temp);temp = temp->next;}// 遍历 listB 判等temp = headB;while (temp != nullptr) {if (check_set.count(temp))return temp;temp = temp->next;}return nullptr;}
};

AC  双指针

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {// 特判if (headA == nullptr || headB == nullptr)return nullptr;// 双指针遍历 O(m + n)ListNode *pA = headA, *pB = headB;while (pA != pB) { // 直到交叉点 或 同时为空(没有交叉点)pA = (pA == nullptr ? headB : pA->next);pB = (pB == nullptr ? headA : pB->next);}return pA;}
};

AC  截去较长 list

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {// 特判if (headA == nullptr || headB == nullptr)return nullptr;// 得到长度int m = 0, n = 0;ListNode *temp = headA;while (temp != nullptr) {m++;temp = temp->next;}temp = headB;while (temp != nullptr) {n++;temp = temp->next;}// 截去较长 listif (m > n) {while (m-- != n) headA = headA->next;}elsewhile (n-- != m)headB = headB->next;// 此时长度相等,遍历判等ListNode *pA = headA, *pB = headB;while (pA != pB) {pA = pA->next;pB = pB->next;}return pA;}
};

🌼反转链表

206. 反转链表 - 力扣(LeetCode)

 做链表的题,一定要画图,明确 建立新连接 的过程,代码就很容易写了

2 种方法

第 1 种(迭代)

时间 O(n),空间 O(1)

3 个变量,pre, cur, nex,代表 3 个位置,同步向右移动,有个小坑,二刷时写一遍就知道了

第 2 种(递归)

时间 O(n),空间 O(n)

先解释下递归👇

遇到递归就模拟栈,顶层的元素被加入栈底,回溯时,栈底最后出栈,栈顶先出栈

关于递归中return的理解(最浅显易懂)_都return了为什么还在递归-CSDN博客

大家可以想像一下,要计算1~100的累加和 sum(100),假设有一个栈,先将 sum(100) 插入,再插入 sum(99),直到 sum(1),此时 sum(100) 在栈底。

n == 1 就是递归的出口,此时从 sum(1) 开始出栈,直到 sum(100) 出栈,得到结果

最顶层是 n1 开始,最顶层 n1 插入栈底,回溯时,先得到 nm 的 return,

这里明确下,递归的出口时,head == nullptr 或 head->next == nullptr,

当到达递归出口时,表示到达了 nm,nm 直接返回,然后开始 n m-1.....

n m-1 在 nm 的基础上 return,同理,先 return 了 n3,才有后续的 n2,return 了 n2 才有 n1

AC  迭代

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {// 画图辅助理解// pre, cur, nex 是反转后的相对位置// cur 当前位置,pre 上一位置,nex 下一位置ListNode *cur = head, *nex = nullptr;while (cur != nullptr) {// pre 声明在 while 里,确保 cur 不为空ListNode *pre = cur->next; // pre 为 cur 右一位置// 建立新的连接cur->next = nex;// nex, cur 同时右移 1 个位置nex = cur;cur = pre;}// 此时 cur 指向空,nex 为新的头指针return nex;}
};

AC  递归

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {// 链表为空 || 递归出口if (head == nullptr || head->next == nullptr) {return head; // m 出栈}// 递归调用// 1 ~ m 入栈ListNode *newNode =  reverseList(head->next); // 新的头节点// m-1 ~ 1 出栈head->next->next = head; // next 反向head->next = nullptr; // 单向链表 且 n1指向nullptrreturn newNode; // 返回新的节点}
};

🌼回文链表

234. 回文链表 - 力扣(LeetCode)

3 种方法

第 1 种(数组):时间 O(n),空间 O(n)

拷贝到数组,然后双指针

第 2 种(递归):时间 O(n),空间 O(n)

递归栈辅助理解:

frontPoiner 从初始的头指针 head 开始,而递归函数中的 curNode 指针,n1 递归到 nm(每次函数执行一半,就要进入下一个递归),才真正开始执行后面的 curNode->val != frontPointer->val 的操作

第 3 种(快慢指针):时间 O(n),空间 O(1)

需要写 3 个函数:

a. 给定的判断回文的函数   b. 链表反转(给定指针往后)  c. 查找前半部分的尾节点

链表反转,采取 “反转链表” 那题,迭代的方法

查找前半部分尾节点,采取快慢指针,快指针一次走 2 步,慢指针一次 1 步

判断回文,分别从 head后半部分起点 出发

---------------

判断完回文后,最好能恢复链表(后半部分再次反转)

具体链表节点 奇数 / 偶数 的情况,自己模拟下

AC  数组

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:bool isPalindrome(ListNode* head) {vector<int> arr; // 类型是 int// 拷贝到数组while (head) {arr.push_back(head->val); // 插入的是大小head = head->next;}int n = arr.size();// 比较回文for (int i = 0, j = n - 1; i < j; ++i, --j) if (arr[i] != arr[j]) return false;return true;        }
};

AC  递归

1,题目原文 “给你一个单链表的头节点 head”,所以 head 可以作为函数参数传入函数,但是只能作为函数参数传入,不能在类内直接赋值给其他变量

2,力扣真的🤢,函数名都不让改,还是喜欢 ACM 模式,自己写处理舒服多了,力扣天天各种 BUG,函数 isPalindrome() 是给定的,不能改

3,return true; 或者 return false;

只会退出当前层递归,所以一个 true 无法影响到最终返回值

只要出现一次 false,考虑到 if (check(curNode->next) == false) return false;

最终返回的就是 false

class Solution {ListNode *frontPointer;
public:bool check(ListNode *curNode) {if (curNode != nullptr) { // 当前节点非空if (check(curNode->next) == false) // 递归下一节点return false;// head 从 nm 开始出栈, n1 在栈底if (frontPointer->val != curNode->val) // curNode: m ~ 1return false; // 非回文串frontPointer = frontPointer->next; // 1 ~ m}return true; // 退出当前层递归}bool isPalindrome(ListNode *head) {frontPointer = head;return check(head);}
};

AC  快慢指针

这里的 “链表反转” 函数,举个例子,

1->2->3->4->4->3->2->1->nullptr 变成了 1->2->3->4->4<-3<-2<-1(而且后半部分终点 4 指向 nullptr)(第一次反转把后半部分开头的 4 传入;第二次反转传入的是最右边的 1,因为反转函数,会返回新的头指针

class Solution {
public:// 特判 + 判断回文 + 恢复链表bool isPalindrome(ListNode* head) {// 特判 空链表if (head == nullptr) return true;ListNode *endFirst = end_of_first(head); // 前半尾节点// 反转链表ListNode *frontSecond = reverseList(endFirst->next); // 后半新的头节点// 判断回文ListNode *p1 = head, *p2 = frontSecond;bool flag = 1;while (p2 != nullptr && flag) {if (p1->val != p2->val)flag = 0;p1 = p1->next, p2 = p2->next;}// 恢复链表 -- 再次反向endFirst->next = reverseList(frontSecond);return flag;}// 反转链表,传入反转前头指针,返回反转后头指针ListNode* reverseList(ListNode* head){   // 1->2->3->o    o<-1<-2<-3ListNode *nex = nullptr, *cur = head;while (cur != nullptr) {ListNode *pre = cur->next; // pre在cur右一位置cur->next = nex; // 建立新连接nex = cur; // nex 右移 cur = pre; // cur 右移}return nex; // cur 为空时, nex 刚好在新的头节点位置}// 传入 头指针,返回 前半部分尾节点ListNode* end_of_first(ListNode* head){ListNode *fast = head, *slow = head; // 快慢指针while (fast->next != nullptr && fast->next->next != nullptr) {fast = fast->next->next; // 一次两步slow = slow->next;}return slow; // 前半部分尾节点}
};

🌼环形链表

141. 环形链表 - 力扣(LeetCode)

1,哈希表           2,快慢指针

快慢指针这个,开始考虑,能否只用一个指针,只需判断 == head,后来发现,head 可能不在环里,所以还是得两个指针

AC  哈希表

时间 O(n),空间 O(n)

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:bool hasCycle(ListNode *head) {unordered_set<ListNode *>s; // 哈希表while (head != nullptr) {if (!s.empty() && s.count(head))return true;s.insert(head);head = head->next;}return false;}
};

AC  快慢指针

时间 O(n),空间 O(1)

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:bool hasCycle(ListNode *head) {ListNode *fast = head;ListNode *slow = head;while (fast != nullptr && fast->next != nullptr) {fast = fast->next->next;slow = slow->next;if (fast == slow) // 不需要判断非空,因为非空的fast不会进去循环return true;}return false;}
};

🚩环形链表 II

142. 环形链表 II - 力扣(LeetCode)

1,哈希表:遍历一遍,第一个 s.count() 已存在的,就是交叉点

2,快慢指针👇

x -- head 到交叉点路程

y -- 快慢指针相遇位置

z -- 相遇位置回到交叉点

已知 fast 是 slow 的两倍速度,且 fast 至少转 1 圈(y + z)才能遇到 slow

假设转了 n 圈

fast 走过的路程 = x + y + n*(y + z)

slow 走过的路程 = x + y

可得等式:2*slow = fast

得到 x + y = n*(y + z),即 x = (n - 1) * (y + z) + z

表示,head 到 交叉点 == 快慢指针相遇点 + n-1 圈

根据上面等式,只需在相遇的同时,头指针从 head 开始移动,俩慢指针相遇点即可 return

AC  哈希表

时间 O(n),空间 O(n)

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *detectCycle(ListNode *head) {unordered_set<ListNode *>s;while (head != nullptr) {if (s.count(head))return head; // 先判断s.insert(head); // 再插入head = head->next;}return nullptr;}
};

AC  快慢指针

注意:只有一个节点时,没有环,此时 head->next == nullptr

时间 O(n),空间 O(1)

时间分析:slow 到相遇点,< O(n);slow 从相遇点到交叉点,也 < O(n),总共时间 < O(2n),所以是 O(n)

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *detectCycle(ListNode *head) {ListNode *fast = head;ListNode *slow = head;while (fast != nullptr) {slow = slow->next;if (fast->next == nullptr) // 无环return nullptr;fast = fast->next->next;if (fast == slow) { // 快慢指针相遇点// 相遇后,说明一定有环,即有交叉点while (slow != head) {head = head->next; // 头节点出发slow = slow->next; // 相遇点出发}return slow; // 交叉点}}return nullptr;}
};

🌼合并两个有序链表

21. 合并两个有序链表 - 力扣(LeetCode)

题目中 “l1 和 l2 均按 非递减顺序 排列”,这里的非递减,可以理解为,存在相等的递增序列

1,递归:l1, l2 任一存在空链表,返回另一链表;然后递归取较小值

2,迭代: 

AC  递归

时间 O(n + m),空间 O(n + m)

时间需要递归 n + m 次,空间递归栈的深度 n + m

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
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;}}
};

AC  迭代

时间 O(n + m),空间 O(1)

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {ListNode *prev = new ListNode(-1); // 只初始化值的构造函数ListNode *p = prev; // 哨兵// 直到一条链遍历完while (list1 != nullptr && list2 != nullptr) {if (list1->val < list2->val) {p->next = list1; // 注意这里是 p->next,下一位置list1 = list1->next;}else {p->next = list2;list2 = list2->next;}p = p->next;}// 将未遍历完的链,接到新的链表后p->next = (list1 == nullptr) ? list2 : list1;return prev->next; // 新链表头指针}
};

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

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

相关文章

Redis从入门到精通(四)Redis实战(一)短信登录

文章目录 前言第4章 Redis实战4.1 短信登录4.1.1 基于session实现短信登录4.1.1.1 短信登录逻辑梳理4.1.1.2 创建测试项目4.1.1.3 实现发送短信验证码功能4.1.1.4 实现用户登录功能4.1.1.5 实现登录拦截功能4.1.1.6 session共享问题 4.1.2 基于Redis实现短信登录4.1.2.1 Key-Va…

SpringBoot3整合RabbitMQ之二_简单队列模型案例

SpringBoot3整合RabbitMQ之二_简单队列模型案例 文章目录 SpringBoot3整合RabbitMQ之二_简单队列模型案例1. 简单队列模型1. 消息发布者1. 创建简单队列的配置类2. 发布消费Controller 2. 消息消费者3. 输出结果 1. 简单队列模型 简单队列模型就是点对点发布消息&#xff0c;有…

dm8 备份与恢复

dm8 备份与恢复 基础环境 操作系统&#xff1a;Red Hat Enterprise Linux Server release 7.9 (Maipo) 数据库版本&#xff1a;DM Database Server 64 V8 架构&#xff1a;单实例1 设置bak_path路径 --创建备份文件存放目录 su - dmdba mkdir -p /dm8/backup--修改dm.ini 文件…

【二分查找】Leetcode 在排序数组中查找元素的第一个和最后一个位置

题目解析 34. 在排序数组中查找元素的第一个和最后一个位置 我们使用暴力方法进行算法演化&#xff0c;寻找一个数字的区间&#xff0c;我们可以顺序查找&#xff0c;记录最终结果 首先数组是有序的&#xff0c;所以使用二分法很好上手&#xff0c;但是我们就仅仅使用上一道题…

【学习笔记】Elsevier的Latex模板文件(附网址)

注&#xff1a;这是一篇没有技术含量的水文&#xff0c;主要是看有人下载下来&#xff0c;居然当成资源需要积分才能下载。我觉得不行&#xff0c;故提供原始下载地址供查阅使用。 链接: 上述图片所示网址&#xff1a;链接直达

赛博炼丹师手记

文章目录 0. 丹炉安置1. 炼丹手法1.1 前置准备1.2 数据标注1.3 开始炼丹1.4 结果验收 2. 炼丹心法2.1 步数相关2.1 效率相关2.2 质量相关 3. 相关文献 0. 丹炉安置 个人推荐b站up主朱尼酱出品的道玄丹炉&#xff0c;新手易懂&#xff0c;老手易用。 丹炉下载地址如下&#xf…

NetSuite 自定义记录类型的权限控制

在近期的一个定制项目中&#xff0c;遭受了一次用户洗礼。有个好奇宝宝把我们的一个自定义类型的表记录进行了删除&#xff0c;导致一个重要功能失败。算是给我们扎实上了一课。自定义类型的权限也需要重视起来。所以&#xff0c;今朝我们记录下这个设置&#xff0c;同时写给未…

Linux 环境下 Redis基础配置及开机自启

Linux 环境下 Redis基础配置及开机自启 linux环境安装redis<redis-6.0.5.tar.gz> 1-redis基本安装配置 解压 获取到tar包后&#xff0c;解压到相关目录&#xff0c;一般是将redis目录放在usr/local/redis目录下&#xff0c;可以使用-C指定到解压下目录 tar -zvxf re…

Matlab|【防骗贴】【免费】基于主从博弈的主动配电网阻塞管理

目录 1 主要内容 程序亮点 2 部分代码 3 程序结果 4 下载链接 1 主要内容 《基于主从博弈的主动配电网阻塞管理》文献介绍&#xff1a;主要采用一种配电网节点边际电价统一出清的主从博弈双层调度框架。上层框架解决用户在负荷聚合商引导下的用电成本最小化问题&#xff0…

docker 部署 dujiaoka 独角数卡自动售货系统 支持 X86 和 ARM 架构

前言 很早就想部署一套自己的发卡自动售货系统&#xff0c;研究了很久发现独角数卡相对更加成熟好用&#xff0c;可是折腾技术三年多最怕的就是php和Laravel之类的语言和框架&#xff0c;各种权限&#xff0c;守护之类配置麻烦&#xff0c;加上如果跑在docker里更加头疼&#…

AttributeError: ‘Text‘ object has no property ‘FontSize‘

在学习《机器学习理论与实践》——1 机器学习编程语言基础中&#xff0c;使用Matplotlib画图&#xff08;在坐标轴或绘图区显示中文&#xff09;时&#xff0c;产生AttributeError: Text object has no property FontSize 错误解决。 AttributeError: Text object has no prop…

公开课学习——仿抖音直播平台

文章目录 直播抖音的直播原理Java继承直播客户端工具&#xff1a; ffmpeg客户端和网页集成CDN网络——性能提升关键——边缘计算 实时聊天——IM系统怎么实现&#xff1f;——websocketIM系统消息如何转发&#xff1f;直播场景IM系统是什么样子&#xff1f; 直播 抖音的直播原…