1#include <iostream>
2#include <utility>
3
4class Nodo { // Un nodo guarda el dato y los punteros al anterior y al siguiente
5private: // Atributos privados: solo se tocan con los métodos
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_; // Primer nodo de la lista
26 Nodo* cola_; // Último nodo de la lista
27 int n_; // Número de nodos
28
29public:
30 Lista() { // Constructor por defecto: crea una lista vacía
31 cabeza_ = nullptr;
32 cola_ = nullptr;
33 n_ = 0;
34 }
35
36 ~Lista() { // Destructor: libera todos los nodos
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) { // Constructor de copia: hace una copia profunda
46 cabeza_ = nullptr;
47 cola_ = nullptr;
48 n_ = 0;
49 Nodo* aux = origin.cabeza_;
50 while (aux != nullptr) {
51 insertarAlFinal(aux->getDato()); // Insertar por la cola conserva el orden original
52 aux = aux->getSiguiente();
53 }
54 }
55
56 Lista& operator=(const Lista& origin) { // Asignación por copia: libera los nodos antiguos y luego copia
57 if (this != &origin) { // Protege contra la autoasignación
58 while (cabeza_ != nullptr) { // Libera los nodos que ya tenía la lista
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 { // Constructor de movimiento: roba los nodos del origen
75 cabeza_ = origin.cabeza_;
76 cola_ = origin.cola_;
77 n_ = origin.n_;
78 origin.cabeza_ = nullptr; // Deja el origen vacío
79 origin.cola_ = nullptr;
80 origin.n_ = 0;
81 }
82
83 Lista& operator=(Lista&& origin) noexcept { // Asignación por movimiento: libera los nodos antiguos y roba los nuevos
84 if (this != &origin) { // Protege contra la autoasignación
85 while (cabeza_ != nullptr) { // Libera los nodos que ya tenía la lista
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; // Deja el origen vacío
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) { // Inserta al inicio: 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) { // Inserta al final: O(1) gracias a 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 { // Imprime de la cabeza a la cola
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 { // Imprime de la cola a la cabeza
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) { // Elimina el nodo de la posición indicada
148 if (indice < 0 || indice >= n_) return; // Índice no válido: no hace nada
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() { // Invierte la lista in situ: cada nodo intercambia anterior_ y 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(); // El anterior_ ya es el antiguo siguiente_
175 }
176 temporal = cabeza_; // Por último se intercambian la cabeza y la cola
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; // Asignación por copia (sin swap)
193 l2.imprimirLista();
194 l2.imprimirListaAtras();
195
196 Lista l3;
197 l3 = std::move(l1); // Asignación por movimiento (sin swap)
198 l3.invertirLista();
199 l3.imprimirLista();
200
201 return 0;
202}