- 定义
struct ListNode{
int value;
ListNode* next;
ListNode(int v,ListNode* nt):value(v),next(nt){}
ListNode(int v):ListNode(v,nullptr){}
ListNode():ListNode(0){}
virtual ~ListNode()=default;
};
- 问题
1.如何判断一个链表有环
2.环的长度是多少
3.环的结点位置在哪里
4.链表的长度是多少
5.如何变成无环单向链表
关于这些问题可以定义一个结构体
struct RingListInfo {
bool has_ring = false;
size_t length = 0;
size_t ring_length = 0;
ListNode* ring_node = nullptr;
ListNode* tail = nullptr;
ListNode* head = nullptr;
RingListInfo()=default;
};
使用map
#include <map>
void list_info(ListNode* head,RingListInfo& info){
info.head = head;
if(!head){return;}
std::map<ListNode*,int> map_;
int idx = 0;
ListNode* before = nullptr; // 记录每次迭代的前一个结点
for(auto p=head;p->next;p=p->next){
if(map_.find(p) == map_.end()){
set_.insert({p,idx++});
}else{ // p点是环的结点
info.has_ring = true;
info.ring_node = p;
info.length= map_.size();
info.ring_length = info.length - map_[p]
info.tail = before;
return;
}
before = p;
}
}
空间复杂度 O(n) ,时间复杂度O(nlogn)
使用快慢指针
void list_info(ListNode* head,RingListInfo& info){
info.head = head;
if(!head){return;}
auto slow = head;
auto fast = head;
int ct_slow = 0; // 记录慢指针的步数
int ct_fast = 0; // 记录快指针的步数
while(true){
if(!fast){break;}
slow = slow->next;
ct_slow += 1;
fast = fast->next;
ct_fast += 1;
if(!fast){break;}
fast = fast->next;
ct_fast += 1;
if(slow == fast){ // 快慢指针的相遇点
info.has_ring = true;
break;
}
}
info.ring_length = ct_slow ; // head 到相遇点的距离就是环的长度!
if(!info.has_ring){return;}
// meet_node => 相遇点
// head到ring_node 的距离等于meet_node到ring_node的距离
auto left = head;
auto right = slow;
int ct = 0;
while(left != right){
ct += 1;
left=left->next;
right=right->next;
}
info.ring_node = left;
info.length = info.ring_length + ct;
}
- 解析
设 头指针head,最后一个结点指针tail
环结点为 ring_node ,快慢指针的相遇点为 meet_node
list上两个指针的距离 <p1,p2> ,就是重复p1 = p1->next直到p1 == p2时候的重复次数
环长度ring_length,链表长度length,<head,ring_node> = length - ring_length;
如果list有环
首先 快慢指针一定会相遇,相遇位置一定是在在环上!
如果单纯想知道环的长度,让慢指针再走一圈就知道了,ring_length = <meet_node,meet_node>
在些需要发现和证明一件事,就是 <head,meet_node> == <meet_node,meet_node>;
因为 speed(fast) = 2 * speed(slow) ,当slow和fast现时从head出发,slow到达meet_node时,fast走的路途就是
<head,meet_node> + <meet_node,meet_node> = 2 * <head,meet_node>
即 <meet_node,meet_node> = <head,meet_node> = ring_length
自然 设计一个slow_left从meet_node出发,slow_right从head出发,slow_left和slow_right就会在ring_node相遇.