1#include <iostream>
2#include <utility>
3
4class Nodo { // Un nœud contient la donnée et les pointeurs vers le précédent et le suivant
5private: // Attributs privés : seules les méthodes y touchent
6 int dato_;
7 Nodo* anterior_;
8 Nodo* siguiente_;
9public:
10 Nodo(int dato, Nodo* anterior = nullptr, Nodo* siguiente = nullptr) {
11 dato_ = dato;
12 anterior_ = anterior;
13 siguiente_ = siguiente;
14 }
15 int getDato() const { return dato_; }
16 void setDato(int dato) { dato_ = dato; }
17 Nodo* getAnterior() const { return anterior_; }
18 void setAnterior(Nodo* anterior) { anterior_ = anterior; }
19 Nodo* getSiguiente() const { return siguiente_; }
20 void setSiguiente(Nodo* siguiente) { siguiente_ = siguiente; }
21};
22
23class Lista {
24private:
25 Nodo* cabeza_; // Premier nœud de la liste
26 Nodo* cola_; // Dernier nœud de la liste
27 int n_; // Nombre de nœuds
28
29public:
30 Lista() { // Constructeur par défaut : crée une liste vide
31 cabeza_ = nullptr;
32 cola_ = nullptr;
33 n_ = 0;
34 }
35
36 ~Lista() { // Destructeur : libère tous les nœuds
37 while (cabeza_ != nullptr) {
38 Nodo* borra = cabeza_;
39 cabeza_ = borra->getSiguiente();
40 delete borra;
41 }
42 cola_ = nullptr;
43 }
44
45 Lista(const Lista& origin) { // Constructeur de copie : fait une copie profonde
46 cabeza_ = nullptr;
47 cola_ = nullptr;
48 n_ = 0;
49 Nodo* aux = origin.cabeza_;
50 while (aux != nullptr) {
51 insertarAlFinal(aux->getDato()); // Insérer en queue conserve l'ordre d'origine
52 aux = aux->getSiguiente();
53 }
54 }
55
56 Lista& operator=(const Lista& origin) { // Affectation par copie : libère les anciens nœuds, puis copie
57 if (this != &origin) { // Protège contre l'auto-affectation
58 while (cabeza_ != nullptr) { // Libère les nœuds que la liste possède déjà
59 Nodo* borra = cabeza_;
60 cabeza_ = borra->getSiguiente();
61 delete borra;
62 }
63 cola_ = nullptr;
64 n_ = 0;
65 Nodo* aux = origin.cabeza_;
66 while (aux != nullptr) {
67 insertarAlFinal(aux->getDato());
68 aux = aux->getSiguiente();
69 }
70 }
71 return *this;
72 }
73
74 Lista(Lista&& origin) noexcept { // Constructeur de déplacement : prend les nœuds de la source
75 cabeza_ = origin.cabeza_;
76 cola_ = origin.cola_;
77 n_ = origin.n_;
78 origin.cabeza_ = nullptr; // Laisse la source vide
79 origin.cola_ = nullptr;
80 origin.n_ = 0;
81 }
82
83 Lista& operator=(Lista&& origin) noexcept { // Affectation par déplacement : libère les anciens nœuds, puis prend les nouveaux
84 if (this != &origin) { // Protège contre l'auto-affectation
85 while (cabeza_ != nullptr) { // Libère les nœuds que la liste possède déjà
86 Nodo* borra = cabeza_;
87 cabeza_ = borra->getSiguiente();
88 delete borra;
89 }
90 cola_ = nullptr;
91 cabeza_ = origin.cabeza_;
92 cola_ = origin.cola_;
93 n_ = origin.n_;
94 origin.cabeza_ = nullptr; // Laisse la source vide
95 origin.cola_ = nullptr;
96 origin.n_ = 0;
97 }
98 return *this;
99 }
100
101 int getTamano() const { return n_; }
102 Nodo* getCabeza() const { return cabeza_; }
103 Nodo* getCola() const { return cola_; }
104
105 void insertarElemento(int valor) { // Insertion en tête : O(1)
106 Nodo* nodo = new Nodo(valor);
107 n_ += 1;
108 nodo->setSiguiente(cabeza_);
109 if (cabeza_ != nullptr) {
110 cabeza_->setAnterior(nodo);
111 } else {
112 cola_ = nodo;
113 }
114 cabeza_ = nodo;
115 }
116
117 void insertarAlFinal(int valor) { // Insertion en queue : O(1) grâce à cola_
118 Nodo* nodo = new Nodo(valor);
119 n_ += 1;
120 nodo->setAnterior(cola_);
121 if (cola_ != nullptr) {
122 cola_->setSiguiente(nodo);
123 } else {
124 cabeza_ = nodo;
125 }
126 cola_ = nodo;
127 }
128
129 void imprimirLista() const { // Affiche de la tête à la queue
130 Nodo* actual = cabeza_;
131 while (actual != nullptr) {
132 std::cout << actual->getDato() << " ";
133 actual = actual->getSiguiente();
134 }
135 std::cout << std::endl;
136 }
137
138 void imprimirListaAtras() const { // Affiche de la queue à la tête
139 Nodo* actual = cola_;
140 while (actual != nullptr) {
141 std::cout << actual->getDato() << " ";
142 actual = actual->getAnterior();
143 }
144 std::cout << std::endl;
145 }
146
147 void eliminarNodo(int indice) { // Supprime le nœud à l'indice donné
148 if (indice < 0 || indice >= n_) return; // Indice invalide : rien ne se passe
149 Nodo* eliminar = cabeza_;
150 for (int i = 0; i < indice; i++) {
151 eliminar = eliminar->getSiguiente();
152 }
153 if (eliminar->getAnterior() != nullptr) {
154 eliminar->getAnterior()->setSiguiente(eliminar->getSiguiente());
155 } else {
156 cabeza_ = eliminar->getSiguiente();
157 }
158 if (eliminar->getSiguiente() != nullptr) {
159 eliminar->getSiguiente()->setAnterior(eliminar->getAnterior());
160 } else {
161 cola_ = eliminar->getAnterior();
162 }
163 delete eliminar;
164 n_--;
165 }
166
167 void invertirLista() { // Inverse la liste sur place : chaque nœud échange anterior_ et siguiente_
168 Nodo* actual = cabeza_;
169 Nodo* temporal = nullptr;
170 while (actual != nullptr) {
171 temporal = actual->getAnterior();
172 actual->setAnterior(actual->getSiguiente());
173 actual->setSiguiente(temporal);
174 actual = actual->getAnterior(); // anterior_ est maintenant l'ancien siguiente_
175 }
176 temporal = cabeza_; // Enfin, on échange la tête et la queue
177 cabeza_ = cola_;
178 cola_ = temporal;
179 }
180};
181
182int main() {
183 Lista l1;
184 l1.insertarElemento(10);
185 l1.insertarElemento(20);
186 l1.insertarAlFinal(5);
187 l1.insertarElemento(30);
188
189 Lista l2;
190 l2.insertarElemento(100);
191
192 l2 = l1; // Affectation par copie (sans swap)
193 l2.imprimirLista();
194 l2.imprimirListaAtras();
195
196 Lista l3;
197 l3 = std::move(l1); // Affectation par déplacement (sans swap)
198 l3.invertirLista();
199 l3.imprimirLista();
200
201 return 0;
202}