1. 力扣141判断是否存在环
双指针,相当于跑步,一个跑的快A一个跑的慢B,当有环(环周长Q)的时候,肯定快的能再遇到慢的人,此时A比B多跑1-N圈的时候都能互相遇到,所以下面的算法很简单
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head ==null || head.next ==null){
return false ;
}
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
}
2. 判断环的入口
这个很难,先看图,就会发现规律:
起点为O,入环口为N,相遇点为:M
O到N的距离为a,N到M的距离为b,环的长度为Q
A、B两个人到达相遇点走过的距离分别是:
A: a+b
//B和A相遇的时候,B可能已经走了N圈,因为可能A很长,但是环很小
B: a+NQ+b
因为B的速度是A的两倍,相同的时间走的距离当然也是A的两倍,所以:
2(a+b)=a+NQ+b
所以:
a+b=NQ
所以
a=NQ-b
2.1 高能预警
a=NQ-b
你有没有发现什么?
- 或者举例:当N=1的时候,
a=Q-b
正是我们要找的环的入口,也就是如果现在能让A从起点O触发速度不变,B速度降低为原来的一半也就是A的速度,这个时候,大家以相同的速度走相同的时间,都能到达环的入口N点,我们就求出来了环的入口 - 或者上面还是不是很理解,我们假定N=6,也就是a的距离是6圈减去b的长度,这个时候还让A回到起点相同速度去找B,B以原来速度的一半在这个环上转圈,当它转到第6圈也就是走的长度为6Q-b的时候,他会发现A,为什么?
- 因为6Q-b=a,所以只要把A弄回起点,同时AB的速度保持一致,A去找B ,B在环上绕圈,当A到达环的入口的时候,AB必然会相遇
3. 解题
我们先判断是否有环,如果有环,那我们定义一个q=head
相当于把q放回了起点,同时,我们判断有环的时候,相交点是p或者p2
我们让q和p都同时继续走下去,相交的时候就是入口的位置
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) {
return null;
}
ListNode p = head, p2 = head;
boolean hasCycle = false;
while (p2.next != null && p2.next.next != null) {
p = p.next;
p2 = p2.next.next;
if (p == p2) {
hasCycle = true;
break;
}
}
if (hasCycle) {
ListNode q = head;
while (p != q) {
p = p.next;
q = q.next;
}
return q;
} else {
return null;
}
}
}
4. 寻找重复数
4.1 题目
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),
可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
说明:
不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。
时间复杂度小于 O(n2) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。
4.2 把数组转化成链表
注意两点,数组大小N+1
,每个数都在1-N之间
说明了什么,必有一个数是重复的
所以下面这种初始化数组的方式肯定不对
int arr []=new int[]{1,2,345,54,452,343,23};
比如我们new一个满足条件的数组并进行初始化
int arr []=new int[]{2,7,5,3,0,1,2,4,3,8};
得到了下面的数据
类型 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
下标: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
值: | 2 | 7 | 5 | 3 | 0 | 1 | 2 | 4 | 3 | 8 |
所以我们用下标组成链表的话,肯定是有环的,不信我来试一下
刚开始用下标0来: 251740 251740
我们就发现了环的入口是2
我们的解决算法:
public int findDuplicate(int[] nums) {
int i = 0;
//temp可以初始化为任何值
int temp = -100;
while (true) {
if (nums[i] == -1) {
//这次访问的元素之前那访问过,返回这次的 下标,
//如果nums[i]==-1说明这个元素之前访问过,直接返回即可
return temp;
}
temp = nums[i];
//这里的值要和if条件里面的值保持一致
nums[i] = -1;
i = temp;
}
}