问题三—环问题
环问题
环问题的数据结构描述就是,在一个单向链表中存在多个节点的尾指针指向同一个非null的节点,对于环问题我们主要面临的问题有二。
1.是否有环
判断是否有环的方法有很多这里给出几种解法的Java实现代码
方法一:基本蛮力求解
正常思路如下,从第一个节点开始,令其为当前节点,然后看看其他节点的后继指针是否指向当前节点,如果存在这样的节点,那么就可以判断链表中存在环,否则,对链表中其余节点重复上述过程。
致命缺点:无法确定尾节点,从而会形成死循环
方法二:散列表
public static boolean DoesLinkedListContainsLoop_01(ListNode head){
Set<ListNode> set = new HashSet<>();
ListNode node = head;
while (node!=null){
if (set.contains(node))
return true;
else
set.add(node);
node = node.getNext();
}
return false;
}
此处的思路是这个样子的:首先,我们要有散列表的基本概念,然后做如下的算法:
①首先建立一个散列表,在这里建立的是Hashset原因是这玩意能主动去重
②其次,对每一个节点进行遍历,看下我在散列表中是否找到了这个节点,如果有的话,则说明我们里面有环
③如果没有的话,则把这个元素加入到散列表中去
研究:Hashset中使用的散列函数是什么:???母鸡,以后研究
方法三 Floyd图算法
这个需要用到一点数论的知识,但是仍然很简单
public static boolean DoesLinkedListContainsLoop_02(ListNode head){
//if (head.ListLength()==1)return false;
ListNode slowptr = head.getNext();
ListNode fastptr = head.getNext().getNext();
if (slowptr==fastptr)return true;
while (slowptr!=fastptr){
if (slowptr==null||fastptr==null)return false;
slowptr=slowptr.getNext();
fastptr=fastptr.getNext().getNext();
}
return true;
}
这个地方的思路是,当一个链表如果有环的话,只要进入环中,两个指针就出不来了,因此,我们需要设置两个速度不同的指针进行操作,如果两者最终能够相遇,则说明有环的存在
2.环开始的位置
现在我们在这个基础上进行拓展:找到环开始的位置,同时优化我们的Print函数和GetLenth函数,那么环开始的位置如何确定呢?这里同样给出两个解决思路
方法一散列表
//method01--- hashtable
public static int WhereLinkedListLoop_01(ListNode head){
Set<ListNode> set = new HashSet<>();
ListNode currnode = head;
ListNode node = head;
ListNode node_01 = null;
int count = 1;
while (node!=null){
if (set.contains(node)){
node_01 = node;
break;
}
else
set.add(node);
node = node.getNext();
}
while (currnode!=node_01){
currnode = currnode.getNext();
count++;
}
return count;
}
基于散列表实现的节点位置查找,主要思路如下:
首先通过第一步查找确定是否有环,如果有环,记录下环开始的节点,然后从头开始遍历直到他们相等,时间复杂度为O(n),空间复杂度为O(m)。
方法二 Floyd算法
这家伙真是个神人…
算法的思路如下
//method02 --- Floyd
public static int WhereLinkedListLoop_02(ListNode head){
if (head==null)return 0;
ListNode slowptr = head;
ListNode fastptr = head;
slowptr=slowptr.getNext();
fastptr=fastptr.getNext().getNext();
if (slowptr==fastptr)return 2;
while (slowptr!=fastptr){
if (slowptr==null||fastptr==null)return 0;
slowptr=slowptr.getNext();
fastptr=fastptr.getNext().getNext();
}
slowptr = head;
int count = 1;
while (slowptr!=fastptr){
slowptr=slowptr.getNext();
fastptr = fastptr.getNext();
count++;
}
return count;
}
这个需要用到一点点数论的知识,我们首先看到
这里有几个限制条件:首先 slowptr的移动速度是1,然后fastptr的移动速度是2,因此当他们第一次相遇的时候此时fastptr移动的距离减去slowptr=nL 其中L为环的长度,n为圈数
且此时乌龟移动的距离一定是fastptr到表头的一半,因此此时,如果将slowptr放回头节点,则有由于此时slow和fastptr同样相差nL,此时,只要将fastptr的移动速度也改为1,则他们始终相差nl,因此只要是他进入环,则由于他们相差nL,因此一定回直接相遇,此时该节点就是他们环开始的地方

245

被折叠的 条评论
为什么被折叠?



