두 개의 연결 목록이 병합되는지 확인하십시오. 그렇다면 어디?


102

이 질문은 오래되었을 지 모르지만 대답을 생각할 수 없었습니다.

길이가 다른 두 개의 목록이 있으며 한 지점에서 병합됩니다 . 병합 지점이 어디에 있는지 어떻게 알 수 있습니까?

정황:

  1. 우리는 길이를 모릅니다
  2. 각 목록을 한 번만 구문 분석해야합니다.

두 개의 병합 된 연결 목록의 예.


병합은 그 시점에서 하나의 목록 만 있다는 것을 의미합니다.
rplusg

목록 수정이 허용됩니까?
Artelius

1
목록을 수정하지 않으면 작동하지 않는다고 확신합니다. (또는 한 번만 구문 분석 제한을 피하기 위해 다른 곳에 복사.)
게오르그 Schölly

2
요점 일 수도 있습니다. 젠장 면접관! Hehe
Kyle Rosendo

1
흥미로운 제안이 있습니다 ... 목록의 공통 꼬리가 무한히 길다고 가정합니다. 상수 메모리를 사용하여 노드 교차를 어떻게 찾을 수 있습니까?
Akusete

답변:


36

만약

  • "수정이 허용되지 않음"은 "변경할 수 있지만 결국 복원되어야 함"을 의미하며
  • 목록을 정확히 두 번 반복 할 수 있습니다.

다음 알고리즘이 해결책이 될 것입니다.

첫째, 숫자. 제리스트의 길이라고 가정 a+c하고, 두 번째의 길이이며 b+c, c(mergepoint 후) 공통의 "꼬리"의 길이이다. 다음과 같이 표시하겠습니다.

x = a+c
y = b+c

우리가 길이를 알 수 없기 때문에, 우리는 계산합니다 xy추가 반복없이; 당신은 방법을 볼 수 있습니다.

그런 다음 각 목록을 반복하고 반복하는 동안 되돌립니다! 두 반복자가 동시에 병합 지점에 도달하면 단순히 비교를 통해 알아냅니다. 그렇지 않으면 한 포인터가 다른 포인터보다 먼저 병합 지점에 도달합니다.

그 후 다른 반복기가 병합 지점에 도달하면 공통 꼬리로 진행되지 않습니다. 대신 이전에 병합 지점에 도달했던 목록의 이전 시작으로 돌아갑니다! 따라서 변경된 목록의 끝에 도달하기 전에 (즉, 다른 목록의 이전 시작 부분) 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

문제가 해결됩니다.


2
질문 상태에 대한 댓글 목록 수정 불가!
Skizz

1
나는이 대답을 좋아한다 (매우 창의적이다). 내가 가진 유일한 문제는 두 목록의 길이를 알고 있다고 가정한다는 것입니다.
tster 2010 년

당신은 목록을 수정할 수없고 우리는 길이를 모릅니다. 이것이 제약입니다 ... 어떻게 든 창의적인 대답에 감사드립니다.
rplusg

2
@tster, @calvin, 대답은 길이가 필요하다고 가정하지 않습니다. 인라인으로 계산할 수 있습니다. 내 답변에 설명 추가.
P Shved 2010 년

2
@Forethinker가 방문한 노드를 해싱하거나 표시된대로 표시하려면 O (목록 길이) 메모리가 필요하지만 많은 솔루션 (불완전하고 복잡하지만 내 것을 포함)에는 O (1) 메모리가 필요합니다.
P Shved 2013-07-23

156

다음은 내가 본 것 중 가장 큰 것입니다-O (N), 카운터 없음. VisionMap 에서 SN 후보와의 인터뷰에서 얻었습니다 .

다음과 같이 인터 레이팅 포인터를 만듭니다. 매번 끝까지 앞으로 이동 한 다음 반대 목록의 시작 부분으로 점프합니다. 두 개의 머리를 가리키는 두 개를 만듭니다. 만날 때까지 각 포인터를 매번 1 씩 전진시킵니다. 이것은 하나 또는 두 번의 패스 후에 발생합니다.

저는 인터뷰에서 여전히이 질문을 사용합니다.하지만 누군가가이 솔루션이 작동하는 이유를 이해하는 데 얼마나 오래 걸리는지 확인합니다.


6
정말 훌륭합니다!
Cong Hui

2
이것은 좋은 대답이지만 조건 # 2를 위반하는 목록을 두 번 확인해야합니다.
tster 2014 년

2
병합 지점이 보장되는 경우이 솔루션은 매우 우아합니다. 병합 지점을 감지하는 것은 작동하지 않습니다. 마치 하나가없는 것처럼 무한 반복됩니다.
교대 방향

4
정말 훌륭합니다! 설명 : 두 개의 목록이 있습니다 : a-b-c-x-y-zp-q-x-y-z. 제 포인터의 경로 a,b,c,x,y,z,p,q,x, 제 포인터의 경로p,q,x,y,z,a,b,c,x
니콜라이 Golub

14
훌륭한. 이해하지 못한 사람들을 위해 head1-> tail1-> head2-> 교차점 및 head2-> tail2-> head1-> 교차점에서 이동 한 노드 수를 세십시오. 둘 다 동일합니다 (이를 확인하기 위해 연결 목록의 diff 유형을 그립니다). 이유는 두 포인터가 다시 IP에 도달하기 전에 같은 거리를 head1-> IP + head2-> IP로 이동해야하기 때문입니다. 따라서 IP에 도달 할 때까지 두 포인터가 같고 병합 지점이 생깁니다.
adev jul.

91

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

이것은 내 다른 답변과 점근 적으로 동일하지만 (선형 시간) 아마도 더 작은 상수를 가지고 있으므로 아마도 더 빠를 것입니다. 그러나 내 다른 대답은 더 멋지다고 생각합니다.


4
오늘 우리가 보드카를 마시고있을 때이 질문을 제 친구에게 제안했고, 그는 당신과 같은 대답을주고 SO에 게시하도록 요청했습니다. 하지만 당신이 첫 번째 인 것 같습니다. 그래서 나는 당신을 위해 +1을 할 것입니다. 또 다른 +1을 할 수 있기를 바랍니다.
P Shved 2009

2
+1은 이와 같이 목록을 수정할 필요가 없습니다. 또한 대부분의 연결 목록 구현은 일반적으로 길이를 제공합니다
keshav84

3
파벨이 너무 많습니다. 내 솔루션은 목록을 수정할 필요가 없습니다.
Pavel Radzivilovsky

좋은 대답입니다. 이것에 대한 시간 복잡성은 무엇입니까? 0 (n + m)? 여기서 n = 목록 1의 노드, m = 목록 2의 노드?
Vihaan Verma

두 목록에서 두 포인터를 모두 이동하는 대신 : diff> = 두 경로 중 작은 지 여부를 확인할 수 있습니다. 그렇다면 작은 목록에서 작은 값으로 이동하고 그렇지 않으면 작은 목록에서 diff + 1 값으로 이동합니다. diff가 0이면 마지막 노드가 답입니다.
Vishal Anand

30

글쎄, 그들이 병합된다는 것을 안다면 :

다음으로 시작한다고 가정 해 보겠습니다.

A-->B-->C
        |
        V
1-->2-->3-->4-->5

1) 각 다음 포인터를 NULL로 설정하는 첫 번째 목록을 살펴보십시오.

이제 다음이 있습니다.

A   B   C

1-->2-->3   4   5

2) 이제 두 번째 목록을 살펴보고 병합 지점 인 NULL이 표시 될 때까지 기다립니다.

그들이 병합되는지 확신 할 수 없다면 포인터 값에 센티넬 값을 사용할 수 있지만 그렇게 우아하지는 않습니다.


3
그러나, 당신은 그 과정에서 목록을 파괴하고 다시는 사용되지 않습니다. : P
Kyle Rosendo

@Kyle Rozendo, 내 솔루션은 처리 후 복원 할 수있는 방식으로 목록을 변경합니다. 그러나 이것은 개념의 더 명확한 시연입니다
P Shved

나는 목록 수정이 허용되지 않는다는 것을 보지 못했습니다. 생각해 보겠습니다.하지만 본 모든 노드를 저장하지 않고서는 아무것도 떠오르지 않습니다.
tster 2010 년

10
어서, 정답입니다! 질문을 조정하면됩니다. :)
P Shved

23
메모리 누수를 생성하는 탁월한 알고리즘.
Karoly Horvath 2013

14

목록을 정확히 두 번 반복 할 수 있다면 병합 지점을 결정하는 방법을 제공 할 수 있습니다.

  • 두 목록을 반복하고 길이 A와 B를 계산합니다.
  • 길이 차이 계산 C = | AB |;
  • 두 목록을 동시에 반복하기 시작하지만 더 큰 목록에 추가 C 단계를 만듭니다.
  • 이 두 포인터는 병합 지점에서 서로 만날 것입니다.

8

다음은 계산 속도가 빠르지 만 (각 목록을 한 번 반복) 많은 메모리를 사용하는 솔루션입니다.

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

2
이는 목록을 두 번 처리하는 것과 같습니다.
Georg Schölly

기술적으로는 목록으로 작업을 두 번 수행한다고 가정하지만 Kyle Rozendo의 솔루션에 대한 상당한 개선입니다. 이제 '목록 처리'가 '링크 값을 읽고 포인터를 따라가는'것으로 정의되면 목록을 한 번 처리한다고 주장 할 수 있습니다. 각 링크 값을 한 번 읽고 저장 한 다음 비교합니다.
Skizz

분명히 내 것보다 빠를 것입니다.
Kyle Rosendo

7

노드 세트를 사용할 수 있습니다. 하나의 목록을 반복하고 각 노드를 세트에 추가하십시오. 그런 다음 두 번째 목록을 반복하고 모든 반복에 대해 노드가 세트에 있는지 확인합니다. 그렇다면 병합 지점을 찾은 것입니다. :)


나는 (Ω (n) 추가 공간 때문에) 이것이 유일한 접근법 (목록을 다시 작성하지 않고) 목록을 두 번 이상 구문 분석하지 않는 것입니다. 목록에서 루프를 감지하는 것은 첫 번째 목록에서 사소한 일입니다 (설정된 노드 확인). 두 번째 목록에서 루프 감지 방법을 사용하여 종료를 확인하십시오. (인터뷰 질문 문제 진술을 주의 깊게 듣고 망치를 사용하여 못을
박지

6

이것은 틀림없이 "각 목록을 한 번만 구문 분석"조건을 위반하지만 거북이와 토끼 알고리즘 (순환 목록의 병합 지점 및주기 길이를 찾는 데 사용됨)을 구현하여 목록 A에서 시작하고 목록에서 NULL에 도달하면 끝은 목록 B의 시작에 대한 포인터 인 척하여 순환 목록의 모양을 만듭니다. 그런 다음 알고리즘은 병합이 List A에서 얼마나 아래에 있는지 정확하게 알려줍니다 (Wikipedia 설명에 따라 변수 'mu').

또한 "lambda"값은 목록 B의 길이를 알려줍니다. 원하는 경우 알고리즘 중에 목록 A의 길이를 계산할 수 있습니다 (NULL 링크를 리디렉션 할 때).


내가 말한 것, 그냥 더 멋진 이름으로. : P
Kyle Rosendo

전혀. 이 솔루션은 연산에서 O (n)이고 메모리 사용량에서 O (1)입니다 (사실 두 개의 포인터 변수 만 필요함).
Artelius

네, 내 솔루션이 약간 변경되었으므로 이전 댓글을 삭제해야했습니다. 헤헤.
Kyle Rosendo

하지만 처음에는 어떻게 적용 할 수 있었는지 모르겠어요?
Artelius

당신의 설명은 알고리즘 자체가 아닙니다. 아마도 나는 그것을 다르게 보지만 헤이.
Kyle Rosendo

3

아마도 이것을 단순화하고 있지만 단순히 가장 작은 목록을 반복하고 마지막 노드 Link를 병합 지점으로 사용합니까?

따라서 병합 지점 (목록의 끝)으로 Data->Link->Link == NULL제공 Data->Link되는 끝 지점은 어디에 있습니까 ?

편집하다:

좋아요, 게시 한 사진에서 두 목록을 가장 작은 목록부터 구문 분석합니다. 가장 작은 목록으로 다음 노드에 대한 참조를 유지할 수 있습니다. 이제 두 번째 목록을 구문 분석 할 때 참조를 비교하여 참조 [i]가 LinkedList [i]-> Link에서 참조 인 위치를 찾습니다. 이것은 병합 지점을 제공합니다. 그림으로 설명 할 시간입니다 (그림의 값을 OP에 겹쳐 놓음).

연결 목록이 있습니다 (아래 참조).

A->B->C->D->E

두 번째 연결 목록이 있습니다.

1->2->

병합 된 목록을 사용하면 참조는 다음과 같이 이동합니다.

1->2->D->E->

따라서 첫 번째 "작은"목록을 매핑합니다 (우리가 계산하는 병합 된 목록의 길이는 4이고 기본 목록은 5).

첫 번째 목록을 반복하고 참조 참조를 유지합니다.

목록에는 다음 참조가 포함됩니다 Pointers { 1, 2, D, E }.

이제 두 번째 목록을 살펴 보겠습니다.

-> A - Contains reference in Pointers? No, move on
-> B - Contains reference in Pointers? No, move on
-> C - Contains reference in Pointers? No, move on
-> D - Contains reference in Pointers? Yes, merge point found, break.

물론, 당신은 새로운 포인터 목록을 유지하지만 그것은 사양을 벗어나지 않습니다. 그러나 첫 번째 목록은 정확히 한 번만 구문 분석되고 두 번째 목록은 병합 지점이없는 경우에만 완전히 구문 분석됩니다. 그렇지 않으면 더 빨리 종료됩니다 (병합 지점에서).


내가 처음에 말하고 싶었던 것과 약간 다르지만 OP가 원하는 것에서 이것은 트릭을 할 것입니다.
Kyle Rosendo

이제 더 명확 해졌습니다. 그러나 메모리 사용은 선형 적입니다. 나는 그것을 좋아하지 않는다.
Artelius

질문은 더 많은 것을 요구하지 않았습니다. 그렇지 않으면 전체 프로세스가 다중 스레드 될 수 있습니다. 이것은 여전히 ​​솔루션의 단순한 "최상위"보기이며 코드는 여러 가지 방법으로 구현할 수 있습니다. :)
Kyle Rosendo

1
어, 뭐? 멀티 스레딩은 알고리즘에 필요한 총 처리 능력을 줄이는 것이 아니라 처리 능력을 더 잘 활용하는 방법입니다. 그리고 코드를 여러 가지 방법으로 구현할 수 있다는 것은 변명 일뿐입니다.
Artelius

1
이것은 실제로 '각 목록을 한 번만 구문 분석'을 거의 중단 점으로 굽 힙니다. 당신이하는 일은 하나의 목록을 복사 한 다음 다른 목록을 복사본과 비교하는 것입니다.
Skizz

3

내 FC9 x86_64에서 병합 케이스를 테스트하고 아래와 같이 모든 노드 주소를 인쇄했습니다.

Head A 0x7fffb2f3c4b0
0x214f010
0x214f030
0x214f050
0x214f070
0x214f090
0x214f0f0
0x214f110
0x214f130
0x214f150
0x214f170


Head B 0x7fffb2f3c4a0
0x214f0b0
0x214f0d0
0x214f0f0
0x214f110
0x214f130
0x214f150
0x214f170

노드 구조를 정렬 했으므로 malloc ()이 노드이면 주소가 16 바이트로 정렬되므로 최소 4 비트를 참조하십시오. 최소 비트는 0, 즉 0x0 또는 000b입니다. 따라서 동일한 특수 사례 (정렬 된 노드 주소)에있는 경우에도 최소 4 비트를 사용할 수 있습니다. 예를 들어 두 목록을 머리에서 꼬리로 이동할 때 방문 노드 주소의 4 비트 중 1 또는 2를 설정합니다. 즉, 플래그를 설정합니다.

next_node = node->next;
node = (struct node*)((unsigned long)node | 0x1UL);

위의 플래그는 실제 노드 주소에 영향을주지 않고 SAVED 노드 포인터 값에만 영향을 미칩니다.

누군가 플래그 비트를 설정 한 것을 발견하면 처음 발견 된 노드가 병합 지점이어야합니다. 완료 한 후 설정 한 플래그 비트를 지워 노드 주소를 복원합니다. 중요한 것은 청소를 위해 반복 (예 : node = node-> next) 할 때주의해야한다는 것입니다. 플래그 비트를 설정했음을 기억하십시오.

real_node = (struct node*)((unsigned long)node) & ~0x1UL);
real_node = real_node->next;
node = real_node;

이 제안은 수정 된 노드 주소를 복원하기 때문에 "수정 없음"으로 간주 될 수 있습니다.


+1, 이것은 "한 번만 반복"으로 자연스럽게 떠오르는 것입니다. 왜 이것이 투표를하지 않았는지 모르겠습니다! 아름다운 솔루션.
jman

3

간단한 해결책이있을 수 있지만 보조 공간이 필요합니다. 아이디어는 목록을 순회하고 각 주소를 해시 맵에 저장하고 이제 다른 목록을 순회하고 주소가 해시 맵에 있는지 여부를 일치시키는 것입니다. 각 목록은 한 번만 순회됩니다. 목록에 수정 사항이 없습니다. 길이는 아직 알 수 없습니다. 사용 된 보조 공간 : O (n) 여기서 'n'은 순회 된 첫 번째 목록의 길이입니다.


2

이 솔루션은 각 목록을 한 번만 반복합니다 ... 목록 수정도 필요하지 않습니다. 공간에 대해 불평 할 수 있습니다.
1) 기본적으로 list1에서 반복하고 각 노드의 주소를 배열 (부호없는 정수 값을 저장하는)에 저장합니다.
2) 그런 다음 list2를 반복하고 각 노드의 주소에 대해 ---> 일치 항목을 찾거나 찾지 못하는 배열을 검색합니다. 그러면 병합 노드입니다.

//pseudocode
//for the first list
p1=list1;
unsigned int addr[];//to store addresses
i=0;
while(p1!=null){
  addr[i]=&p1;
  p1=p1->next;
}
int len=sizeof(addr)/sizeof(int);//calculates length of array addr
//for the second list
p2=list2;
while(p2!=null){
  if(search(addr[],len,&p2)==1)//match found
  {
    //this is the merging node
    return (p2);
  }
  p2=p2->next;
}

int search(addr,len,p2){
  i=0;  
  while(i<len){
    if(addr[i]==p2)
      return 1;
    i++;
  }
 return 0;
} 

유효한 해결책 이길 바랍니다 ...


이것은 목록 자체가 아닌 배열 형태이지만 목록 중 하나를 두 번 이상 반복합니다.
syockit

1

목록을 수정할 필요가 없습니다. 각 목록을 한 번만 탐색하면되는 솔루션이 있습니다.

  1. 두 개의 스택을 생성합니다 (예 : stck1 및 stck2).
  2. 첫 번째 목록을 탐색하고 stck1에서 탐색하는 각 노드의 복사본을 푸시합니다.
  3. 2 단계와 동일하지만 이번에는 두 번째 목록을 탐색하고 stck2의 노드 사본을 푸시합니다.
  4. 이제 두 스택에서 팝하고 두 노드가 동일한 지 확인한 다음 해당 노드에 대한 참조를 유지합니다. 그렇지 않다면 동일한 이전 노드가 실제로 우리가 찾고 있던 병합 지점입니다.

1
int FindMergeNode(Node headA, Node headB) {
  Node currentA = headA;
  Node currentB = headB;

  // Do till the two nodes are the same
  while (currentA != currentB) {
    // If you reached the end of one list start at the beginning of the other
    // one currentA
    if (currentA.next == null) {
      currentA = headA;
    } else {
      currentA = currentA.next;
    }
    // currentB
    if (currentB.next == null) {
      currentB = headB;
    } else {
      currentB = currentB.next;
    }
  }
  return currentB.data;
}


0

여기 순진한 해결책이 있습니다. 전체 목록을 탐색 할 필요가 없습니다.

구조화 된 노드에 다음과 같은 세 개의 필드가있는 경우

struct node {
    int data;   
    int flag;  //initially set the flag to zero  for all nodes
    struct node *next;
};

두 목록의 헤드를 가리키는 두 개의 헤드 (head1 및 head2)가 있다고 가정합니다.

두 목록을 동일한 속도로 탐색하고 해당 노드에 대해 = 1 (visited flag) 플래그를 설정합니다.

  if (node->next->field==1)//possibly longer list will have this opportunity
      //this will be your required node. 

0

이건 어때요:

  1. 각 목록을 한 번만 탐색 할 수있는 경우 새 노드를 만들고 첫 번째 목록을 탐색하여 모든 노드가이 새 노드를 가리 키도록하고 두 번째 목록을 탐색하여 새 노드를 가리키는 노드가 있는지 확인할 수 있습니다 ( 그것이 당신의 병합 지점입니다). 두 번째 순회가 새 노드로 이어지지 않으면 원래 목록에 병합 지점이없는 것입니다.

  2. 목록을 두 번 이상 탐색 할 수있는 경우 각 목록을 탐색하여 길이를 찾을 수 있으며 다른 경우 더 긴 목록의 시작 부분에있는 "추가"노드를 생략하십시오. 그런 다음 두 목록을 한 번에 한 단계 씩 순회하고 첫 번째 병합 노드를 찾으십시오.


1. 첫 번째 목록을 수정할뿐만 아니라 파괴합니다. 2. 몇 번이고 제안됩니다.
수염이 희끗 희끗 한

0

Java의 단계 :

  1. 지도를 만듭니다.
  2. 목록의 두 가지 분기에서 순회를 시작하고 노드 (예 : 노드 Id)와 관련된 고유 한 것을 사용하여 목록의 모든 순회 된 노드를 맵에 넣고 모두 시작에 값을 1로 입력합니다.
  3. 첫 번째 중복 키가 올 때마다 해당 키의 값을 증가시킵니다 (이제 값이> 1 인 2가되었다고 가정 해 보겠습니다.
  4. 값이 1보다 크고 두 목록이 병합되는 노드 여야하는 키를 가져옵니다.

1
합쳐진 부분에 사이클이 있다면?
Rohit

그러나 오류 처리 사이클의 경우 isyi의 대답 과 매우 유사 합니다.
greybeard

0

"isVisited"필드를 도입하여 효율적으로 해결할 수 있습니다. 첫 번째 목록을 탐색하고 끝까지 모든 노드에 대해 "isVisited"값을 "true"로 설정합니다. 이제 두 번째부터 시작하여 플래그가 true이고 Boom 인 첫 번째 노드를 찾으십시오.


0

1 단계 : 두 목록의 길이 찾기 2 단계 : 차이점을 찾고 차이가있는 가장 큰 목록을 이동합니다. 3 단계 : 이제 두 목록이 비슷한 위치에 있습니다. 4 단계 : 목록을 반복하여 병합 지점을 찾습니다.

//Psuedocode
def findmergepoint(list1, list2):
lendiff = list1.length() > list2.length() : list1.length() - list2.length() ? list2.lenght()-list1.lenght()
biggerlist = list1.length() > list2.length() : list1 ? list2  # list with biggest length
smallerlist = list1.length() < list2.length() : list2 ? list1 # list with smallest length


# move the biggest length to the diff position to level both the list at the same position
for i in range(0,lendiff-1):
    biggerlist = biggerlist.next
#Looped only once.  
while ( biggerlist is not None and smallerlist is not None ):
    if biggerlist == smallerlist :
        return biggerlist #point of intersection


return None // No intersection found

(각 항목이 한 줄로 시작하는 목록이 더 마음에 들었습니다. 맞춤법 검사기를 사용해보십시오.)
greybeard

0
int FindMergeNode(Node *headA, Node *headB)
{
    Node *tempB=new Node;
    tempB=headB;
   while(headA->next!=NULL)
       {
       while(tempB->next!=NULL)
           {
           if(tempB==headA)
               return tempB->data;
           tempB=tempB->next;
       }
       headA=headA->next;
       tempB=headB;
   }
    return headA->data;
}

답변에 몇 가지 설명을 추가해야합니다. 코드 전용 답변이 삭제 될 수 있습니다.
rghome

0

맵 또는 사전을 사용하여 노드의 주소 대 값을 저장하십시오. 지도 / 사전에 이미 주소가 있으면 키의 값이 답입니다. 나는 이걸했다:

int FindMergeNode(Node headA, Node headB) {

Map<Object, Integer> map = new HashMap<Object, Integer>();

while(headA != null || headB != null)
{
    if(headA != null && map.containsKey(headA.next))
    {
        return map.get(headA.next);
    }

    if(headA != null && headA.next != null)
    {
         map.put(headA.next, headA.next.data);
         headA = headA.next;
    }

    if(headB != null && map.containsKey(headB.next))
    {
        return map.get(headB.next);
    }

    if(headB != null && headB.next != null)
    {
        map.put(headB.next, headB.next.data);
        headB = headB.next;
    }
}

return 0;
}

0

AO (n) 복잡성 솔루션. 그러나 가정에 근거합니다.

가정은 다음과 같습니다. 두 노드 모두 양의 정수만 있습니다.

논리 : list1의 모든 정수를 음수로 만듭니다. 그런 다음 음의 정수를 얻을 때까지 list2를 살펴보십시오. 일단 발견되면 => 가져 가면 기호를 다시 양수로 변경하고 돌아갑니다.

static int findMergeNode(SinglyLinkedListNode head1, SinglyLinkedListNode head2) {

    SinglyLinkedListNode current = head1; //head1 is give to be not null.

    //mark all head1 nodes as negative
    while(true){
        current.data = -current.data;
        current = current.next;
        if(current==null) break;
    }

    current=head2; //given as not null
    while(true){
        if(current.data<0) return -current.data;
        current = current.next;
    }

}

0

두 개의 포인터를 사용하고 포인터 중 하나가 null이면 다른 목록의 헤드를 가리키고 다른 목록의 헤드를 가리 키도록하는 방식으로 이동할 수 있습니다. 이렇게하면 목록 길이가 다르면 두 번째 패스에서 만날 것입니다. . list1의 길이가 n이고 list2가 m이면 차이는 d = abs (nm)입니다. 그들은이 거리를 커버하고 병합 지점에서 만날 것입니다.
암호:

int findMergeNode(SinglyLinkedListNode* head1, SinglyLinkedListNode* head2) {
    SinglyLinkedListNode* start1=head1;
    SinglyLinkedListNode* start2=head2;
    while (start1!=start2){
        start1=start1->next;
        start2=start2->next;
        if (!start1)
        start1=head2;
        if (!start2)
        start2=head1;
    }
    return start1->data;
}

0

의 노드를 list1해시 세트에 추가하고 두 번째를 통해 루프를 추가 할 수 있으며의 노드 list2가 이미 세트에있는 경우 해당 노드 가 병합됩니다.

static int findMergeNode(SinglyLinkedListNode head1, SinglyLinkedListNode head2) {
    HashSet<SinglyLinkedListNode> set=new HashSet<SinglyLinkedListNode>();
    while(head1!=null)
    {
        set.add(head1);
        head1=head1.next;
    }
    while(head2!=null){
        if(set.contains(head2){
            return head2.data;
        }
    }
    return -1;
}

0

자바 스크립트를 사용한 솔루션

var getIntersectionNode = function(headA, headB) {
    
    if(headA == null || headB == null) return null;
    
    let countA = listCount(headA);
    let countB = listCount(headB);
    
    let diff = 0;
    if(countA > countB) {

        diff = countA - countB;
        for(let i = 0; i < diff; i++) {
            headA = headA.next;
        }
    } else if(countA < countB) {
        diff = countB - countA;
        for(let i = 0; i < diff; i++) {
            headB = headB.next;
        }
    }

    return getIntersectValue(headA, headB);
};

function listCount(head) {
    let count = 0;
    while(head) {
        count++;
        head = head.next;
    }
    return count;
}

function getIntersectValue(headA, headB) {
    while(headA && headB) {
        if(headA === headB) {
            return headA;
        }
        headA = headA.next;
        headB = headB.next;
    }
    return null;
}

0

연결 목록 편집이 허용되면

  1. 그런 다음 목록 2의 모든 노드의 다음 노드 포인터를 null로 만듭니다.
  2. 목록 1의 마지막 노드의 데이터 값을 찾으십시오. 이것은 "고음질 논리 없음"으로 두 목록의 단일 순회에서 교차 노드를 제공합니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.