环形链表相关的练习
创始人
2024-06-01 00:45:44
0

目录

一、相交链表

二、环形链表

三、环形链表 ||



一、相交链表

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

图示两个链表在节点 c1 开始相交:

 题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

代码实现

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{// 1. 分别找到两个单链表的尾结点,并计算它们的长度struct ListNode *tailA = headA, *tailB = headB;int lenA = 1, lenB = 1;while (tailA->next != NULL){++lenA;tailA = tailA->next;}while (tailB->next != NULL){++lenB;tailB = tailB->next;}if (tailA != tailB)  // 如果两个链表不相交,则尾结点的地址不同{return NULL;}// 2. 让指向长链表的指针先走差距步int gap = abs(lenA - lenB);struct ListNode *longCur = headA, *shortCur = headB;if (lenA < lenB){longCur = headB;shortCur = headA;}for (int i = 0; i < gap; ++i){longCur = longCur->next;}// 3. 让 longCur 和 shortCur 同时向后走,直到找到相同地址的结点while (longCur != shortCur){longCur = longCur->next;shortCur = shortCur->next;}return longCur;
}


二、环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

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

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例 1

 输入:head = [3,2,0,-4], pos = 1

输出:true

解释:链表中有一个环,其尾部连接到第二个节点。

示例 2

 输入:head = [1,2], pos = 0

输出:true

解释:链表中有一个环,其尾部连接到第一个节点。

示例 3

 输入:head = [1], pos = -1

输出:false

解释:链表中没有环。

提示

  • 链表中节点的数目范围是 [0, 10^4]

  • -105 <= Node.val <= 105

  • pos-1 或者链表中的一个 有效索引

进阶:你能用 O(1)(即,常量)内存解决此问题吗?

代码实现一

bool hasCycle(struct ListNode *head) 
{struct ListNode* addr[10000] = { 0 };  // addr 是保存每个结点地址的指针数组int pos = 0;  // pos 始终是第一个未存放结点地址的数组下标struct ListNode* cur = head;while (cur != NULL){addr[pos++] = cur;// 检查 cur->next 是否指向之前的结点或自己for (int i = 0; i < pos; ++i) {if (addr[i] == cur->next){return true;}}cur = cur->next;}return false;  
}

代码实现二(快慢双指针)

bool hasCycle(struct ListNode *head)
{struct ListNode* slow = head;struct ListNode* fast = head;while (fast && fast->next)  // 如果链表不带环,则快指针先走到空或尾{slow = slow->next;fast = fast->next->next;if (slow == fast){return true;}}return false;
}

问题(前提是链表带环)

  1. 快慢指针从相同的起始位置出发,慢指针 slow 每次走一步,快指针 fast 每次走两步,请问这两个指针为什么一定会再次相遇?

    设当 slow 走到入环的第一个结点时,fast 距 slow y 步(0 <= y <= C,C 表示环的长度),然后 slow 走 x 步,fast 走 2x 步后,两个指针再次相遇,则有:2x - x = y,即 x = y

    y == 0,即当 slow 走到入环的第一个结点时,就和 fast 再次相遇了;y == C,即入环的第一个结点就是头结点,如示例 2

  2. 快慢指针从相同的起始位置出发,慢指针 slow 每次走一步,快指针 fast 每次走 n 步(n >= 3),请问这两个指针也一定会再次相遇吗?

    n * x - x = y,即 (n - 1)x = y ==> x = y / (n - 1)

    例如

     


三、环形链表 ||

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

不允许修改 链表。

示例 1

 输入:head = [3,2,0,-4], pos = 1

输出:返回索引为 1 的链表节点

解释:链表中有一个环,其尾部连接到第二个节点。

示例 2

 输入:head = [1,2], pos = 0

输出:返回索引为 0 的链表节点

解释:链表中有一个环,其尾部连接到第一个节点。

示例 3

 输入:head = [1], pos = -1

输出:返回 null

解释:链表中没有环。

提示

  • 链表中节点的数目范围在范围 [0, 10^4]

  • -105 <= Node.val <= 105

  • pos 的值为 -1 或者链表中的一个有效索引

进阶:你是否可以使用 O(1) 空间解决此题?

代码实现一

struct ListNode *detectCycle(struct ListNode *head) 
{struct ListNode* addr[10000] = { 0 };int pos = 0;struct ListNode* cur = head;while (cur != NULL){addr[pos++] = cur;for (int i = 0; i < pos; ++i){if (addr[i] == cur->next){return addr[i];}}cur = cur->next;}  return NULL;
}

代码实现二

struct ListNode *detectCycle(struct ListNode *head)
{struct ListNode* slow = head;struct ListNode* fast = head;while (fast && fast->next){slow = slow->next;fast = fast->next->next;if (slow == fast)  // 再次相遇{struct ListNode* start = head;  // start 从起始结点出发struct ListNode* meet = slow;  // meet 从相遇结点出发while (start != meet){start = start->next;meet = meet->next;}return start;  // 或者 return meet;}}return NULL;
}

分析

其中 L 表示指针从头结点走到入环第一个结点所需要的步数,x 表示慢指针走到入环的第一个结点后,与快指针再次相遇所需走的步数,C 则表示环的长度

从起点出发到再次相遇,慢指针 slow 走的步数为 L + x,快指针 fast 走的步数为 L + k * C + x(k >= 1),又因为 slow 每次走一步,fast 每次走两步,所以有:2(L + x) = L + k * C + x,即 L = k * C + x ==> L = (k - 1)C + x

由此得出一个结论:在链表有环的前提下,一个指针从起始结点开始走,另一个指针从再相遇结点开始走,两个指针每次走一步,最终这两个结点会在入环的第一个结点相遇

代码实现三

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{struct ListNode *tailA = headA, *tailB = headB;int lenA = 1, lenB = 1;while (tailA->next != NULL){++lenA;tailA = tailA->next;}while (tailB->next != NULL){++lenB;tailB = tailB->next;}if (tailA != tailB){return NULL;}int gap = abs(lenA - lenB);struct ListNode *longCur = headA, *shortCur = headB;if (lenA < lenB){longCur = headB;shortCur = headA;}for (int i = 0; i < gap; ++i){longCur = longCur->next;}while (longCur != shortCur){longCur = longCur->next;shortCur = shortCur->next;}return longCur;
}
​
struct ListNode *detectCycle(struct ListNode *head)
{struct ListNode* slow = head;struct ListNode* fast = head;while (fast && fast->next){slow = slow->next;fast = fast->next->next;if (slow == fast){// 转换成求相交结点struct ListNode* headB = slow->next;slow->next = NULL;return getIntersectionNode(head, headB);}}return NULL;
}

相关内容

热门资讯

六月二十七日望湖楼醉书 这首诗... 六月二十七日望湖楼醉书 这首诗的写作结构是怎样的?雨前:黑云到入船。雨停:第三句。雨后:最后一句
如果我带着醉意出生,或许我会忘... 如果我带着醉意出生,或许我会忘掉所有的哀伤,这是哪本书上的句子《八百万种死法》,最近电视剧无懈可击之...
优美动人怎么造句孑 优美动人怎么造句孑她的舞姿优美动人,真是让人难以忘怀!
在科学上没有平坦的大道,只有不... 在科学上没有平坦的大道,只有不畏艰险沿着陡峭山路攀登的人,才有希望达到光辉的顶点。这句话的意思。这句...
有什么好看的网王小说?要完结的 有什么好看的网王小说?要完结的四叶三叶草,我的幸福《网王守望幸福《网王——今生无忧》(《夏之纪年》《...
郭达换大米剧本谁写的 郭达换大米剧本谁写的换大米的作者是 郭达和蔡明
园林绿化公司怎么起名 园林绿化公司怎么起名我们是做园林绿化设计、施工管理的自然世界园林绿化公司,新鲜空气园林绿化公司,超时...
命运石之门,谁发信对主人公说你... 命运石之门,谁发信对主人公说你知道的太多了FB指示萌郁发送的(官方资料设定集里写了)FB即是楼下的显...
有关超市开业播放的音乐 有关超市开业播放的音乐请各位关注本问题的大侠们,帮我找一下超市、商场开业时用到的喜庆音乐!谢谢,有追...
现代人物人物苦学成才的故事 现代人物人物苦学成才的故事现代人物人物苦学成才的故事 张恨水先生是我国著名的现代作家。17岁时,他...
是惊的成语,以惊字结尾的成语,... 是惊的成语,以惊字结尾的成语,惊字在后面的成语 第四个字是惊的成语详细〔 胆颤心惊 〕颤:发抖。形...
判断一个女人对你“欲擒故纵”,... 判断一个女人对你“欲擒故纵”,还是“压根没戏”,关键看哪几点?首先知竖,对方给你回消息的语气。如历芹...
自己写的儿童诗 自己写的儿童诗有什么可以帮你的追问:我要自己写的儿童诗,谢谢了!追答:主要是我都不知道有什么要求啊追...
地下城堡2聚火之心有用吗 地下城堡2聚火之心有用吗有用。《地下城堡2:黑暗觉醒》是一款模拟经营与地牢探险游戏高贺轿。游戏中聚火...
在学校如何和老师斗智斗勇 在学校如何和老师斗智斗勇 老师说上课是对牛弹琴时,作为学生的我们应该感到高兴,因为老师说的是事实啊...
神级龙卫男主和谁在一起 神级龙卫男主和谁在一起你好。神级龙卫男主沈浪跟女主白倾雨,苏若雪,柳潇潇最终走到一起了。让我们来看看...
开学以来作文520初中 开学以来作文520初中初一的感觉 似水流年,今天的我们已不再是在草地里玩过家家的小朋友;今天的我们已...
家有黄仙能养两只猫吗 家有黄仙能养两只猫吗可以养的,不犯冲突,猫捉老鼠是老鼠的天敌,养猫后家里的老鼠会少很多。
失忆后的人会不会有恐惧和绝望的... 失忆后的人会不会有恐惧和绝望的心理?我是指那种完全忘记了自己的一切的人。失忆...如果真的全部忘记,...
胡萝卜长期保存方法 胡萝卜长期保存方法胡萝卜放保鲜袋里放冰箱可以放很长时间。