Bu soru eski olabilir ama bir cevap düşünemedim.
Diyelim ki, bir noktada birleşen farklı uzunluklarda iki liste var ; birleşme noktasının nerede olduğunu nasıl bileceğiz?
Koşullar:
- Uzunluğu bilmiyoruz
- Her listeyi yalnızca bir kez ayrıştırmalıyız.
Bu soru eski olabilir ama bir cevap düşünemedim.
Diyelim ki, bir noktada birleşen farklı uzunluklarda iki liste var ; birleşme noktasının nerede olduğunu nasıl bileceğiz?
Koşullar:
Yanıtlar:
Eğer
Aşağıdaki algoritma çözüm olacaktır.
Önce sayılar. İlk listenin uzunlukta a+c
ve ikinci listenin uzunlukta olduğunu varsayın b+c
, burada c
ortak "kuyruk" unun uzunluğu (birleşme noktasından sonra). Bunları şu şekilde gösterelim:
x = a+c
y = b+c
Biz uzunluğunu bilmiyorum yana, hesaplar x
ve y
ek tekrarlamalar olmadan; nasıl olduğunu göreceksin.
Ardından, her listeyi yineler ve yinelerken tersine çeviririz! Her iki yineleyici de birleştirme noktasına aynı anda ulaşırsa, bunu yalnızca karşılaştırma yaparak buluruz. Aksi takdirde, bir işaretçi diğerinden önce birleştirme noktasına ulaşacaktır.
Bundan sonra, diğer yineleyici birleştirme noktasına ulaştığında, ortak kuyruğa ilerlemeyecektir. Bunun yerine, daha önce birleşme noktasına ulaşan listenin eski başlangıcına geri dönecektir! Dolayısıyla, değiştirilen listenin sonuna (yani diğer listenin önceki başlangıcına) ulaşmadan önce, a+b+1
toplam yinelemeleri yapacaktır . Hadi diyelim z+1
.
İlk olarak birleştirme noktasına ulaşan işaretçi, listenin sonuna ulaşıncaya kadar yinelemeye devam edecektir. Yaptığı yineleme sayısı hesaplanmalı ve eşittir x
.
Ardından, bu işaretçi geri döner ve listeleri yeniden tersine çevirir. Ama şimdi başlangıçta başladığı listenin başına dönmeyecek! Bunun yerine, diğer listenin başına gidecek! Yaptığı yineleme sayısı hesaplanmalı ve eşit olmalıdır y
.
Yani aşağıdaki sayıları biliyoruz:
x = a+c
y = b+c
z = a+b
Bunu belirlediğimiz
a = (+x-y+z)/2
b = (-x+y+z)/2
c = (+x+y-z)/2
Sorunu çözen.
Aşağıdakiler, gördüğüm her şeyin en büyüğü - O (N), sayaç yok. SN adayıyla bir röportaj sırasında aldım:VisionMap'te .
Şunun gibi araya giren bir işaretçi yapın: sonuna kadar her defasında ileri gider ve sonra karşı listenin başına atlar, vb. İki kafayı göstererek bunlardan iki tane oluşturun. İşaretçilerin her birini buluşana kadar her seferinde 1 artırın. Bu, bir veya iki geçişten sonra gerçekleşir.
Bu soruyu görüşmelerde hala kullanıyorum - ancak birinin bu çözümün neden işe yaradığını anlamasının ne kadar sürdüğünü görmek için.
a-b-c-x-y-z
ve p-q-x-y-z
. ilk işaretçinin a,b,c,x,y,z,p,q,x
yolu, ikinci işaretçinin yolup,q,x,y,z,a,b,c,x
Pavel'in cevabı, listelerin yanı sıra iki kez her liste yineleme.
İşte her listeyi yalnızca iki kez yinelemeyi gerektiren bir çözüm (uzunluklarını ilk kez hesaplamak için; uzunluk verilirse yalnızca bir kez yinelemeniz gerekir).
Buradaki fikir, uzun listenin başlangıç girişlerini yok saymaktır (birleştirme noktası orada olamaz), böylece iki işaretçi listenin sonundan eşit uzaklıkta olur. Sonra birleşene kadar ileriye doğru hareket ettirin.
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
Bu, diğer cevabımla asimptotik olarak aynıdır (doğrusal zaman), ancak muhtemelen daha küçük sabitlere sahiptir, bu nedenle muhtemelen daha hızlıdır. Ama diğer cevabımın daha havalı olduğunu düşünüyorum.
Peki, birleşeceklerini biliyorsanız:
Şöyle başladığını söyle:
A-->B-->C
|
V
1-->2-->3-->4-->5
1) Her bir sonraki işaretçiyi NULL olarak ayarlayan ilk listeden geçin.
Şimdi sahipsin:
A B C
1-->2-->3 4 5
2) Şimdi ikinci listeye geçin ve bir NULL görene kadar bekleyin, bu sizin birleştirme noktanızdır.
Birleştiklerinden emin olamıyorsanız, işaretçi değeri için bir gözcü değeri kullanabilirsiniz, ancak bu o kadar zarif değildir.
Listeleri tam olarak iki kez yineleyebilseydik, birleştirme noktasını belirlemek için bir yöntem sağlayabilirim:
İşte bir çözüm, hesaplama açısından hızlı (her listeyi bir kez yineliyor) ancak çok fazla bellek kullanıyor:
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
Bir dizi Düğüm kullanabilirsiniz. Bir listeyi yineleyin ve her Düğümü kümeye ekleyin. Ardından ikinci listeyi yineleyin ve her yinelemede düğümün kümede olup olmadığını kontrol edin. Varsa, birleştirme noktanızı buldunuz :)
Bu muhtemelen "her listeyi yalnızca bir kez ayrıştır" koşulunu ihlal eder, ancak kaplumbağa ve tavşan algoritmasını (döngüsel bir listenin birleştirme noktasını ve döngü uzunluğunu bulmak için kullanılır) uygular, böylece Liste A'dan başlarsınız ve NULL'a ulaştığınızda end B listesinin başlangıcına bir gösterici gibi davranırsınız, böylece döngüsel bir liste görünümü yaratırsınız. Ardından algoritma size A Listesinin tam olarak ne kadar aşağıda olduğunu söyleyecektir (Wikipedia açıklamasına göre 'mu' değişkeni).
Ayrıca, "lambda" değeri size B listesinin uzunluğunu söyler ve isterseniz, algoritma sırasında (NULL bağlantısını yeniden yönlendirdiğinizde) A listesinin uzunluğunu hesaplayabilirsiniz.
Belki bunu fazla basitleştiriyorum, ancak en küçük listeyi yineleyin ve son düğümleri Link
birleşme noktası olarak kullanın.
Öyleyse, Data->Link->Link == NULL
bitiş noktası nerede , Data->Link
birleşme noktası olarak (listenin sonunda) verilir.
DÜZENLE:
Tamam, gönderdiğiniz resimden, en küçüğü ilk olmak üzere iki listeyi ayrıştırırsınız. En küçük liste ile aşağıdaki düğüme referansları koruyabilirsiniz. Şimdi, ikinci listeyi ayrıştırdığınızda, Referans [i] 'nin LinkedList [i] -> Link'teki referans olduğunu bulmak için referans üzerinde bir karşılaştırma yaparsınız. Bu, birleştirme noktasını verecektir. Resimlerle açıklama zamanı (değerleri resimdeki OP üzerine yerleştirin).
Bağlantılı bir listeniz var (aşağıda gösterilen referanslar):
A->B->C->D->E
İkinci bir bağlantılı listeniz var:
1->2->
Birleştirilmiş listeyle, referanslar şu şekilde olacaktır:
1->2->D->E->
Bu nedenle, ilk "daha küçük" listeyi eşlersiniz (bizim saydığımız birleştirilmiş listenin uzunluğu 4 ve ana liste 5 olduğu için)
İlk listede döngü yapın, referansların referansını koruyun.
Liste aşağıdaki referansları içerecektir Pointers { 1, 2, D, E }
.
Şimdi ikinci listeye geçiyoruz:
-> 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.
Elbette, yeni bir işaretçiler listesi tutuyorsunuz, ancak bu spesifikasyonun dışında değil. Bununla birlikte, ilk liste tam olarak bir kez ayrıştırılır ve ikinci liste yalnızca bir birleştirme noktası yoksa tam olarak ayrıştırılır. Aksi takdirde, daha erken biter (birleştirme noktasında).
FC9 x86_64 cihazımda bir birleştirme durumunu test ettim ve her düğüm adresini aşağıda gösterildiği gibi yazdırdım:
Head A 0x7fffb2f3c4b0
0x214f010
0x214f030
0x214f050
0x214f070
0x214f090
0x214f0f0
0x214f110
0x214f130
0x214f150
0x214f170
Head B 0x7fffb2f3c4a0
0x214f0b0
0x214f0d0
0x214f0f0
0x214f110
0x214f130
0x214f150
0x214f170
Düğüm yapısını hizaladığıma dikkat edin, bu nedenle malloc () bir düğüm olduğunda, adres 16 bayt ile hizalanır, bkz. En az 4 bit. En küçük bitler 0'dır, yani 0x0 veya 000b. Yani siz de aynı özel durumdaysanız (hizalanmış düğüm adresi), bu en az 4 biti kullanabilirsiniz. Örneğin, her iki listeyi de baştan sona gezerken, ziyaret düğümü adresinin 4 bitinin 1 veya 2'sini ayarlayın, yani bir bayrak ayarlayın;
next_node = node->next;
node = (struct node*)((unsigned long)node | 0x1UL);
Yukarıdaki bayraklar gerçek düğüm adresini etkilemez, yalnızca SAVED düğüm işaretçi değerini etkiler.
Birinin bayrak bitlerini ayarladığını bulduktan sonra, bulunan ilk düğüm birleştirme noktası olmalıdır. tamamlandıktan sonra, ayarladığınız bayrak bitlerini temizleyerek düğüm adresini geri yüklersiniz. önemli olan şey ise, temizlik yapmak için yinelediğinizde (örn. düğüm = düğüm-> sonraki) dikkatli olmanız gerektiğidir. bayrak bitleri koyduğunuzu hatırlayın, bu şekilde yapın
real_node = (struct node*)((unsigned long)node) & ~0x1UL);
real_node = real_node->next;
node = real_node;
Bu teklif, değiştirilen düğüm adreslerini geri yükleyeceğinden, "değişiklik yok" olarak kabul edilebilir.
Basit bir çözüm olabilir, ancak yardımcı bir alan gerektirecektir. Buradaki fikir, bir listeyi dolaşmak ve her adresi bir hash haritasında saklamak, şimdi diğer listeyi taramak ve adres, karma haritada yer alıp almamaktır. Her liste yalnızca bir kez taranır. Herhangi bir listede değişiklik yok. Uzunluk hala bilinmiyor. Kullanılan yardımcı boşluk: O (n) burada 'n', geçilen ilk listenin uzunluğudur.
bu çözüm, her listeyi yalnızca bir kez yineler ... listenin değiştirilmesine de gerek yoktur ... gerçi alan hakkında şikayetçi olabilirsiniz ..
1) Temel olarak liste1'de yinelersiniz ve her düğümün adresini bir dizide saklarsınız (işaretsiz int değerini saklar)
2) Sonra list2'yi yinelersiniz ve her düğümün adresi için ---> bir eşleşme bulup bulmadığınız dizide arama yaparsınız ... Eğer yaparsanız, o zaman bu birleştirme düğümüdür
//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;
}
Umarım geçerli bir çözümdür ...
Herhangi bir listeyi değiştirmeye gerek yoktur. Her listeyi yalnızca bir kez geçmemiz gereken bir çözüm var.
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;
}
İşte saf bir çözüm, tüm listeleri taramaya gerek yok.
yapılandırılmış düğümünüz gibi üç alan varsa
struct node {
int data;
int flag; //initially set the flag to zero for all nodes
struct node *next;
};
iki listenin başına işaret eden iki başınız (başlık1 ve baş2) olduğunu söyleyin.
Her iki listeyi de aynı hızda geçin ve bu düğüm için bayrağı = 1 (ziyaret edilen bayrak) koyun,
if (node->next->field==1)//possibly longer list will have this opportunity
//this will be your required node.
Buna ne dersin:
Her listeyi yalnızca bir kez geçme izniniz varsa, yeni bir düğüm oluşturabilir, her düğüm noktasının bu yeni düğüme gelmesini sağlamak için ilk listeden geçebilir ve herhangi bir düğümün yeni düğümünüzü işaret edip etmediğini görmek için ikinci listeyi geçebilirsiniz ( bu birleştirme noktanız). İkinci geçiş yeni düğümünüze yol açmazsa, orijinal listelerin bir birleştirme noktası yoktur.
Listeleri birden fazla kez gezme izniniz varsa, uzunluklarımızı bulmak için her listeyi gezebilir ve farklılarsa uzun listenin başındaki "ekstra" düğümleri atlayabilirsiniz. Ardından, her iki listeyi bir seferde bir adım öteye taşıyın ve ilk birleştirme düğümünü bulun.
Java'daki adımlar:
"İsVisited" alanını tanıtarak bunu verimli bir şekilde çözebiliriz. İlk listeyi çaprazlayın ve sonuna kadar tüm düğümler için "isVisited" değerini "true" olarak ayarlayın. Şimdi ikinciden başlayın ve bayrağın doğru olduğu ilk düğümü bulun ve Boom, onun birleşme noktanız.
Adım 1: Her iki listenin uzunluğunu bulun Adım 2: Farkı bulun ve en büyük listeyi farkla taşıyın Adım 3: Şimdi her iki liste de benzer konumda olacaktır. Adım 4: Birleştirme noktasını bulmak için listeyi yineleyin
//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;
}
Düğümün değerine karşı adreslerini saklamak için Harita veya Sözlük kullanın. Adres Harita / Sözlük'te zaten mevcutsa, anahtarın değeri cevaptır. Bunu ben yaptım:
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) karmaşıklık çözümü. Ama bir varsayıma dayanıyor.
varsayım şudur: her iki düğüm de yalnızca pozitif tam sayılara sahiptir.
mantık: list1'in tüm tam sayılarını negatif yapın. Daha sonra, negatif bir tam sayı elde edene kadar list2 üzerinden ilerleyin. Bulduktan sonra => al, işareti tekrar pozitif olarak değiştir ve geri dön.
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;
}
}
İki işaretçi kullanabiliriz ve öyle bir şekilde hareket edebiliriz ki, işaretçilerden biri boş ise onu diğer listenin başına gösteririz ve diğeri için aynıysa, bu şekilde liste uzunlukları farklıysa ikinci geçişte buluşurlar. . List1'in uzunluğu n ve list2 m ise, farkları d = abs (nm) olur. Bu mesafeyi katedecekler ve birleşme noktasında buluşacaklar.
Kod:
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;
}
Düğümlerini list1
bir hashset'e ve döngüden ikinciye ekleyebilirsiniz ve kümede herhangi bir düğüm list2
zaten varsa .Evet ise , bu birleştirme düğümüdür.
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 kullanarak çözüm
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;
}
Bağlantılı listeyi düzenlemeye izin veriliyorsa,