每日精讲:环形链表、两个数组中的交集、随机链表的复制
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; } };本篇文章就到此结束,欢迎大家订阅我的专栏,欢迎大家指正,希望有所能帮到读者更好了算法与数据结构相关知识 ,觉得有帮助的还请三联支持一下~后续会不断更新算法与数据结构相关知识,我们下期再见。