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

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

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

Conditions:

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

Exemple de deux listes liées fusionnées.

Si

  • par “modification n’est pas autorisée”, cela signifiait “vous pouvez changer mais à la fin ils doivent être restaurés”, et
  • nous pourrions itérer les listes exactement deux fois

l’algorithme suivant serait la solution.

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

 x = a+c y = b+c 

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

Ensuite, nous itérons chaque liste et les inversons tout en itérant! Si les deux iterators atteignent le sharepoint fusion en même temps, nous le trouvons par simple comparaison. Sinon, un pointeur atteindra le sharepoint fusion avant l’autre.

Après cela, lorsque l’autre iterator atteint le sharepoint fusion, il ne passera pas à la queue commune. Au lieu de cela reviendra à l’ancien début de la liste qui avait atteint le sharepoint fusion avant! Ainsi, avant d’atteindre la fin de la liste modifiée (c’est-à-dire l’ancien début de l’autre liste), il effectuera a+b+1 total de a+b+1 itérations. Appelons-le z+1 .

Le pointeur qui a atteint le sharepoint 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 inverse les listes. Mais maintenant, il ne reviendra pas au début de la liste à partir de laquelle il a été créé! Au lieu de cela, il 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+yz)/2 

Ce qui résout le problème.

Ce qui suit est de loin le plus grand de tout ce que j’ai vu – O (N), pas de jetons. Je l’ai eu lors d’une interview à un candidat SN chez VisionMap .

Créez un pointeur d’interaction comme celui-ci: il avance chaque fois jusqu’à la fin, puis passe au début de la liste opposée, et ainsi de suite. Créez-en deux, pointant vers deux têtes. Avancez chacun des indicateurs de 1 à chaque fois, jusqu’à ce qu’ils se rencontrent. Cela se produira après une ou deux passes.

J’utilise toujours cette question dans les interviews – mais pour voir combien de temps il faut à quelqu’un pour comprendre pourquoi cette solution fonctionne.

La réponse de Pavel nécessite la modification des listes et la répétition de chaque liste deux fois.

Voici une solution qui ne nécessite que l’itération de chaque liste deux fois (la première fois pour calculer sa longueur; si la longueur est donnée, il suffit de l’itérer une fois).

L’idée est d’ignorer les entrées de départ de la liste plus longue (le sharepoint fusion ne peut pas être là), de sorte que les deux pointeurs soient à égale distance de la fin de la liste. Puis déplacez-les vers l’avant jusqu’à ce qu’ils fusionnent.

 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 

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

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

Dites que vous commencez avec:

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

1) Parcourez la première liste en définissant chaque pointeur suivant sur NULL.

Maintenant vous avez:

 ABC 1-->2-->3 4 5 

2) Maintenant, parcourez la deuxième liste et attendez de voir un NULL, qui est votre sharepoint fusion.

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

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

  • itérer les deux listes et calculer les longueurs A et B
  • calculer la différence de longueur C = | AB |;
  • commencer à itérer les deux listes simultanément, mais append des étapes C supplémentaires sur la liste
  • ces deux pointeurs se rencontreront dans le sharepoint fusion

Cela viole sans doute la condition “parsingr chaque liste une seule fois”, mais implémente l’ algorithme tortue et hare (utilisé pour trouver le sharepoint fusion et la longueur de cycle d’une liste cyclique) pour commencer à la liste A et A la fin, vous faites semblant de pointer vers le début de la liste B, créant ainsi l’apparence d’une liste cyclique. L’algorithme vous dira alors exactement à quelle distance se trouve la fusion dans List A (la variable ‘mu’ selon la description de Wikipedia).

De plus, la valeur “lambda” vous indique la longueur de la liste B et, si vous le souhaitez, vous pouvez calculer la longueur de la liste A pendant l’algorithme (lorsque vous redirigez le lien NULL).

Voici une solution rapide sur le plan informatique (itère une fois chaque liste) 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 

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 sharepoint fusion 🙂

Peut-être que je suis en train de simplifier ceci, mais simplement itérer la plus petite liste et utiliser les derniers nœuds Link comme sharepoint fusion?

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

MODIFIER:

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

Vous avez une liste lié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 “plus petite” (comme la liste fusionnée, qui est ce que nous comptons, a une longueur de 4 et la liste principale 5)

Parcourez la première liste, conservez 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 des spécifications. Cependant, la première liste est analysée exactement une fois et la seconde liste ne sera entièrement analysée que s’il n’ya pas de sharepoint fusion. Sinon, il se terminera plus tôt (au sharepoint fusion).

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 

Notez que si j’avais aligné la structure du nœud, alors, lorsque malloc () est un nœud, l’adresse est alignée avec 16 octets, voir le moins de 4 bits. Les moindres bits sont des 0, c’est-à-dire 0x0 ou 000b. Donc, si vous êtes dans le même cas particulier (adresse de noeud alignée), vous pouvez utiliser ces 4 bits minimum. Par exemple, lorsque vous parcourez les deux listes de la tête vers la queue, définissez 1 ou 2 des 4 bits de l’adresse du nœud visiteur, c’est-à-dire définissez un indicateur;

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

Notez que les indicateurs ci-dessus n’affecteront pas l’adresse réelle du nœud mais uniquement la valeur de votre pointeur SAVED.

Une fois que quelqu’un a trouvé le bit de drapeau, le premier nœud trouvé doit être le sharepoint fusion. une fois 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 un nettoyage (par exemple node = node-> next). rappelez-vous que vous aviez défini des bits de drapeau, faites comme ça

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

Comme cette proposition restaure les adresses de nœud modifiées, elle pourrait être considérée comme “sans modification”.

cette solution ne réitère chaque liste qu’une seule fois … pas de modification de la liste requirejse aussi bien que vous puissiez vous plaindre de l’espace ..
1) Fondamentalement, vous effectuez une itération dans list1 et stockez l’adresse de chaque nœud dans un tableau (qui stocke la valeur int non signée)
2) Ensuite, vous parcourez list2, et pour chaque adresse de nœud —> vous recherchez dans le tableau que vous avez trouvé une correspondance ou non … si vous faites cela, c’est le 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 

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

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

Traverse la liste à la même vitesse et place le drapeau = 1 (indicateur visité) pour ce noeud,

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

Que dis-tu de ça:

  1. Si vous ne pouvez parcourir qu’une seule fois chaque liste, 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 seconde liste pour voir si un nœud pointe vers votre nouveau nœud ( c’est votre sharepoint fusion). Si la seconde traversée n’aboutit pas à votre nouveau nœud, les listes d’origine n’ont pas de sharepoint fusion.

  2. Si vous êtes autorisé à parcourir les listes plus d’une fois, vous pouvez parcourir chaque liste pour trouver leurs longueurs et, si elles sont différentes, omettre les nœuds “supplémentaires” au début de la liste plus longue. Il suffit ensuite de parcourir les deux listes une étape à la fois et de trouver le premier nœud de fusion.

Étapes en Java:

  1. Créer une carte
  2. Commencez à parcourir les deux twigs de la liste et mettez tous les nœuds de liste parcourus dans la carte en utilisant une chose unique liée aux nœuds (par exemple, identifiant de nœud) en tant que clé et mettez les valeurs à 1 dans le début pour tous.
  3. Lorsque la première clé dupliquée arrive, incrémentez la valeur de cette clé (supposons que sa valeur devienne 2, qui est> 1).
  4. Obtenez la clé dont la valeur est supérieure à 1 et qui devrait être le nœud où deux listes sont en train de fusionner.

Nous pouvons le résoudre efficacement en introduisant le champ “isVisited”. Traversez la première liste et définissez la valeur “isVisited” sur “true” pour tous les nœuds jusqu’à la fin. Commencez maintenant à partir de la seconde et trouvez le premier noeud où l’indicateur est vrai et Boom, c’est votre sharepoint fusion.

Etape 1: trouver la longueur de la liste Etape 2: trouver le diff et déplacer la plus grande liste avec la différence Etape 3: Maintenant, les deux listes seront dans la même position. Étape 4: Parcourez la liste pour trouver le sharepoint 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 
 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; } 

Utilisez Map ou Dictionary pour stocker l’adresse vs la valeur du noeud. Si l’adresse existe déjà dans la carte / dictionnaire, la valeur de la clé est la réponse. J’ai fait ça:

 int FindMergeNode(Node headA, Node headB) { Map map = new HashMap(); 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; } 

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 stacks, disons stck1 et stck2.
  2. Traverse 1st liste et pousse une copie de chaque nœud que vous traversez dans stck1.
  3. Identique à l’étape deux, mais cette fois-ci, parcourez la deuxième liste et poussez la copie des nœuds dans stck2.
  4. Maintenant, sortez des deux stacks et vérifiez si les deux nœuds sont égaux, si oui, conservez-leur une référence. Si non, les nœuds précédents qui étaient égaux sont en fait le sharepoint fusion que nous recherchions.

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, maintenant parcourir l’autre liste et 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 à aucune liste. La longueur est encore inconnue. Espace auxiliaire utilisé: O (n) où ‘n’ est la longueur de la première liste parcourue.