大概的题意是,给出一个单向链表的头节点,求这么链表是否有环。有环的定义是,链表的尾节点指向了链接中间的某个节点。单链表的定义如下:
struct Node {
Node(): next(NULL) {}
Node *next;
};
这道题目还有一些变种,比如求链接的长度,求链表的尾节点等,不过大同小异。第一次听说这个题目,是在大三的时候,从一个刚面完试的学长那里。他当时的做法是对这个链表求逆,求逆的结果链表上每一个指针都反向,如果逆序完之后,链表的头节点不变,即说明链接有环。具体的证明过程略。这个方法的复杂也是O(N),但是需要修改原链表,或者使用O(N)的额外空间。
标准做法是使用两个指针,一个每次往前走2步,一个每次往前走1步,如果两个指针相遇,即说明链表有环,时间复杂度为O(N),空间复杂度为O(1)。
代码如下:
bool TestCircle(Node *p){
if (p == NULL){
return false;
}
if (p->next == NULL){
return false;
}
if (p->next == p){
return true;
}
Node *y = p->next;
Node *x = p->next->next;
while(x != NULL && y != NULL){
x = x->next;
if (x == NULL) break;
x = x->next;
y = y->next;
if (x == y) break;
}
return x == y;
}
测试数据也比较简单,考虑一下空指针、单个节点成环、整条链表成环就可以了:
void RunTest() {
Node *p = NULL;
Node *q = NULL;
cout << "empty list" << endl;
cout << TestCircle(NULL) << endl;
cout << "one node not circle" << endl;
p = new Node;
cout << TestCircle(p) << endl;
delete p;
p = NULL;
cout << "one node circle" << endl;
p = new Node;
p->next = p;
cout << TestCircle(p) << endl;
delete p;
p = NULL;
cout << "two nodes not circle" << endl;
p = new Node;
p->next = new Node;
cout << TestCircle(p) << endl;
delete p->next;
delete p;
p = NULL;
cout << "two nodes circle" << endl;
p = new Node;
p->next = new Node;
p->next->next = p;
cout << TestCircle(p) << endl;
delete p->next;
delete p;
p = NULL;
cout << "two nodes circle case 2" << endl;
p = new Node;
p->next = new Node;
p->next->next = p->next;
cout << TestCircle(p) << endl;
delete p->next;
delete p;
p = NULL;
cout << "5 nodes not circle" << endl;
p = new Node;
q = p;
for (int i = 0; i < 4; ++i) {
q->next = new Node;
q = q->next;
}
cout << TestCircle(p) << endl;
while (p->next != NULL) {
q = p->next->next;
delete p->next;
p->next = q;
}
delete p;
p = NULL;
cout << "5 nodes circle" << endl;
p = new Node;
q = p;
for (int i = 0; i < 4; ++i) {
q->next = new Node;
q = q->next;
}
q->next = p;
cout << TestCircle(p) << endl;
q->next = NULL;
while (p->next != NULL) {
q = p->next->next;
delete p->next;
p->next = q;
}
delete p;
p = NULL;
cout << "5 nodes circle case 2" << endl;
p = new Node;
q = p;
for (int i = 0; i < 4; ++i) {
q->next = new Node;
q = q->next;
}
q->next = p->next->next;
cout << TestCircle(p) << endl;
q->next = NULL;
while (p->next != NULL) {
q = p->next->next;
delete p->next;
p->next = q;
}
delete p;
p = NULL;
cout << "5 nodes circle case 3" << endl;
p = new Node;
q = p;
for (int i = 0; i < 4; ++i) {
q->next = new Node;
q = q->next;
}
q->next = q;
cout << TestCircle(p) << endl;
q->next = NULL;
while (p->next != NULL) {
q = p->next->next;
delete p->next;
p->next = q;
}
delete p;
p = NULL;
cout << "100000000 nodes not circle" << endl;
p = new Node;
q = p;
for (int i = 0; i < 100000000; ++i) {
q->next = new Node;
q = q->next;
}
cout << TestCircle(p) << endl;
while (p->next != NULL) {
q = p->next->next;
delete p->next;
p->next = q;
}
delete p;
p = NULL;
cout << "100000000 nodes circle" << endl;
p = new Node;
q = p;
for (int i = 0; i < 100000000; ++i) {
q->next = new Node;
q = q->next;
}
q->next = p;
cout << TestCircle(p) << endl;
q->next = NULL;
while (p->next != NULL) {
q = p->next->next;
delete p->next;
p->next = q;
}
delete p;
p = NULL;
cout << "100000000 nodes circle case 2" << endl;
p = new Node;
q = p;
for (int i = 0; i < 100000000; ++i) {
q->next = new Node;
q = q->next;
}
Node *t = p;
for (int i = 0; i < 16; ++i) {
t = t->next;
}
q->next = t;
cout << TestCircle(p) << endl;
q->next = NULL;
while (p->next != NULL) {
q = p->next->next;
delete p->next;
p->next = q;
}
delete p;
p = NULL;
}