web-dev-qa-db-fra.com

Vérifiez si deux listes liées se fusionnent. Si oui où?

Cette question est peut-être ancienne, mais je ne pouvais pas penser à une réponse.

Disons qu'il y a deux listes de longueurs différentes, fusionnant en un point ; Comment savons-nous où se trouve le point de fusion?

Conditions:

  1. Nous ne connaissons pas la longueur 
  2. Nous devrions analyser chaque liste une seule fois.

Example of two merged linked lists.

81
rplusg

Si

  • par "modification n'est pas autorisée" cela signifiait "vous pouvez changer mais à la fin ils devraient être restaurés", et
  • nous pourrions parcourir les listes exactement deux fois

l'algorithme suivant serait la solution.

Tout d'abord, les chiffres. Supposons que la première liste a la longueur a+c et la seconde la longueur b+c, où c est la longueur de leur queue commune (après le point de fusion). Notons-les comme suit:

x = a+c
y = b+c

Puisque nous ne connaissons pas la longueur, nous calculerons x et y sans itérations supplémentaires; vous verrez comment.

Ensuite, nous parcourons chaque liste et les inversons en les parcourant! Si les deux itérateurs atteignent le point de fusion en même temps, nous le découvrons simplement en comparant. Sinon, un pointeur atteindra le point de fusion avant l'autre.

Après cela, lorsque l'autre itérateur atteint le point de fusion, il ne passera pas à la fin commune. Nous reviendrons plutôt au début de la liste qui avait atteint le point de fusion auparavant! Donc, avant qu’il atteigne la fin de la liste modifiée (c’est-à-dire l’ancien début de l’autre liste), il effectuera un total de a+b+1 itérations. Appelons-le z+1.

Le pointeur qui a atteint le point de fusion en premier continuera à itérer jusqu'à la fin de la liste. Le nombre d'itérations effectuées doit être calculé et est égal à x.

Ensuite, ce pointeur revient en arrière et renverse les listes. Mais maintenant, il ne reviendra pas au début de la liste d'origine! Au lieu de cela, cela ira au début de l'autre liste! Le nombre d'itérations effectuées doit être calculé et égal à y.

Nous connaissons donc les chiffres suivants:

x = a+c
y = b+c
z = a+b

À partir de laquelle nous déterminons que

a = (+x-y+z)/2
b = (-x+y+z)/2
c = (+x+y-z)/2

Ce qui résout le problème.

35
P Shved

Ce qui suit est de loin le plus grand de tous ce que j'ai vu - O (N), pas de jetons. Je l'ai eu lors d'un entretien avec un candidat S.N. à VisionMap .

Faites un pointeur interagissant comme ceci: il avance chaque fois jusqu'à la fin, puis saute au début de la liste opposée, et ainsi de suite . Créez-en deux, en pointant sur deux têtes. Avancez chaque pointeur de 1 à chaque fois, jusqu'à ce qu'ils se rencontrent. Cela se produira après un ou deux passages.

J'utilise encore cette question dans les entretiens - mais pour voir combien de temps il faut à quelqu'un pour comprendre pourquoi cette solution fonctionne.

113
Pavel Radzivilovsky

La réponse de Pavel nécessite la modification des listes ainsi que itérant chaque liste deux fois.

Voici une solution qui seulement nécessite d’itérer chaque liste deux fois (la première fois pour calculer leur longueur; si la longueur est donnée, vous n’avez qu’à itérer une fois).

L'idée est d'ignorer les entrées de départ de la liste la plus longue (le point de fusion ne peut pas être là), afin que les deux pointeurs soient à égale distance de la fin de la liste. Puis déplacez-les vers l'avant jusqu'à leur fusion.

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

C'est asymptotiquement la même chose (temps linéaire) que mon autre réponse, mais a probablement des constantes plus petites, donc est probablement plus rapide. Mais je pense que mon autre réponse est plus cool.

88
Artelius

Eh bien, si vous savez qu'ils vont fusionner:

Dites que vous commencez par:

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

1) Parcourez la première liste en réglant chaque pointeur sur NULL.

Maintenant vous avez:

A   B   C

1-->2-->3   4   5

2) Maintenant, parcourez la deuxième liste et attendez qu’un NULL apparaisse, c’est votre point de fusion.

Si vous ne pouvez pas être sûr qu'ils fusionnent, vous pouvez utiliser une valeur sentinelle pour le pointeur, mais ce n'est pas aussi élégant.

27
tster

Si nous pouvions itérer des listes exactement deux fois, je pourrais fournir une méthode pour déterminer le point de fusion:

  • itérer les deux listes et calculer les longueurs A et B
  • calculer la différence de longueur C = | A-B |;
  • commencer à itérer les deux listes simultanément, mais faire des étapes C supplémentaires sur la liste, ce qui était plus important
  • ces deux pointeurs se rencontreront dans le point de fusion 
12
rachvela

Voici une solution, rapide en calcul (itère chaque liste une fois) mais utilise beaucoup de mémoire:

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

Cela viole sans doute la condition "analyser chaque liste une seule fois", mais implémente l'algorithme tortoise and hare (utilisé pour trouver le point de fusion et la longueur du cycle d'une liste cyclique) afin le NULL à la fin vous faites semblant que c'est un pointeur vers le début de la liste B, créant ainsi l'apparence d'une liste cyclique. L'algorithme vous indiquera alors à quelle distance se trouve la liste A de la fusion (la variable 'mu' d'après la description de Wikipedia).

En outre, la valeur "lambda" vous indique la longueur de la liste B et, si vous le souhaitez, vous pouvez déterminer la longueur de la liste A au cours de l'algorithme (lorsque vous redirigez le lien NULL).

6
Artelius

Vous pouvez utiliser un ensemble de nœuds. Parcourez une liste et ajoutez chaque nœud à l'ensemble. Ensuite, parcourez la deuxième liste et, pour chaque itération, vérifiez si le nœud existe dans l'ensemble. Si c'est le cas, vous avez trouvé votre point de fusion :)

5
isyi

J'ai testé un cas de fusion sur mon FC9 x86_64 et j'imprime chaque adresse de noeud comme indiqué ci-dessous:

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


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

Note parce que j'avais aligné la structure du noeud, donc quand malloc () est un noeud, l'adresse est alignée sur 16 octets, voir les 4 bits au moins. Les bits les moins importants sont des 0, c’est-à-dire 0x0 ou 000b . Ainsi, si vous êtes dans le même cas particulier (adresse de noeud alignée), vous pouvez également utiliser ces 4 bits au moins . listes de tête en queue, jeu 1 ou 2 des 4 bits de l'adresse du noeud visiteur, c'est-à-dire jeu d'un drapeau;

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

Remarque Les indicateurs ci-dessus n'affecteront pas l'adresse du nœud réel, mais uniquement la valeur de votre pointeur de nœud SAVED.

Une fois que quelqu'un a trouvé le ou les bits d'indicateur, le premier noeud trouvé doit être le point de fusion. Après avoir terminé, vous restaureriez l'adresse du nœud en effaçant les bits de drapeau que vous aviez définis. Une chose importante est que vous devez faire attention lorsque vous effectuez une itération (par exemple, node = node-> next) pour effectuer un nettoyage. rappelez-vous que vous aviez défini des bits de drapeau, alors faites de cette façon 

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

Etant donné que cette proposition restaurera les adresses de noeud modifiées, elle pourrait être considérée comme "aucune modification".

3
Test

Peut-être que je simplifie trop la procédure, mais il suffit d’itérer la plus petite liste et d’utiliser les derniers nœuds Link comme point de fusion?

Donc, où Data->Link->Link == NULL est le point final, donnant Data->Link comme point de fusion (à la fin de la liste).

MODIFIER:

D'accord, d'après la photo que vous avez publiée, vous analysez les deux listes, la plus petite en premier. Avec la plus petite liste, vous pouvez gérer les références au nœud suivant. Maintenant, lorsque vous analysez la deuxième liste, vous effectuez une comparaison sur la référence pour trouver où Référence [i] est la référence sur LinkedList [i] -> Lien. Cela donnera le point de fusion. Temps pour expliquer avec des images (superposer les valeurs sur l'image de l'OP).

Vous avez une liste chaînée (références ci-dessous):

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

Vous avez une deuxième liste chaînée:

1->2->

Avec la liste fusionnée, les références iraient alors comme suit:

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

Par conséquent, vous mappez la première liste "la plus petite" (comme la liste fusionnée, ce que nous comptons a une longueur de 4 et la liste principale 5)

Parcourez la première liste, maintenez une référence de références.

La liste contiendra les références suivantes Pointers { 1, 2, D, E }.

Nous passons maintenant à la deuxième liste:

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

Bien sûr, vous maintenez une nouvelle liste de pointeurs, mais ce n’est pas en dehors de la spécification. Cependant, la première liste est analysée exactement une fois, et la deuxième liste ne sera complètement analysée que s'il n'y a pas de point de fusion. Sinon, cela se terminera plus tôt (au point de fusion).

3
Kyle Rozendo

cette solution itère chaque liste une seule fois ... aucune modification de liste n’est requise aussi .. bien que vous puissiez vous plaindre de l’espace ..
1) En gros, vous parcourez list1 et stockez l’adresse de chaque nœud dans un tableau (qui stocke la valeur unsigned int)
2) Ensuite, vous parcourez list2, et pour chaque adresse de nœud ---> vous recherchez dans le tableau que vous trouviez une correspondance ou non ... si vous le faites, il s'agit du nœud de fusion

//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;
} 

J'espère que c'est une solution valable ...

2
rajya vardhan

Il peut y avoir une solution simple mais nécessitera un espace auxiliaire. L'idée est de parcourir une liste et de stocker chaque adresse dans une carte de hachage, puis de parcourir l'autre liste et de la faire correspondre si l'adresse se trouve ou non dans la carte de hachage. Chaque liste est parcourue une seule fois. Il n'y a aucune modification sur aucune liste. La longueur est encore inconnue. Espace auxiliaire utilisé: O(n) où 'n' est la longueur de la première liste parcourue.

1
Vikas Agarwal

Il n'est pas nécessaire de modifier une liste. Il existe une solution dans laquelle il suffit de parcourir chaque liste une fois. 

  1. Créez deux piles, disons stck1 et stck2.
  2. Parcourez la 1ère liste et insérez une copie de chaque nœud que vous parcourez dans stck1.
  3. Identique à la deuxième étape mais cette fois, parcourez la deuxième liste et transmettez la copie des nœuds dans stck2.
  4. Maintenant, éjectez des deux piles et vérifiez si les deux nœuds sont égaux. Si oui, conservez une référence à ces nœuds. Si non, les nœuds précédents qui étaient égaux sont en réalité le point de fusion que nous recherchions.
1
ABVash

Voici la solution naïve, pas besoin de parcourir des listes entières.

si votre noeud structuré a trois champs comme

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

disons que vous avez deux têtes (head1 et head2) pointant vers la tête de deux listes.

Parcourez la liste au même rythme et mettez le drapeau = 1 (drapeau visité) pour ce nœud, 

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

Utilisez Map ou Dictionary pour stocker la valeur addressess vs du noeud. si l'adresse existe déjà dans la carte/le dictionnaire, la valeur de la clé est la réponse ..__J'ai fait ceci:

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

Que dis-tu de ça:

  1. Si vous n'êtes autorisé à parcourir chaque liste qu'une seule fois, vous pouvez créer un nouveau nœud, parcourir la première liste pour que chaque nœud pointe vers ce nouveau nœud et parcourir la deuxième liste pour voir si un nœud pointe vers votre nouveau nœud ( c'est votre point de fusion). Si la deuxième traversée ne mène pas à votre nouveau nœud, les listes d'origine n'ont pas de point de fusion.

  2. Si vous êtes autorisé à parcourir les listes plusieurs fois, vous pouvez parcourir chaque liste pour trouver sa longueur et, si elles sont différentes, omettre les nœuds "extra" situés au début de la liste plus longue. Ensuite, parcourez les deux listes une par une et trouvez le premier noeud de fusion.

0
user2024069

Étape 1: recherchez la longueur de la liste Étape 2: trouvez le diff et déplacez la plus grande liste avec la différence Étape 3: Maintenant, les deux listes seront dans une position similaire Étape 4: Parcourez la liste. pour trouver le point de fusion

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

Étapes en Java:

  1. Créer une carte. 
  2. Commencez à parcourir les deux branches de la liste et placez tous les nœuds de liste traversés dans la carte en utilisant un élément unique lié aux nœuds (par exemple, ID de nœud) en tant que clé et définissez les valeurs sous la forme 1 au début pour tous.
  3. Lorsque vient la première copie de clé, incrémentez la valeur de cette clé (disons maintenant que sa valeur est devenue 2, ce qui est> 1.
  4. Obtenez la clé où la valeur est supérieure à 1 et qui devrait être le nœud où deux listes fusionnent. 
0
King KB

Ça c'est un vrai problème! Si nous avons une arborescence dans la base de données SQL. Nous voulons trouver l'ACL dans un déclencheur SQL.

Dans un environnement de déclenchement, nous ne pouvons pas créer de table temporaire car elle impliquera implicitement la transaction. Nous devons donc le faire dans la mémoire O(1), c’est-à-dire que nous ne pouvons utiliser que variable.

0
Kai Liu

A O(n) solution de complexité. Mais basé sur une hypothèse.

l’hypothèse est la suivante: les deux nœuds n’ont que des entiers positifs.

logique: rendre tout l'entier de list1 négatif. Parcourez ensuite la liste2 jusqu'à obtenir un entier négatif. Une fois trouvé => prenez-le, changez le signe en positif et revenez.

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

Nous pouvons le résoudre efficacement en introduisant le champ "isVisited". Parcourez la première liste et définissez la valeur "isVisited" sur "true" pour tous les nœuds jusqu'à la fin. Maintenant, commencez en deuxième et trouvez le premier noeud où flag est true et Boom, votre point de fusion.

0
Riya kathil