[algorithm] 두 개의 연결 목록이 병합되는지 확인하십시오. 그렇다면 어디?
이 질문은 오래되었을 지 모르지만 대답을 생각할 수 없었습니다.
길이가 다른 두 개의 목록이 있으며 한 지점에서 병합됩니다 . 병합 지점이 어디에 있는지 어떻게 알 수 있습니까?
정황:
- 우리는 길이를 모릅니다
- 각 목록을 한 번만 구문 분석해야합니다.
답변
만약
- “수정이 허용되지 않음”은 “변경할 수 있지만 결국 복원되어야 함”을 의미하며
- 목록을 정확히 두 번 반복 할 수 있습니다.
다음 알고리즘이 해결책이 될 것입니다.
첫째, 숫자. 제리스트의 길이라고 가정 a+c
하고, 두 번째의 길이이며 b+c
, c
(mergepoint 후) 공통의 “꼬리”의 길이이다. 다음과 같이 표시하겠습니다.
x = a+c
y = b+c
우리가 길이를 알 수 없기 때문에, 우리는 계산합니다 x
및 y
추가 반복없이; 당신은 방법을 볼 수 있습니다.
그런 다음 각 목록을 반복하고 반복하는 동안 되돌립니다! 두 반복자가 동시에 병합 지점에 도달하면 단순히 비교를 통해 알아냅니다. 그렇지 않으면 한 포인터가 다른 포인터보다 먼저 병합 지점에 도달합니다.
그 후 다른 반복기가 병합 지점에 도달하면 공통 꼬리로 진행되지 않습니다. 대신 이전에 병합 지점에 도달했던 목록의 이전 시작으로 돌아갑니다! 따라서 변경된 목록의 끝에 도달하기 전에 (즉, 다른 목록의 이전 시작 부분) a+b+1
반복을 합산합니다. 그것을라고 부르 자 z+1
.
병합 지점에 먼저 도달 한 포인터는 목록 끝에 도달 할 때까지 계속 반복됩니다. 반복 횟수를 계산해야하며과 같습니다 x
.
그런 다음이 포인터는 역순으로 목록을 다시 반전합니다. 그러나 이제는 원래 시작된 목록의 처음으로 돌아 가지 않습니다! 대신 다른 목록의 처음으로 이동합니다! 반복 횟수를 계산하고 y
.
따라서 우리는 다음 숫자를 알고 있습니다.
x = a+c
y = b+c
z = a+b
우리가 결정하는 것으로부터
a = (+x-y+z)/2
b = (-x+y+z)/2
c = (+x+y-z)/2
문제가 해결됩니다.
답변
다음은 내가 본 것 중 가장 큰 것입니다-O (N), 카운터 없음. VisionMap 에서 SN 후보와의 인터뷰에서 얻었습니다 .
다음과 같이 인터 레이팅 포인터를 만듭니다. 매번 끝까지 앞으로 이동 한 다음 반대 목록의 시작 부분으로 점프합니다. 두 개의 머리를 가리키는 두 개를 만듭니다. 만날 때까지 각 포인터를 매번 1 씩 전진시킵니다. 이것은 하나 또는 두 번의 패스 후에 발생합니다.
저는 인터뷰에서 여전히이 질문을 사용합니다.하지만 누군가가이 솔루션이 작동하는 이유를 이해하는 데 얼마나 오래 걸리는지 확인합니다.
답변
Pavel의 대답은 목록 을 수정하고 각 목록을 두 번 반복해야합니다.
여기에 각 목록을 두 번만 반복해야하는 솔루션이 있습니다 (길이를 처음 계산할 때; 길이가 주어지면 한 번만 반복하면 됨).
아이디어는 더 긴 목록 (병합 지점이있을 수 없음)의 시작 항목을 무시하여 두 포인터가 목록 끝에서 같은 거리에 있도록하는 것입니다. 그런 다음 병합 될 때까지 앞으로 이동합니다.
lenA = count(listA) //iterates list A
lenB = count(listB) //iterates list B
ptrA = listA
ptrB = listB
//now we adjust either ptrA or ptrB so that they are equally far from the end
while(lenA > lenB):
ptrA = ptrA->next
lenA--
while(lenB > lenA):
prtB = ptrB->next
lenB--
while(ptrA != NULL):
if (ptrA == ptrB):
return ptrA //found merge point
ptrA = ptrA->next
ptrB = ptrB->next
이것은 내 다른 답변과 점근 적으로 동일하지만 (선형 시간) 아마도 더 작은 상수를 가지고 있으므로 아마도 더 빠를 것입니다. 그러나 내 다른 대답은 더 멋지다고 생각합니다.
답변
글쎄, 그들이 병합된다는 것을 안다면 :
다음으로 시작한다고 가정 해 보겠습니다.
A-->B-->C
|
V
1-->2-->3-->4-->5
1) 각 다음 포인터를 NULL로 설정하는 첫 번째 목록을 살펴보십시오.
이제 다음이 있습니다.
A B C
1-->2-->3 4 5
2) 이제 두 번째 목록을 살펴보고 병합 지점 인 NULL이 표시 될 때까지 기다립니다.
그들이 병합되는지 확신 할 수 없다면 포인터 값에 센티넬 값을 사용할 수 있지만 그렇게 우아하지는 않습니다.
답변
목록을 정확히 두 번 반복 할 수 있다면 병합 지점을 결정하는 방법을 제공 할 수 있습니다.
- 두 목록을 반복하고 길이 A와 B를 계산합니다.
- 길이 차이 계산 C = | AB |;
- 두 목록을 동시에 반복하기 시작하지만 더 큰 목록에 추가 C 단계를 만듭니다.
- 이 두 포인터는 병합 지점에서 서로 만날 것입니다.
답변
다음은 계산 속도가 빠르지 만 (각 목록을 한 번 반복) 많은 메모리를 사용하는 솔루션입니다.
for each item in list a
push pointer to item onto stack_a
for each item in list b
push pointer to item onto stack_b
while (stack_a top == stack_b top) // where top is the item to be popped next
pop stack_a
pop stack_b
// values at the top of each stack are the items prior to the merged item
답변
노드 세트를 사용할 수 있습니다. 하나의 목록을 반복하고 각 노드를 세트에 추가하십시오. 그런 다음 두 번째 목록을 반복하고 모든 반복에 대해 노드가 세트에 있는지 확인합니다. 그렇다면 병합 지점을 찾은 것입니다. 🙂