web-dev-qa-db-fra.com

Fusion de deux listes chaînées triées

C’est l’une des questions de programmation posées lors du test écrit de Microsoft. Je donne la question et la réponse que je suis venu avec. La chose est ma réponse bien que semble complète (au moins pour moi), je pense que le nombre de lignes peut être réduit. Cela a été demandé en C et je suis une personne de Java mais j'ai réussi à le coder (ma réponse peut contenir trop de syntaxes semblables à Java)

Ok, voici la question.

Vous avez deux listes déjà triées , Vous devez les fusionner et Renvoyer une nouvelle liste sans nouveaux nœuds Supplémentaires. La liste renvoyée devrait également être Triée. 

La signature de la méthode est,

Node* MergeLists(Node* list1, Node* list2);

struct Node{
    int data;
    Node *next;
}

Voici la solution que j'ai trouvée,

Node* MergeLists(Node* list1, Node* list2){
    Node* mergedList;
    if(list1 == null && list2 ==null){//if both are null, return null
        return null;
    }
    if(list1 == null){//if list1 is null, simply return list2
        return list2;
    }
    if(list2 == null){//if list2 is null, simply return list1
        return list1;
    }
    if(list1.data < list2.data){//initialize mergedList pointer to list1 if list1's data is lesser
        mergedList = list1;
    }else{//initialize mergedList pointer to list2 if list2's data is lesser or equal
        mergedList = list2;
    }
    while(list1!=null && list2!=null){
        if(list1.data < list2.data){
            mergedList->next = list1;
            list1 = list1->next;
        }else{
            mergedList->next = list2;
            list2 = list2->next;
        }
    }
    if(list1 == null){//remaining nodes of list2 appended to mergedList when list1 has reached its end.
        mergedList->next = list2;
    }else{//remaining nodes of list1 appended to mergedList when list2 has reached its end
        mergedList->next = list1;
    }
    return mergedList;
}

Je suis très confiant que cela peut être amélioré. Aidez-moi, s'il vous plaît, à trouver les lignes redondantes que j'ai ajoutées. N'hésitez pas à critiquer mes erreurs de syntaxe et ma logique.

Merci!

23
bragboy

Votre code est surchargé avec if- s inséré pour traiter les cas "spéciaux", ce qui le gonfle beaucoup et le rend difficile à lire. Cela se produit généralement lorsque vous décidez de gérer des cas particuliers "par code" au lieu de trouver un moyen de les gérer "par données". Selon une déclaration attribuée à David Wheeler, "Tous les problèmes informatiques peuvent être résolus par un autre niveau d'indirection". Ce "niveau supplémentaire d'indirection" fonctionne généralement très bien avec les listes, ce qui permet de réduire considérablement l'encombrement créé par ces ifs.

Pour illustrer ce qui précède, voici à quoi ressemblerait mon code

#define SWAP_PTRS(a, b) do { void *t = (a); (a) = (b); (b) = t; } while (0)

Node* MergeLists(Node* list1, Node* list2) 
{
  Node *list = NULL, **pnext = &list;

  if (list2 == NULL)
    return list1;

  while (list1 != NULL)
  {
    if (list1->data > list2->data)
      SWAP_PTRS(list1, list2);

    *pnext = list1;
    pnext = &list1->next;
    list1 = *pnext;
  }

  *pnext = list2;
  return list;
}

Certains pourraient soutenir que l'utilisation d'un niveau supplémentaire d'indirection dans le pointeur pnext rend en réalité le code plus difficile à lire. Je conviens que pour un débutant, l’approche peut poser quelques difficultés, mais pour un programmeur expérimenté, cela devrait être facilement assimilable comme un idiome.

13
AnT

Le bogue le plus criant est dans votre boucle, vous écrasez de plus en plus mergedList-> next, en perdant le noeud ajouté précédemment. C'est-à-dire que votre liste renvoyée ne contiendra jamais plus de deux nœuds, quelle que soit l'entrée ...

Cela fait longtemps que je n'ai pas C, mais je le ferais comme suit:

Node* merge(Node* list1, Node* list2) {
    Node* merged = null;
    Node** tail = &merged;

    while (list1 && list2) {
        if (list1->data < list2->data) {
            *tail = list1;
            list1 = list1->next;
        } else {
            *tail = list2;
            list2 = list2->next;
        }
        tail = &((*tail)->next);
    }
    *tail = list1 ? list1 : list2;
    return merged;
}
19
meriton

Ma prise, avec un cas de test

Jusqu'à présent, toutes les réponses ont été intéressantes et bien faites. Il est possible que celui-ci ressemble davantage à ce qu'un intervieweur aimerait voir, avec DRY/DIE et TDD. :-)

#include <stdio.h>

typedef struct ns {
    int data;
    struct ns *next;
} Node;

Node l1[] = {
  { 1, &l1[1] },
  { 3, &l1[2] },
  { 5, &l1[3] },
  { 7, &l1[4] },
  { 9, &l1[5] },
  {11, 0 },
};

Node l2[] = {
  { 2, &l2[1] },
  { 4, &l2[2] },
  { 6, 0 },
};

Node* MergeLists(Node* list1, Node* list2) {
  Node *t, **xt;
  for(xt = &t; list1 || list2;) {
    Node **z = list1 == NULL ? &list2 :
               list2 == NULL ? &list1 :
               list1->data < list2->data ? &list1 : &list2;
    *xt = *z;
     xt = &(*z)->next;
    *z  = *xt;
  }
  *xt = NULL;
  return t;
}

int main(void) {
  for(Node *t = MergeLists(l1, l2); t; t = t->next) 
    printf("%d\n", t->data);
  return 0;
}
9
DigitalRoss

Ça ne devient pas plus élégant que ça:

Node* merge2(Node* n1, Node* n2) {
    n1->next = merge(n1->next, n2);
    return n1;
}

Node* merge(Node* n1, Node* n2) {
    return (n1 == null) ? n2 :
           (n2 == null) ? n1 :
           (n1->data < n2->data) ?
               merge2(n1, n2) :
               merge2(n2, n1);
}

En supposant que vous compreniez la récursivité, cela est aussi clair que possible.


Je devrais souligner que cela n’est utile que pour une réponse à une interview (dans le cas où la démonstration de la clarté de la pensée a probablement plus d’impact que la simple démonstration du fait que vous savez écrire des programmes). En pratique, vous ne voudriez pas fusionner de cette façon, car il utilise la profondeur de pile O(n), ce qui provoquerait probablement un débordement de pile. En outre, il ne s'agit pas d'une récursion finale, elle n'est donc pas optimisable par le compilateur.

5
polygenelubricants

Divide et Impera

(c'est-à-dire MergeSort )

4
Luca

Donc, en fusionnant polygen et AndreyT, nous obtenons:

Node* merge(Node* n1, Node* n2) {
    return (n1 == null) ? n2 :
           (n2 == null) ? n1 :
             (n1->data < n2->data) ? 
               (n1->next = merge(n1->next, n2), n1) : 
               (n2->next = merge(n2->next, n1), n2)}

Je ne peux pas prétendre à un crédit pour celui-ci, mais il est le plus concis et montre la symétrie entre les deux arguments, sans introduire de fonctions auxiliaires obscures. Je ne suis pas sûr qu'un compilateur optimiseur verra une récursion de queue ici, mais c'est ce que je fais. L'indentation est une touche finale.

2
piccolbo

Utilisez la récursivité. Le code est comme suit:

ListNode* mergeTwoSortedLists(ListNode* pHead1, ListNode* pHead2)
{
    if(pHead1 == NULL)
        return pHead2;
    else if(pHead2 == NULL)
        return pHead1;

    ListNode* pMergedHead = NULL;

    if(pHead1->m_nValue < pHead2->m_nValue)
    {
        pMergedHead = pHead1;
        pMergedHead->m_pNext = mergeTwoSortedLists(pHead1->m_pNext, pHead2);
    }
    else
    {
        pMergedHead = pHead2;
        pMergedHead->m_pNext = mergeTwoSortedLists(pHead1, pHead2->m_pNext);
    }

    return pMergedHead;
}
2
herohuyongtao

Ceci est ma prise. Contrairement à d'autres solutions, il identifie et ignore les nœuds consécutifs d'une liste qui sont plus petits ou égaux au nœud de tête de l'autre liste. La tête de l'autre liste est jointe à la fin de cette séquence et le processus est répété après le changement de rôle. Cette approche minimise le nombre d'assignations dans Node.next tout en limitant le test NULL à une vérification unique à chaque itération.

Node * merge(Node *list1, Node *list2)
{
    if (!list1) return list2;
    if (!list2) return list1;

    Node *tmp;

    // compare head nodes and swap lists to guarantee list1 has the smallest node
    if (list1->val > list2->val) {
        tmp = list2;
        list2 = list1;
        list1 = tmp;
    }

    Node *tail = list1;

    do {
        // Advance the tail pointer skipping over all the elements in the result
        // which have smaller or equal value than the first node on list2
        while (tail->next && (tail->next->val <= list2->val)) {
            tail = tail->next;
        }
        // concat list2 at tail of result and make the rest after tail list2 
        tmp = tail->next;
        tail->next = list2;
        tail = list2;
        list2 = tmp;
    } while (list2);

    return list1;
}
0
Ronen

Vous pouvez utiliser la récursivité:

Node* MergeLists(Node *headA, Node* headB)
{

if(headA==NULL){
    return headB;
}else if(headB ==NULL){
    return headA;
}
Node* head = NULL;
if(headA->data <= headB->data){
    head= headA;
    head->next = MergeLists(headA->next,headB);
}else{
    head= headB;
    head->next = MergeLists(headA,headB->next);
}
 return head;
}
0
Abhishek Kshirsagar
#include<stdio.h>

typedef struct NODE
{
    int data;
    struct NODE * next;
}NODE;

NODE * func(NODE*,NODE*);
int main()
{
    int i;
    int size;
    int value;
    NODE * head1,*head2,*newNode,*ptr,*final;
    printf("\nPlease enter the number of elements\n");
    scanf("%d",&size);

    for(i=0;i<size;i++)
    {
            printf("List 1\n");
            printf("Please enter the value number %d \n",i+1);
            scanf("%d",&value);
            newNode=(NODE*)malloc(sizeof(NODE));
            newNode->data=value;
            newNode->next=NULL;
            if(i!=0)
            {
                ptr->next=newNode;  
                ptr=ptr->next;
            }

            if(i==0)
            {
                head1=newNode;
                ptr=newNode;

            }
    }
    for(i=0;i<size;i++)
    {
            printf("\n\nList 2\n");
            printf("Please enter the value number %d \n",i+1);
            scanf("%d",&value);
            newNode=(NODE*)malloc(sizeof(NODE));
            newNode->data=value;
            newNode->next=NULL;
            if(i!=0)
            {
                ptr->next=newNode;  
                ptr=ptr->next;
            }

            if(i==0)
            {
                head2=newNode;
                ptr=newNode;

            }
    }

    final=func(head1,head2);
    printf("\n\n");
    while (final!=NULL)
    {
        printf("%d -->",final->data);
        final=final->next;
    }
    printf("NULL
    ");
    return 0;
}

NODE* func(NODE* list1, NODE* list2)
{

    NODE* mergedList,*mergeHead=NULL;
    if(list1 == NULL && list2 ==NULL){//if both are NULL, return NULL
        return NULL;
    }
    if(list1 == NULL){//if list1 is NULL, simply return list2
        return list2;
    }
    if(list2 == NULL){//if list2 is NULL, simply return list1
        return list1;
    }
    mergedList = (NODE*)malloc(sizeof(NODE));
    if(list1->data < list2->data){//initialize mergedList pointer to list1 if list1's data is lesser

        mergedList->data=list1->data;
        mergedList->next=NULL;
        list1 = list1->next;

    }else{//initialize mergedList pointer to list2 if list2's data is lesser or equal
        mergedList->data=list2->data;
        mergedList->next=NULL;
        list2 = list2->next;

    }
    mergeHead=mergedList;

    while(list1!=NULL && list2!=NULL){
        if(list1->data < list2->data){
            mergedList->next = (NODE*)malloc(sizeof(NODE));
            mergedList=mergedList->next;
            mergedList->data=list1->data;
            mergedList->next=NULL;
            list1 = list1->next;
        }else{
            mergedList->next = (NODE*)malloc(sizeof(NODE));
            mergedList=mergedList->next;
            mergedList->data=list2->data;
            mergedList->next=NULL;
            list2 = list2->next;
        }
    }
    if(list1 == NULL){//remaining nodes of list2 appended to mergedList when list1 has reached its end.
       while(list2!=NULL)
       {
            mergedList->next = (NODE*)malloc(sizeof(NODE));
            mergedList=mergedList->next;
            mergedList->data=list2->data;
            mergedList->next=NULL;
            list2 = list2->next;
       }

    }else{//remaining nodes of list1 appended to mergedList when list2 has reached its end
            mergedList->next = (NODE*)malloc(sizeof(NODE));
            mergedList=mergedList->next;
            mergedList->data=list1->data;
            mergedList->next=NULL;
            list1 = list1->next;
    }
    return mergeHead;
}
0
Abhishek Jadhav
public void Merge(LinkList list1, LinkList list2) {
        if (list1.head == null && list2.head == null) {
            System.out.println("Empty list"); //Check if lists are empty
        }
        if (list1.head == null) { //If list1 is empty print list2
            list2.printList();
        }
        if (list2.head == null) { //If list2 is empty print list1
            list1.printList(); 
        }
        LinkList list3 = new LinkList(); //create a new LinkList object for merging
        Node a = list1.head; //Beginning of list1
        Node b = list2.head; //Beginning of list2
        while (a != null && b != null) { //Until list ends
            if (a.value <= b.value) { //Compare values of list1 against list2
                list3.insert(a.value); //Insert values to new list
                a = a.next;
            } else if (a.value >= b.value) {
                list3.insert(b.value);
                b = b.next;
            }  else if (a.value == b.value){ //Insert only unique values and discard repeated values
            list3.insert(a.value);
            a = a.next;
            b = b.next;
        }
        }
        if (a == null) {
            while (b != null) {
                list3.insert(b.value); //If list1 becomes empty, attach rest of the list2 to list3
                b = b.next;
            }
        }
        if (b == null) {
            while (a != null) {
                list3.insert(a.value);
                a = a.next;
            }
        }
        list3.printList(); //print merged list
    }
}

Salut les gars ! Je me préparais pour une entrevue ce mois-ci et pendant que je travaillais sur ce problème, voici la solution que j'ai proposée. J'ai comparé ma solution à de nombreuses solutions que vous avez publiées ici et trouve mon programme extrêmement long. Bien que je trouve cela plus facile à comprendre et à mettre en œuvre, existe-t-il une meilleure solution en Java pour le code existant. Je cherche une meilleure solution de complexité temporelle. Toute aide/direction/astuce est appréciée.

PS: Je n'ai pas pu trouver de solution Java pour les programmes listés ci-dessus en C qui utilisaient des pointeurs.

0
Naveen

Vous pouvez utiliser Java 8, API Stream pour fusionner, distinguer et trier. Vous trouverez ci-dessous un exemple de code pour le tri et la fusion de deux listes avec des éléments Integer.

List<Integer> list1= Arrays.asList(2,3,5,8);
List<Integer> list2 = Arrays.asList(3,6,8);

List<Integer> finalList = new ArrayList<>();
finalList.addAll(list1);
finalList.addAll(list2);

List<Integer>  mergeSortedList = 
  finalList.stream()
    .distinct()
    .sorted()
    .collect(Collectors.toList());
System.out.println(mergeSortedList);
0
Rupesh Kumar

J'ai créé une fonction de récursivité pour elle. Voici ma solution:

Node* merge_recursion(Node* l1, Node* l2)
{
        if (!l1)
                return l2;
        if (!l2)
                return l1;

        if (l1->data < l2->data) {
                l1->next = merge_recursion(l1->next, l2);
                return l1;
        } else {
                l2->next = merge_recursion(l1, l2->next);
                return l2;
        }
}

Stocker le pointeur de retour dans une nouvelle variable de pointeur (dans main ()/fonction appelante) et parcourir la liste chaînée sur le nouveau pointeur pour imprimer les données; il en résulte une liste chaînée fusionnée triée.

0
Brijesh Valera