链表 与 环
「链表带环问题」是经典面试考点。总结解法如下
1 判断单链表是否带环
Key:
快慢指针法。使用两个指针指向头节点,一个指针每次遍历两个结点(快指针),另一个指针每次遍历一个结点。当快慢指针相遇,则链表带环(不带环时 遇到空指针break)。
证明:带环链表快慢指针一定相遇。
快指针每次走两步,两步分开看:第一步与 慢指针的一步走完后在圆内相对位置不变,则快慢指针 的遍历过程可看作「慢指针不动,快指针每次走一步」
则两者必定相遇。
2 判断入口点
设快慢指针相遇于 a 点,其他参数见图示。
易知 快指针的移动距离为慢指针的两倍, 则有
2∗(L1+right)=L1+right+k∗L22∗(L1+right)=L1+right+k∗L2
化简可得
L1=left+(k−1)∗L2L1=left+(k−1)∗L2
由上式可知:
从起点遍历到入口点的长度 等于 从a点逆时针遍历n圈的距离 + 从a点逆时针走到入口点的距离
即:
一指针从起点开始遍历,另一指针从a点开始逆时针遍历,必定相遇于入口点。
3 链表相交问题
不带环
key:
若两链表相交,则链表最后一个点相同。
可通过栈的结构找到相交点
或遍历两链表a、b的长度,以 a 大于 b 为例,使用一个指针先走b-a步,再同时开始走,相遇点为交点。
带环
环在 交点后,同不带环问题
交点在 环中,视作交点在环的入口点
4 复杂链表的复制
//#define _CRT_SECURE_NO_WARNINGS
#define N 100
#include<iostream>
#include<assert.h>
using namespace std;
typedef struct ComplexNode
{
int _data; // 数据
struct ComplexNode * _next; // 指向下一个节点的指针
struct ComplexNode * _random; // 指向随机节点(可以是链表中的任意节点 or 空)
}cNode;
cNode* initNode(int data)
{
cNode* tempNode = (cNode*)malloc(sizeof(cNode));
tempNode->_data = data;
tempNode->_next = NULL;
tempNode->_random = NULL;
return tempNode;
}
cNode* initNode(cNode& lastNode)
{
cNode* tempNode = (cNode*)malloc(sizeof(cNode));
tempNode->_data = lastNode._data;
tempNode->_next = lastNode._next;
tempNode->_random = NULL;
return tempNode;
}
cNode* copyList(cNode* head)
{
assert(head);
cNode* nowNode = head;
while (nowNode)
{
cNode* tempNode = initNode(*nowNode);
}
}
int main()
{
getchar();
getchar();
return 0;
}