检测单链表是否有环有好几种方法。
- 可以设置两个指针前后步遍历链表,但是这个不好确定环的T型交叉点。
- 如果数据结构可以改就直接加个flag变量表示是否已经被遍历就可以,这个是最简单,但是一般要在不能改数据结构的情况下检测出环。
- 我自己还能想到就是初步估计链表的长度,利用链表节点的地址构建一个散列表,访问一个节点就把这个节点的地址放到散列表里面,这个要额外的数据结构来支持算法,在链表节点比较少的情况下额外的内存消耗不会太大。
struct test{
char c;
struct test* next;
};
其中的c只是测试用的,没什么含义。从结构体的定义可以看出点问题了。结构体分配内存的时候地址会对齐结构体里面成员字节数最大的那一个,也就是说,单链表节点的地址会四字节对齐,因此单链表节点地址的最后两位永远是0,也就是说next指针的最后两位肯定是0了, 这就给我们提供一个flag,只要遍历一个节点将next的最后一位设为1就ok啦。
下面看看程序吧
struct test* test_cycle(struct test* head){
unsigned long addr = 0;
struct test* prev;
while(head->next){
prev = head;
addr = (unsigned long)head->next;
if(addr & 0x01) //检测最后一位是否为1
return head;
head = head->next;
prev->next = (struct test*)(addr | 0x01); //最后一位设1
}
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
struct test t1 = {'a', 0};
struct test t2 = {'b', 0};
struct test t3 = {'c', 0};
struct test t4 = {'d', 0};
struct test t5 = {'e', 0};
t1.next = &t2;
t2.next = &t3;
t3.next = &t4;
t4.next = &t5;
t5.next = &t3;
struct test* t_joint = test_cycle(&t1);
if(t_joint)
printf("%c", t_joint->c);
return 0;
}
这个程序最后没有将所有next指针还原了,只要遍历一下将所有的next的最后一位重置为0就行了。