Table des matières
1. Fusionner deux listes chaînées ordonnées
1. Fusionner deux listes chaînées ordonnées
1. Récursion
Idées
Nous pouvons définir de manière récursive l'opération de fusion dans deux listes chaînées comme suit (en ignorant les cas extrêmes, tels que les listes chaînées vides, etc.) :
list1[0]+merge(list1[1:],list2) list1[0]<list2[0]
list2[0]+merge(list1,list2[1:]) sinon
En d'autres termes
, deux têtes de liste liées. le nœud avec la valeur la plus petite est fusionné avec le résultat de l’opération de fusion des éléments restants.
algorithme
Nous modélisons directement le processus récursif ci-dessus et nous devons considérer les cas limites.
Si l1 ou l2 est une liste chaînée vide depuis le début, alors aucune opération n'a besoin d'être fusionnée, nous avons donc seulement besoin de renvoyer une liste chaînée non vide. Sinon, nous devons déterminer quel nœud principal de la liste chaînée a une valeur plus petite, l1 ou l2, puis déterminer de manière récursive le nœud suivant à ajouter au résultat. Si l'une des deux listes chaînées est vide, la récursion se termine.
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == nullptr) {
return l2;
} else if (l2 == nullptr) {
return l1;
} else if (l1->val < l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
} else {
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
};
Analyse de complexité
Complexité temporelle : O(n+m), où n et m sont respectivement les longueurs des deux listes chaînées. Étant donné que chaque appel récursif supprimera le nœud principal de l1 ou l2 (jusqu'à ce qu'au moins une liste chaînée soit vide), la fonction mergeTwoList n'appellera chaque nœud de manière récursive qu'une fois au maximum. Par conséquent, la complexité temporelle dépend de la longueur de la liste chaînée fusionnée, qui est O(n+m).
Complexité spatiale : O(n+m), où nn et mm sont respectivement les longueurs des deux listes chaînées. L'appel récursif de la fonction mergeTwoLists nécessite de l'espace de pile. La taille de l'espace de pile dépend de la profondeur de l'appel récursif. À la fin de l’appel récursif, la fonction mergeTwoLists peut être appelée au plus n+m fois, donc la complexité spatiale est O(n+m).
2. Itération
Idées
Nous pouvons implémenter l'algorithme ci-dessus en utilisant une méthode itérative. Lorsque ni l1 ni l2 ne sont une liste chaînée vide, déterminez quel nœud principal de la liste chaînée a une valeur plus petite et ajoutez le nœud avec la valeur la plus petite au résultat. Lorsqu'un nœud est ajouté au résultat, ajoutez la liste chaînée correspondante au résultat. Le nœud est déplacé vers l'arrière d'une position.
algorithme
Tout d’abord, nous définissons un pré-en-tête de nœud sentinelle, qui nous permet de renvoyer plus facilement la liste chaînée fusionnée à la fin. Nous maintenons un pointeur précédent et nous devons ajuster son pointeur suivant. Ensuite, on répète le processus suivant jusqu'à ce que l1 ou l2 pointe vers null : si la valeur du nœud courant de l1 est inférieure ou égale à l2, on connecte le nœud courant de l1 à l'arrière du nœud précédent et on déplace le l1. pointeur en arrière un peu. Sinon, on fait la même chose pour l2. Quel que soit l'élément que nous connectons à l'arrière, nous devons reculer le précédent.
Lorsque la boucle se termine, au moins l’un des l1 et l2 n’est pas vide. Étant donné que les deux listes chaînées d'entrée sont toutes deux ordonnées, quelle que soit la liste chaînée qui n'est pas vide, tous les éléments qu'elle contient sont plus grands que tous les éléments des listes chaînées précédemment fusionnées. Cela signifie que nous ajoutons simplement la liste chaînée non vide à la liste fusionnée et renvoyons la liste fusionnée.
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* preHead = new ListNode(-1);
ListNode* prev = preHead;
while (l1 != nullptr && l2 != nullptr) {
if (l1->val < l2->val) {
prev->next = l1;
l1 = l1->next;
} else {
prev->next = l2;
l2 = l2->next;
}
prev = prev->next;
}
// 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
prev->next = l1 == nullptr ? l2 : l1;
return preHead->next;
}
};
Analyse de complexité
Complexité temporelle : O(n+m), où n et m sont respectivement les longueurs des deux listes chaînées. Étant donné qu'un seul élément de l1 et l2 sera placé dans la liste chaînée fusionnée à chaque itération de boucle, le nombre de boucles while ne dépassera pas la somme des longueurs des deux listes chaînées. Toutes les autres opérations ont une complexité temporelle constante, donc la complexité temporelle totale est O(n+m).
Complexité spatiale : O(1). Nous n'avons besoin que d'un espace constant pour stocker plusieurs variables.