この質問は古いかもしれませんが、私は答えを考えることができませんでした。
たとえば、長さが異なる2つのリストがあり、ある時点でマージされます。マージポイントがどこにあるかをどのように知ることができますか?
条件:
- 長さがわからない
- 各リストを一度だけ解析する必要があります。
この質問は古いかもしれませんが、私は答えを考えることができませんでした。
たとえば、長さが異なる2つのリストがあり、ある時点でマージされます。マージポイントがどこにあるかをどのように知ることができますか?
条件:
回答:
もし
次のアルゴリズムが解決策です。
まず、数字。最初のリストが長さ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
これは問題を解決します。
以下は、私が見た中で最も優れている-O(N)、カウンターなし。VisionMapでの SN候補者へのインタビュー中に入手しました。
次のように対話ポインタを作成します。毎回最後まで進み、反対のリストの先頭にジャンプします。これらの2つを作成し、2つの頭を指すようにします。彼らが会うまで、それぞれのポインターを毎回1ずつ進めます。これは、1つまたは2つのパスの後に発生します。
私はまだこの質問をインタビューで使用していますが、このソリューションが機能する理由を誰かが理解するのにどれくらい時間がかかるかを確認します。
a-b-c-x-y-z
とp-q-x-y-z
。最初のポインタのa,b,c,x,y,z,p,q,x
パス、2番目のポインタのパスp,q,x,y,z,a,b,c,x
パヴェルの答えは、リストの変更を必要とするだけでなく、 2回リストを反復します。
これは、各リストを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
これは漸近的に他の答えと同じ(線形時間)ですが、おそらく定数が小さいため、おそらく高速です。しかし、私は私のもう1つの答えはもっとクールだと思います。
まあ、あなたが彼らが合併することを知っているなら:
あなたが次のように始めるとしましょう:
A-->B-->C
|
V
1-->2-->3-->4-->5
1)次の各ポインターをNULLに設定して、最初のリストを調べます。
今あなたは持っています:
A B C
1-->2-->3 4 5
2)次に、2番目のリストを調べ、NULL、つまりマージポイントが表示されるまで待ちます。
それらがマージすることを確認できない場合は、ポインター値にセンチネル値を使用できますが、それはエレガントではありません。
以下は、計算が速い(各リストを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
ノードのセットを使用できます。1つのリストを反復処理し、各ノードをセットに追加します。次に、2番目のリストを反復処理し、反復ごとに、ノードがセットに存在するかどうかを確認します。もしそうなら、あなたはあなたのマージポイントを見つけました:)
これは間違いなく「各リストを1回だけ解析する」条件に違反しますが、亀とうさぎのアルゴリズム(循環リストのマージポイントとサイクル長を見つけるために使用)を実装しているため、リストAから開始し、 endは、リストBの先頭へのポインタであると偽って、循環リストの外観を作成します。アルゴリズムは、リストAのマージがどこまで下にあるかを正確に通知します(Wikipediaの説明によると、変数 'mu')。
また、「lambda」値はリストBの長さを示します。必要な場合は、アルゴリズム中に(NULLリンクをリダイレクトするときに)リストAの長さを計算できます。
多分私はこれを単純化しすぎていますが、最小のリストを反復して、最後のノードLink
をマージポイントとして使用していますか?
したがって、(リストの最後にある)マージポイントとしてData->Link->Link == NULL
与えるエンドポイントはどこにあるのData->Link
でしょうか。
編集:
投稿した写真から、2つのリストを最初に解析します。最小のリストを使用すると、次のノードへの参照を維持できます。ここで、2番目のリストを解析するときに、参照を比較して、Reference [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番目のリストは、マージポイントがない場合にのみ完全に解析されます。それ以外の場合は、(マージポイントで)より早く終了します。
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回だけトラバースされます。どのリストにも変更はありません。長さはまだ不明です。使用される補助スペース:O(n)ここで、「n」は最初に通過したリストの長さです。
このソリューションは..あなたがスペースに文句を言うかもしれtoo..though一度だけ...リストの修正は必要ありません各リストを反復する
リスト1に基本的に反復)1と配列(店舗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;
}
それが有効な解決策であることを願っています...
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;
}
これは素朴な解決策です。リスト全体を走査する必要はありません。
構造化ノードに次のような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.
これはどう:
各リストを1回だけトラバースできる場合は、新しいノードを作成し、最初のリストをトラバースしてすべてのノードがこの新しいノードを指すようにし、2番目のリストをトラバースして、新しいノードを指しているノードがあるかどうかを確認できます(それがマージポイントです)。2番目のトラバーサルが新しいノードにつながらない場合、元のリストにはマージポイントがありません。
リストを複数回トラバースすることが許可されている場合は、各リストをトラバースして長さを確認し、長さが異なる場合は、長いリストの先頭にある「余分な」ノードを省略できます。次に、両方のリストを一度に1ステップずつトラバースし、最初のマージノードを見つけます。
Javaでの手順:
ステップ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
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;
}
マップまたは辞書を使用して、ノードのアドレスと値を保存します。住所がすでに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;
}
AO(n)複雑性ソリューション。しかし、仮定に基づいています。
仮定は次のとおりです。両方のノードが正の整数のみを持っています。
logic: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;
}
}
2つのポインターを使用して、一方のポインターがnullの場合、もう一方のリストの先頭を指し、もう一方も同じように移動することができます。これにより、リストの長さが異なる場合、2番目のパスで出会います。 。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;
}
のノードをlist1
ハッシュセットに追加し、2番目のループを実行できます。ノードの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;
}
JavaScriptを使用したソリューション
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;
}
リンクされたリストの編集が許可されている場合、