1#include <iostream>
2#include <utility>
3
4class Nodo { // Um nó guarda o dado e os ponteiros para o anterior e o próximo
5private: // Atributos privados: só os métodos acessam
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_; // Primeiro nó da lista
26 Nodo* cola_; // Último nó da lista
27 int n_; // Número de nós
28
29public:
30 Lista() { // Construtor padrão: cria uma lista vazia
31 cabeza_ = nullptr;
32 cola_ = nullptr;
33 n_ = 0;
34 }
35
36 ~Lista() { // Destrutor: libera todos os nós
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) { // Construtor de cópia: faz uma cópia profunda
46 cabeza_ = nullptr;
47 cola_ = nullptr;
48 n_ = 0;
49 Nodo* aux = origin.cabeza_;
50 while (aux != nullptr) {
51 insertarAlFinal(aux->getDato()); // Inserir no fim mantém a ordem original
52 aux = aux->getSiguiente();
53 }
54 }
55
56 Lista& operator=(const Lista& origin) { // Atribuição por cópia: libera os nós antigos e depois copia
57 if (this != &origin) { // Protege contra a autoatribuição
58 while (cabeza_ != nullptr) { // Libera os nós que a lista já tinha
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 { // Construtor de movimentação: toma os nós da origem
75 cabeza_ = origin.cabeza_;
76 cola_ = origin.cola_;
77 n_ = origin.n_;
78 origin.cabeza_ = nullptr; // Deixa a origem vazia
79 origin.cola_ = nullptr;
80 origin.n_ = 0;
81 }
82
83 Lista& operator=(Lista&& origin) noexcept { // Atribuição por movimentação: libera os nós antigos e depois toma os novos
84 if (this != &origin) { // Protege contra a autoatribuição
85 while (cabeza_ != nullptr) { // Libera os nós que a lista já tinha
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; // Deixa a origem vazia
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) { // Inserir no início: 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) { // Inserir no fim: O(1) graças 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 da cabeça à cauda
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 da cauda à cabeça
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) { // Remove o nó da posição indicada
148 if (indice < 0 || indice >= n_) return; // Índice inválido: nada acontece
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() { // Inverte a lista no lugar: cada nó troca anterior_ e 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_ agora é o antigo siguiente_
175 }
176 temporal = cabeza_; // Por fim, troca-se a cabeça e a cauda
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; // Atribuição por cópia (sem swap)
193 l2.imprimirLista();
194 l2.imprimirListaAtras();
195
196 Lista l3;
197 l3 = std::move(l1); // Atribuição por movimentação (sem swap)
198 l3.invertirLista();
199 l3.imprimirLista();
200
201 return 0;
202}