연결 목록에 링이 있는지 확인하고 링 항목을 찾는 알고리즘
@author : Jingdai
@date : 2020.12.26
제목 설명
주어진 연결 목록에 링이 있는지 확인하고, 링이 있으면 링의 항목을 찾아 출력하고, 링이 없으면 출력합니다
null
.
아이디어와 코드
우선 첫 번째 질문은 연결 목록에 링이 있는지 여부를 판단하는 방법입니다. 빠르고 느린 포인터에 익숙한 아동용 부츠는 연결 목록에 링이 있는지 여부를 판단하는 것이 빠르고 느린 포인터로 해결할 수있는 일반적인 문제임을 알아야합니다.
우리는 두 개의 포인터, 하나의 느린 포인터 slowPointer
와 하나의 빠른 포인터를 사용 fastPointer
합니다. 모두 처음부터 시작하고 느린 포인터는 한 번에 한 걸음 씩, 빠른 포인터는 한 번에 두 걸음 씩 걷습니다. 링이 있으면 빠르기와 느린 포인터가 확실히 만나게됩니다.이를 기반으로 판단됩니다. 반지가 있는지 여부.
다음 질문은 반지의 입구를 찾는 방법입니다. 이 문제는 좀 더 복잡합니다. 아래 그림을보세요.
링의 바깥 쪽 길이는 l
이고 빨간색 점이 만남의 지점입니다. 회의, 느린 포인터가 사라 때 l + a
, 빠른 포인터가 사라 l + n (a + b) + a
은, n
회전 수, 그리고 빠른 포인터의 거리를 단순화하여 얻을 수 있습니다 느린 포인터의 두 배입니다 l = (n - 1) (a + b) + b
. 제공되는 링 둘레 c
를 얻을 수있다 l = (n - 1) c + b
, 즉 l
링과 회전 이내의 거리 n-1
링 걸어 b
동일한 거리. 이때 만남의 장소에서이 정도 먼 거리를 걸 으면 링 입구에 도착합니다. 그런 다음 처음부터 시작하도록 포인터를 설정하고 회의 지점에서 멀리 떨어진 다른 포인터를 시작할 수 있습니다. 만날 때, 사라진 경우 l
, 링 진입 점에서 만날 것입니다. 이에 따라 링 입구를 찾을 수 있습니다.
참고 :주의 깊은 사람들은 왜 링의 느린 포인터 a
가 원 안에 들어 가지 않는지 궁금해 할 수 있습니다 a
. 느린 포인터는 한 원의 빠른 포인터 만 만나기 때문입니다. 느린 포인터가 링에 들어갈 때 빠른 포인터와 느린 포인터 사이의 거리는 링의 둘레보다 작아야합니다. 여기서는 빠른 포인터와 느린 포인터 사이의 거리가 링의 둘레와 같다고 가정하여 제한을받습니다. (실제로 사용할 수 없음). 그러면 빠른 포인터와 느린 포인터가 링 입구에 있습니다. 슬로우 포인터가 한 원으로 가고 빠른 포인터가 두 개의 원으로 가면 링 입구에서 다시 만나게됩니다. 느린 포인터는 한 원만 이동하고 거리는 가장 큽니다. 값을 사용할 수 없으므로 느린 포인터는 한 원으로 이동하지 않으므로 느린 포인터는 한 원 내에서 빠른 포인터를 확실히 만나게됩니다.
최종 코드는 다음과 같습니다.
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) {
return null;
}
ListNode dummy = new ListNode();
dummy.next = head;
ListNode fastPointer = head.next;
ListNode slowPointer = head;
while (fastPointer != null && fastPointer.next != null) {
slowPointer = slowPointer.next;
fastPointer = fastPointer.next.next;
if (fastPointer == slowPointer) {
// hasCicle
slowPointer = dummy;
while (slowPointer != fastPointer) {
slowPointer = slowPointer.next;
fastPointer = fastPointer.next;
}
return slowPointer;
}
}
return null;
}