每日精讲:环形链表、两个数组中的交集、随机链表的复制

每日精讲:环形链表、两个数组中的交集、随机链表的复制
 Hello大家好! 很高兴与大家见面! 给生活添点快乐,开始今天的编程之路。



我的博客:<但愿.

我的专栏:C语言题目精讲算法与数据结构C++

欢迎点赞,关注

一 环形链表

1.1题目链接:环形链表II

1.2题目描述:

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。注意不允许修改 链表。

1.3题目示例:

1.4题目思路:

在我的主页的题目精讲中讲过C语言的方法(快慢指针)C语言相对复杂,这里我们使用C++STL实现。这里使用到STL的set遍历链表如果链表中的节点不在set中就插入(注意这里不是插入节点的值,而是节点的指针,因为节点的值可能有几个相等);如果在就带环,并且该节点就是环入口点。

1.5解决代码

//1cur不在set对象中,就插入到set对象中 //2cur在set对象中就带环,并且该节点就是环入口点 class Solution { public: ListNode *detectCycle(ListNode *head) { set<ListNode*> s; ListNode* cur = head; while(cur) { auto it = s.find(cur);//查找cur是否在set对象中 if(it == s.end())//cur不在set对象中,就插入到set对象中 { s.insert(cur); } else//cur在set对象中就带环,并且该节点就是环入口点 { return *it } cur = cur->next; } return nullptr; } };

二 两个数组中的交集

2.1题目链接:两个数组的交集

2.2题目描述:

给定两个数组 nums1 和 nums2 ,返回 它们的 交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

2.3题目示例:

示例 1:输入:nums1 = [1,2,2,1], nums2 = [2,2] 输出:[2]

示例 2:输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出:[9,4] 解释:[4,9] 也是可通过的

2.4题目思路:

思路1:一个数组的每个值到另一个数组中遍历,这种虽然可行,但是和拿哪个数组中的值到哪个数组中遍历有关例如示例2如果拿nums1中的每个值到nums2中遍历可行,但是如果拿nums2中的每个值到nums1中就会得到结果[9,4,9,4]是不正确的,所以这种方法不行。

思路2(正确思路):先将两个数组去重(要先排序只有排序了才能真正的去重)【去重可用STL中是set;也可以用算法库中的去重算法std::unique】,在拿一个数组中的值分别到另一个数组中找(使用STL->set->count,最后将遍历找到的插入到一个容器中返回即可。

2.5解决代码:

class Solution { public: vector<int> intersection(vector<int>& nums1, vector<int>& nums2) { //去重 set<int> s1(nums1.begin(),nums1.end()); set<int> s2(nums2.begin(),nums2.end()); vector<int> v; //将一个数组中的值依次到另一个数组中查找 for(auto e : s1) { if(s2.count(e)) { v.push_back(e);//在另一个数组中插入到一个容器中 } } return v; } };

2.6作用:

从上面来看这个是没有什么作用的,但是现实生活中不只是用来查找交集的,一般是要找交/差/并集,这时候就有著名的对比算法。

两个数组中找交/差/并集的整体思路(对比算法):

【示例】使用对比算法找交集:

class Solution { public: vector<int> intersection(vector<int>& nums1, vector<int>& nums2) { set<int> s1(nums1.begin(), nums1.end()); set<int> s2(nums2.begin(), nums2.end()); // 因为set遍历是有序的,有序值,依次⽐较 // ⼩的++,相等的就是交集 vector<int> ret; auto it1 = s1.begin(); auto it2 = s2.begin(); while(it1 != s1.end() && it2 != s2.end()) { if(*it1 < *it2) { it1++; } else if(*it1 > *it2) { it2++; } else { ret.push_back(*it1); it1++; it2++; } } return ret; } };

【对比算法在生活中的应用】

例如我们要更换手机,而之前的手机中有许多的照片是要的,此时怎么办呢?这时候可以买个华为云,将华为云中的照片和之前手机中的照片进行对比,判断是否要导入到华为云中,而照片存在,但是照片可能存在更改,这时候可以比较照片最后的时间。所以这个对比算法在生活中还是有很多用处的。

三 随机链表的复制(两种解决办法C语言[了解思路]、C++)

3.1题目链接:随机链表的复制

3.2题目描述:

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。

你的代码接受原链表的头节点 head 作为传入参数。

3.3题目示例:


3.4题目思路:

3.5解决代码:

【c语言解决代码】注意利用这个了解思路

/**  * Definition for a Node.  * struct Node {  *     int val;  *     struct Node *next;  *     struct Node *random;  * };  */ typedef struct Node Node; //构造一个简单 Node* buyNode(int x) {     Node* newnode = (Node*)malloc(sizeof(Node));     newnode->val = x;     newnode->next=newnode->random = NULL;     return newnode; } //在原链表的基础上拷贝节点(即原链表的每个节点后面拷贝一个一样的节点) void AddNode(Node* head) {     Node* pcur = head;     while(pcur)     {         Node* newnode = buyNode(pcur->val);         Node* next = pcur->next;         newnode->next = next;         pcur->next = newnode;         pcur = next;     } } //设置新节点的random利用关系式 //copy(新节点)->random = pcur(原链表节点)->random->next; void setRandom(Node* head) {     Node* pcur = head;     while(pcur)     {         Node* copy = pcur->next;         if(pcur->random)         {             copy->random = pcur->random->next;         }         pcur = copy->next;     } } struct Node* copyRandomList(struct Node* head) {     if(head == NULL)//如果头节点为空要特殊处理     {         return head;     }     //1在原链表基础上拷贝节点并插入到原链表的每个节点之后     AddNode(head);     //2设置新节点的random     setRandom(head);     //断开新旧链表 //注意这里要返回新链表的根节点,所以这里要定义两个变量 //一个用于储存新链表的根节点位置,一个用于从新链表的根节点位置开始遍历。     Node* pcur = head;     Node* copyHead,*copyTail;     copyHead = copyTail = pcur->next;     while(copyTail->next)     {         pcur = copyTail->next;         copyTail->next = pcur->next;         copyTail = copyTail->next;     }     return copyHead; }

【c++使用STL解决代码】

上面讲了C语言的处理方法,由于出现一个很难处理的问题节点中的random(随机)节点怎么拷贝,如果通过节点中的值进行判断指向关系【如果一个节点的random指向的节点值是1,而节点值为1的节点不止一个有怎么解决呢?显然这种方法是不行的】,如果想走这种匹配关系,那只能暴力遍历,此时时间复杂度为O(N^2)不会,这是因为原链表和拷贝出的链表直接的节点没关系才导致的,C语言的处理方法是先拷贝在原链表中在就导致原链表和拷贝出的链表之间的节点有了关系。那C++怎么处理了,C++所以STL中的map由于储存map(原链表节点,拷贝链表的节点)这不就巧妙的将原链表和拷贝出的链表之间的节点联系起来了吗,可以通过原链表节点得到拷贝对应的节点。

【解决代码】

class Solution { public: Node* copyRandomList(Node* head) { map<Node*, Node*> nodeMap;//由于存在原链表节点和拷贝的节点,从而导致两者之间有关系 Node* copyhead = nullptr,*copytail = nullptr;//由于储存拷贝链表的根节点和遍历拷贝链表 Node* cur = head; while(cur) { if(copytail == nullptr)//如果为空特殊处理 { copyhead = copytail = new Node(cur->val); } else//如果不为空进行尾插 { copytail->next = new Node(cur->val); copytail = copytail->next; } // 原节点和拷⻉节点map kv存储 nodeMap[cur] = copytail; cur = cur->next; } // 处理random cur = head; Node* copy = copyhead; while(cur) { if(cur->random == nullptr)//如果原节点的random为空则拷贝节点的random也为空 { copy->random = nullptr; } else//如果原节点的random不为空则,则使用map[],传入参数原链表节点指针,得到拷贝出的对应节点 { copy->random = nodeMap[cur->random]; } cur = cur->next; copy = copy->next; } return copyhead; } };

本篇文章就到此结束,欢迎大家订阅我的专栏,欢迎大家指正,希望有所能帮到读者更好了算法与数据结构相关知识 ,觉得有帮助的还请三联支持一下~后续会不断更新算法与数据结构相关知识,我们下期再见。

Read more

极致性能的服务器Redis之Hash类型及相关指令介绍

极致性能的服务器Redis之Hash类型及相关指令介绍

目录 1. Hash介绍 2. hset 3. hget 3. hdel 5. hkeys 6. hvals 编辑 7. hgetall  8. hexists 9. hmget 10. hlen 11. hsetnx 12. hincrby 13. hincrbyfloat 1. Hash介绍 Redis 哈希类型是键值对的集合,字段与值均支持字符串、数字等类型,适合建模用户信息、配置项等对象类数据。其支持单字段 / 多字段的增删改查、字段存在性判断、值自增自减等原子操作,且底层通过压缩列表或哈希表优化存储,空间利用率高、查询效率快,是 Redis 中存储结构化数据的核心类型之一。 在Redis中因为本身就是按照哈希的KV结构来进行存储的,所以当我们想要使用Redis里面的哈希的时候,实际上是哈希的哈希,在后者中,

By Ne0inhk
环形房屋如何 “安全劫舍”?动态规划解题逻辑与技巧

环形房屋如何 “安全劫舍”?动态规划解题逻辑与技巧

环形房屋如何 “安全劫舍”?动态规划解题逻辑与技巧 * 1、问题描述 * 2、解题思路 * 3、动态规划解法 * 3.1 辅助函数 * 3.2 主函数 * 4、代码解析 * 5、复杂度分析 * 6、测试用例 * 7、关键点总结 * 8、常见问题解答 🌺The Begin🌺点点关注,收藏不迷路🌺 1、问题描述 你是一个专业的小偷,计划偷窃环形排列的房屋。每间房屋都有一定金额,但如果偷窃相邻的两间房屋就会触发警报。计算在不触发警报的情况下能够偷窃到的最高金额。 2、解题思路 这个问题是经典打家劫舍问题的变种,房屋排列成环形。我们可以将其分解为两个子问题: 1. 不偷第一间房屋 2. 不偷最后一间房屋 然后取这两个子问题的最大值作为最终结果。 3、动态规划解法 3.1

By Ne0inhk

多模态算法面经准备

目录 * 小米-多模态算法工程师 * 1、对多模态大模型的了解 * 1.1 CLIP * 1.2 BLIP * 1.3 BLIP-2 * 2、文生图、图生图? * 3、目前的图像或视频编码器,核心思想方法是什么? * 4、GPT * 4、语义分割模型与指标 * 4.1 Unet * 4.2 DeepLab * 4.3 语义分割的损失函数 * 4.4 评价指标 小米-多模态算法工程师 1、对多模态大模型的了解 1.1 CLIP CLIP利用对比学习(Contrastive Learning)对图像和文本进行联合训练。 1.2 BLIP 原文 BLIP的模型架构包括4个关键部分:

By Ne0inhk
【递归、搜索与回溯算法必刷42题:专题一】从汉诺塔问题到快速幂

【递归、搜索与回溯算法必刷42题:专题一】从汉诺塔问题到快速幂

🎬 个人主页:艾莉丝努力练剑 ❄专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》 《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》 ⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平 🎬 艾莉丝的简介: 🎬艾莉丝的算法专栏简介: 文章目录 * 本文设计专题一算法题链接 * 1 汉诺塔问题 * 题目描述 * 汉诺塔问题(递归解法) * 1. 问题描述 * 2. 递归思想 * 基本情况(递归终止条件) * 递归分解(n ≥ 2) * 3. 递归算法流程(函数设计) * 函数头 * 递归函数流程: * 解题过程 * 算法实现(C++) * 2 合并两个有序链表 * 题目描述 * 解题过程 * 算法实现(

By Ne0inhk