web-dev-qa-db-ja.com

2つのリンクリストがマージされるかどうかを確認します。もしそうなら、どこで?

この質問は古いかもしれませんが、答えを考えることができませんでした。

たとえば、長さの異なる2つのリスト、が1つのポイントでマージされる;どのようにして合流点がどこにあるかを知ることができますか?

条件:

  1. 長さがわからない
  2. 各リストを一度だけ解析する必要があります。

Example of two merged linked lists.

84
rplusg

もし

  • 「変更は許可されていません」とは、「変更することはできますが、最終的には復元する必要がある」ことを意味し、
  • リストを正確に反復できますtwice

次のアルゴリズムが解決策になります。

まず、数字。最初のリストの長さはa+cで、2番目のリストの長さはb+cであると想定します。ここで、cは(マージポイントの後の)一般的な「テール」の長さです。それらを次のように示しましょう。

x = a+c
y = b+c

長さがわからないため、追加の反復なしでxおよびyを計算します。方法がわかります。

次に、各リストを繰り返し、繰り返しながらそれらを逆にします!両方のイテレータが同時にマージポイントに到達した場合、単純に比較することでそれを見つけます。そうしないと、1つのポインターが他のポインターの前にマージポイントに到達します。

その後、他のイテレータがマージポイントに到達すると、共通のテールには進みません。代わりに、以前にマージポイントに到達したリストの前の先頭に戻ります!そのため、変更されたリストの最後(つまり、他のリストの前の部分)に到達する前に、彼は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

問題を解決します。

35
P Shved

以下は、私が見た中で最も素晴らしいものです-O(N)、カウンターなし。 S.N.候補者へのインタビューでそれを得た VisionMap で。

このように相互作用するポインターを作成します。最後まで毎回進み、反対のリストの先頭にジャンプします。 2つの頭を指すこれらの2つを作成します。ポインタが出会うまで、毎回1ずつ進めます。これは、1つまたは2つのパスの後に発生します。

私はまだインタビューでこの質問を使用していますが、誰かがこのソリューションが機能する理由を理解するのにどれくらい時間がかかるかを見るために。

126

Pavelの答えは、リストの修正を必要とします同様に各リストを2回繰り返す。

onlyで各リストを2回反復する必要があるソリューションを示します(最初にその長さを計算する。長さが指定されている場合、1回だけ反復する必要があります)。

これは、2つのポインターがリストの末尾から等しい距離になるように、長いリストの開始エントリを無視することです(マージポイントは存在できません)。次に、結合するまでそれらを前方に移動します。

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

これは、他の答えと漸近的に同じ(線形時間)ですが、おそらく定数が小さいため、おそらくより高速です。しかし、他の答えはもっとクールだと思います。

89
Artelius

まあ、あなたがそれらがマージすることを知っているなら:

次のように始めます:

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

1)最初のリストを調べて、次の各ポインターをNULLに設定します。

今、あなたは持っています:

A   B   C

1-->2-->3   4   5

2)2番目のリストを調べて、NULL(マージポイント)が表示されるまで待ちます。

確実にマージできない場合は、ポインタ値にセンチネル値を使用できますが、それほどエレガントではありません。

28
tster

リストを正確に2回反復できる場合、マージポイントを決定する方法を提供できます。

  • 両方のリストを繰り返し、長さAとBを計算します
  • 長さの差を計算C = | A-B |;
  • 両方のリストを同時に繰り返し開始しますが、リストに追加のCステップを追加します
  • この2つのポインターは、マージポイントで互いに一致します。
12
rachvela

これは、計算が速い(各リストを1回繰り返す)ソリューションですが、大量のメモリを使用します。

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
7
Skizz

これはおそらく「各リストを1回だけ解析する」条件に違反しますが、 カメとウサギのアルゴリズム (循環リストのマージポイントとサイクル長を見つけるために使用)を実装し、リストAから開始し、最後にNULLに到達すると、リストBの先頭へのポインタであると偽装し、循環リストの外観を作成します。アルゴリズムは、リストAのマージがどれだけ下にあるかを正確に示します(Wikipediaの説明によると、変数「mu」)。

また、「ラムダ」値はリストBの長さを示し、必要に応じて、アルゴリズム中(NULLリンクをリダイレクトするとき)にリストAの長さを計算できます。

6
Artelius

一連のノードを使用できます。 1つのリストを反復処理し、各Node=をセットに追加します。2番目のリストを反復処理し、反復ごとに、Nodeがセットに存在するかどうかを確認します。もしそうなら、あなたはあなたのマージポイントを見つけました:)

6
isyi

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;

この提案は変更されたノードアドレスを復元するため、「変更なし」と見なすことができます。

3
Test

たぶん私はこれを単純化しすぎているかもしれませんが、単に最小のリストを繰り返し、最後のノードLinkをマージポイントとして使用しますか?

だから、Data->Link->Link == NULLは終点であり、Data->Linkをマージポイントとして(リストの最後に)。

編集:

さて、投稿した写真から、2つのリストを解析します。最小のものが最初です。最小のリストを使用すると、次のノードへの参照を維持できます。ここで、2番目のリストを解析するとき、参照で比較を行い、参照[i]がLinkedList [i]-> Linkの参照である場所を見つけます。これにより、マージポイントが得られます。写真で説明する時間(写真に値を重ね合わせますOP)。

リンクされたリストがあります(以下に示す参照)。

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

2番目のリンクリストがあります。

1->2->

マージされたリストを使用すると、参照は次のようになります。

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

したがって、最初の「より小さい」リストをマップします(マージリストの長さは4で、メインリストは5です)

最初のリストをループし、参照の参照を維持します。

リストには、次の参照が含まれますPointers { 1, 2, D, E }

次に、2番目のリストを確認します。

-> 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.

もちろん、ポインタの新しいリストを維持しますが、それは仕様の範囲外ではありません。ただし、最初のリストは1回だけ解析され、2番目のリストはマージポイントがない場合のみ完全に解析されます。それ以外の場合は、(マージポイントで)より早く終了します。

3
Kyle Rozendo

簡単な解決策もありますが、補助スペースが必要になります。これは、リストを走査して各アドレスをハッシュマップに保存し、もう一方のリストを走査して、アドレスがハッシュマップにあるかどうかを照合するという考え方です。各リストは1回だけ走査されます。リストに変更はありません。長さはまだ不明です。使用される補助スペース:O(n)ここで、「n」は最初にトラバースされたリストの長さです。

2
Vikas Agarwal

このソリューションは、各リストを1回だけ反復します...リストの変更も必要ありません。ただし、スペースについて文句を言うかもしれません。
1)基本的にはlist1を反復処理し、各ノードのアドレスを配列(unsigned int値を格納する)に格納します
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;
} 

それが有効な解決策であることを願っています...

2
rajya vardhan

リストを変更する必要はありません。各リストを一度だけトラバースするだけで済む解決策があります。

  1. 2つのスタックを作成します。stck1とstck2とします。
  2. 1番目のリストをトラバースし、stck1でトラバースする各ノードのコピーをプッシュします。
  3. ステップ2と同じですが、今回は2番目のリストを走査し、stck2でノードのコピーをプッシュします。
  4. 次に、両方のスタックからポップし、2つのノードが等しいかどうかを確認します。等しい場合は、それらへの参照を保持します。いいえの場合、等しい以前のノードは実際に探していたマージポイントです。
1
ABVash

ここに素朴な解決策があります。リスト全体を横断する必要はありません。

構造化ノードに次のような3つのフィールドがある場合

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

2つのリストの頭を指す2つの頭(head1とhead2)があるとします。

同じペースで両方のリストをトラバースし、そのノードにフラグ= 1(訪問済みフラグ)を設定し、

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

マップまたは辞書を使用して、アドレスとノードの値を保存します。 Map/Dictionaryにすでにアドレスが存在する場合、キーの値が答えです。これは私がしました:

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
Vishal Anand

これはどう:

  1. 各リストを1回だけトラバースできる場合、新しいノードを作成し、最初のリストをトラバースしてすべてのノードがこの新しいノードを指すようにし、2番目のリストをトラバースして、ノードが新しいノードを指しているかどうかを確認できます(それがあなたのマージポイントです)。 2番目のトラバースが新しいノードにつながらない場合、元のリストにはマージポイントがありません。

  2. リストを複数回トラバースすることが許可されている場合、各リストをトラバースして長さを確認できます。異なる場合は、長いリストの先頭にある「余分な」ノードを省略します。次に、両方のリストを一度に1ステップずつトラバースし、最初のマージノードを見つけます。

0
user2024069

ステップ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
0
svaithin

Javaでの手順:

  1. マップを作成します。
  2. リストの両方のブランチでトラバースを開始し、ノード(ノードIDなど)に関連するいくつかの一意のキーをキーとして使用して、リストのトラバースされたすべてのノードをマップに入れます。
  3. 最初に重複するキーが来たら、そのキーの値を増やします(その値が2になり、1より大きいとしましょう。
  4. 値が1より大きく、2つのリストがマージされるノードである必要があるキーを取得します。
0
King KB

これは本当の問題です! SQLデータベースにツリー構造がある場合。 SQLトリガーでLCAを見つけたい。

トリガー環境では、トランザクションを暗黙的にコミットするため、一時テーブルを作成できません。したがって、O(1)メモリで行う必要があります。つまり、変数のみを使用できます。

0
Kai Liu

A O(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
user3828943
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;
}
0
Yachana Yachana

「isVisited」フィールドを導入することにより、効率的に解決できます。最初のリストを走査し、最後まですべてのノードの「isVisited」値を「true」に設定します。次に、2番目から開始して、フラグがtrueで、Boomがマージポイントである最初のノードを見つけます。

0
Riya kathil