单链表是否有环的判断
首先,对于文章中给出的bisearch()程序有一点疑问,对于strcmp()函数调用的字符指针都不判断是否为空。显然,这个保证留给了该函数的调用者,本身也没什么问题。但是,我觉得在实际代码中,起码应该使用assert()来测试一下为好。但是考虑到字符指针数组其实在运行时可能经常变化,其实风险还是有的。其实这样的东西严格来讲的确不能算错误,但是却是真真正正的风险。
好了,言归正传。
给定一个单链表,只给出头指针h:
1、如何判断是否存在环?
2、如何知道环的长度?
3、如何找出环的连接点在哪里?
4、带环链表的长度是多少?
解法:
1、对于问题1,使用追赶的方法,设定两个指针slow、fast,从头指针开始,每次分别前进1步、2步。如存在环,则两者相遇;如不存在环,fast遇到NULL退出。
2、对于问题2,记录下问题1的碰撞点p,slow、fast从该点开始,再次碰撞所走过的操作数就是环的长度s。
3、问题3:有定理:碰撞点p到连接点的距离=头指针到连接点的距离,因此,分别从碰撞点、头指针开始走,相遇的那个点就是连接点。
该定理的证明可参考:http://fayaa.com/tiku/view/7/
4、问题3中已经求出连接点距离头指针的长度,加上问题2中求出的环的长度,二者之和就是带环单链表的长度
判断是否存在环的程序:
[cpp] view plaincopy
- bool IsExitsLoop(slist *head)
- {
- slist *slow = head, *fast = head;
- while ( fast && fast->next )
- {
- slow = slow->next;
- fast = fast->next->next;
- if ( slow == fast ) break; //二者指针相等时说明碰到了
- }
- return !(fast == NULL || fast->next == NULL);
- }
寻找环连接点(入口点)的程序:
[cpp] view plaincopy
- slist* FindLoopPort(slist *head)
- {
- slist *slow = head, *fast = head;
- while ( fast && fast->next )
- {
- slow = slow->next;
- fast = fast->next->next;
- if ( slow == fast ) break;
- }
- if (fast == NULL || fast->next == NULL)
- return NULL;
- slow = head; //把head给slow,
- while (slow != fast)
- {
- slow = slow->next;
- fast = fast->next; //碰撞点给fast,当二者不等时一直循环,相等时即到达连接点
- }
- return slow;
- }
1)判断一个单链表是否有环。如果有,把指向环开始的指针返回;如果没有,返回NULL。
LinkedList* IsCyclicLinkedList (LinkedList* pHead)
{
LinkedList *pCur;
LinkedList *pStart;
while (pCur != NULL)
{
for (;;)
{
if (pStart == pCur -> pNext)
return pStart;
pStart = pStart->pNext;
}
pCur = pCur->pNext;
}
}
我觉得这个程序挺麻烦的。按照这个程序的思路,最终可能陷入到那个环中无法脱身,而且没有办法探测出已经陷入环中了。我改的程序如下:
LinkedList* IsCyclicLinkedList(LinkedList* pHead)
{
if (pHead == NULL)
return NULL;
LinkedList *pStart = pHead->pNext;
vector<LinkedList*> v;
v.push_back(pHead);
for (; pStart != NULL; ) {
for (int i=0; i<v.size(); i++) {
if (pStart == v[i])
return pStart;
}
pStart = pStart->pNext
}
return NULL;
}
这个应该不是作者的原意吧?不知道大家有没有什么思路可以提供给我。实在想不出好的方法。以前听说过快慢指针的方法检测环,但是好像用在这里也没有特别好的思路。
字符串数组arr中值为字符串v的元素的序号,若存在多个符合条件的,返回序号最大的那个序号
如arr={“asdf”,”sad”,”sad”…..},v=”sad”,返回的序号就是2啦
- int bisearch(char** arr, int b, int e, char* v)
- {
- int minIndex = b, maxIndex = e, midIndex;
- while (minIndex < maxIndex - 1)
- //防止进入死循环,如min=2,max=3,mid=2,arr[mid]<v;判定条件是min<max时就会一直循环
- {
- midIndex = minIndex + (maxIndex - minIndex) / 2; //防止数据溢出
- if (strcmp(arr[midIndex], v) <= 0) //如果mid处的字符串《v就让mid成为序号最喜小的位置,因为只有在mid之后去查找符合条件的值,
- {
- minIndex = midIndex;
- }
- else //否则就在mid之前去查找可能的结果,mid-----max
- {
- maxIndex = midIndex;
- }
- }
- //先判断序号最大的值,因为要返回序号最大的值,要是返回序号最小的值,就先判断min
- if (!strcmp(arr[maxIndex], v)) //比较max处的值与v,max等于v返回等于0的值取反满足条件,得到最大的序号
- {
- return maxIndex;
- }
- else if (!strcmp(arr[minIndex], v)) //同理,
- {
- return minIndex;
- }
- else
- {
- return -1;
- }
- }
另外,扩展问题上的几个小问题:
给定一个有序(不降序)数组arr,求任意一个i使得arr[i]等于v,不存在则返回-1:
就用书上代码就可以了:
[c-sharp] view plaincopy
- int bisearch(char** arr, int b, int e, char* v)
- {
- int minIndex = b, maxIndex = e, midIndex;
- while (minIndex < maxIndex - 1)
- //防止进入死循环,如min=2,max=3,mid=2,arr[mid]<v;判定条件是min<max时就会一直循环
- {
- midIndex = minIndex + (maxIndex - minIndex) / 2; //防止数据溢出
- if (strcmp(arr[midIndex], v) <= 0)
- {
- minIndex = midIndex;
- }
- else
- {
- maxIndex = midIndex;
- }
- }
- if (!strcmp(arr[maxIndex], v))
- {
- return maxIndex;
- }
- else if (!strcmp(arr[minIndex], v))
- {
- return minIndex;
- }
- else
- {
- return -1;
- }
- }
给定一个有序(不降序)数组arr,求最小的i使得arr[i]等于v,不存在则返回-1:
[cpp] view plaincopy
- int bisearch(char** arr, int b, int e, char* v)
- {
- int minIndex = b, maxIndex = e, midIndex;
- while (minIndex < maxIndex - 1)
- {
- midIndex = minIndex + (maxIndex - minIndex) / 2;
- if (strcmp(arr[midIndex], v) < 0)
- {
- minIndex = midIndex;
- }
- else
- {
- maxIndex = midIndex;
- }
- }
- if (!strcmp(arr[minIndex], v))
- {
- return minIndex;
- }
- else if (!strcmp(arr[maxIndex], v))
- {
- return maxIndex;
- }
- else
- {
- return -1;
- }
- }
给定一个有序(不降序)数组arr,求最大的i使得arr[i]等于v,不存在则返回-1:
[cpp] view plaincopy
- int bisearch(char** arr, int b, int e, char* v)
- {
- int minIndex = b, maxIndex = e, midIndex;
- while (minIndex < maxIndex - 1)
- {
- midIndex = minIndex + (maxIndex - minIndex) / 2;
- if (strcmp(arr[midIndex], v) <= 0)
- {
- minIndex = midIndex;
- }
- else
- {
- maxIndex = midIndex;
- }
- }
- if (!strcmp(arr[maxIndex], v))
- {
- return maxIndex;
- }
- else if (!strcmp(arr[minIndex], v))
- {
- return minIndex;
- }
- else
- {
- return -1;
- }
- }
给定一个有序(不降序)数组arr,求最大的i使得arr[i]小于v,不存在则返回-1:
[cpp] view plaincopy
- int bisearch(char** arr, int b, int e, char* v)
- {
- int minIndex = b, maxIndex = e, midIndex;
- while (minIndex < maxIndex - 1)
- {
- midIndex = minIndex + (maxIndex - minIndex) / 2;
- if (strcmp(arr[midIndex], v) < 0)
- {
- minIndex = midIndex;
- }
- else
- {
- maxIndex = midIndex;
- }
- }
- if (strcmp(arr[maxIndex], v) < 0)
- {
- return maxIndex;
- }
- else if (strcmp(arr[minIndex], v) < 0)
- {
- return minIndex;
- }
- else
- {
- return -1;
- }
- }
给定一个有序(不降序)数组arr,求最大的i使得arr[i]大于v,不存在则返回-1:
[cpp] view plaincopy
- int bisearch(char** arr, int b, int e, char* v)
- {
- int minIndex = b, maxIndex = e, midIndex;
- while (minIndex < maxIndex - 1)
- {
- midIndex = minIndex + (maxIndex - minIndex) / 2;
- if (strcmp(arr[midIndex], v) <= 0)
- {
- minIndex = midIndex;
- }
- else
- {
- maxIndex = midIndex;
- }
- }
- if (strcmp(arr[minIndex], v) > 0)
- {
- return minIndex;
- }
- else if (strcmp(arr[maxIndex], v) > 0)
- {
- return maxIndex;
- }
- else
- {
- return -1;
- }
- }