python-ed-fcad-uner 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. data_structures/__init__.py +6 -0
  2. data_structures/graphs/__init__.py +3 -0
  3. data_structures/graphs/edge.py +57 -0
  4. data_structures/graphs/graph.py +343 -0
  5. data_structures/graphs/vertex.py +28 -0
  6. data_structures/linear/__init__.py +4 -0
  7. data_structures/linear/list_node.py +9 -0
  8. data_structures/linear/lists/__init__.py +1 -0
  9. data_structures/linear/lists/linked_list.py +226 -0
  10. data_structures/linear/queues/__init__.py +2 -0
  11. data_structures/linear/queues/array_queue.py +97 -0
  12. data_structures/linear/queues/linked_queue.py +114 -0
  13. data_structures/linear/stacks/__init__.py +2 -0
  14. data_structures/linear/stacks/array_stack.py +65 -0
  15. data_structures/linear/stacks/array_stack_generics.py +64 -0
  16. data_structures/linear/stacks/linked_stack.py +88 -0
  17. data_structures/linear/util.py +8 -0
  18. data_structures/maps_dicts_hash/__init__.py +4 -0
  19. data_structures/maps_dicts_hash/chain_hashmap.py +114 -0
  20. data_structures/maps_dicts_hash/hashmap_base.py +148 -0
  21. data_structures/maps_dicts_hash/map_base.py +53 -0
  22. data_structures/maps_dicts_hash/probe_hashmap.py +123 -0
  23. data_structures/maps_dicts_hash/unsorted_table_map.py +105 -0
  24. data_structures/priority_queues/__init__.py +2 -0
  25. data_structures/priority_queues/array_heap.py +192 -0
  26. data_structures/priority_queues/priority_queue_base.py +50 -0
  27. data_structures/search_trees/__init__.py +1 -0
  28. data_structures/search_trees/linked_binary_search_tree.py +87 -0
  29. data_structures/trees/__init__.py +2 -0
  30. data_structures/trees/binary_tree_node.py +47 -0
  31. data_structures/trees/linked_binary_tree.py +403 -0
  32. python_ed_fcad_uner-0.1.0.dist-info/METADATA +34 -0
  33. python_ed_fcad_uner-0.1.0.dist-info/RECORD +35 -0
  34. python_ed_fcad_uner-0.1.0.dist-info/WHEEL +4 -0
  35. python_ed_fcad_uner-0.1.0.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,6 @@
1
+ from .linear import ArrayStack, LinkedStack, ArrayQueue, LinkedQueue, LinkedList, ListNode
2
+ from .trees import BinaryTreeNode, LinkedBinaryTree
3
+ from .priority_queues import ArrayHeap, PriorityQueueBase
4
+ from .maps_dicts_hash import MapBase, UnsortedTableMap, ChainHashMap, ProbeHashMap
5
+ from .search_trees import LinkedBinarySearchTree
6
+ from .graphs import Vertex, Edge, Graph
@@ -0,0 +1,3 @@
1
+ from .vertex import Vertex
2
+ from .edge import Edge
3
+ from .graph import Graph
@@ -0,0 +1,57 @@
1
+ from typing import Any, Tuple
2
+ from .vertex import Vertex
3
+
4
+ class Edge:
5
+ """ Representa la arista de un grafo. """
6
+ __slots__ = '_origin', '_destination', '_element'
7
+
8
+ def __init__(self, u: Vertex, v: Vertex, x: Any) -> None:
9
+ self._origin = u
10
+ self._destination = v
11
+ self._element = x
12
+
13
+ @property
14
+ def element(self) -> Any:
15
+ return self._element
16
+
17
+ @element.setter
18
+ def element(self, value: Any) -> None:
19
+ self._element = value
20
+
21
+ def __repr__(self) -> str:
22
+ return str(self)
23
+
24
+ def __str__(self) -> str:
25
+ return f"origin: {self._origin} - destination: {self._destination}, element: {self._element}"
26
+
27
+ def __eq__(self) -> bool:
28
+ return (self._origin, self._destination) == (self._origin, self._destination)
29
+
30
+ def __hash__(self) -> int:
31
+ return hash((self._origin, self._destination))
32
+
33
+ def endpoints(self) -> Tuple[Vertex, Vertex]:
34
+ """ Devuelve una tupla formada por los vértices
35
+ que conecta esta arista.
36
+
37
+ Returns:
38
+ Tuple[Vertex, Vertex]: Devuelve el vértice origen y destino
39
+ en ese orden como una tupla.
40
+ """
41
+ return (self._origin, self._destination)
42
+
43
+ def opposite(self, v: Vertex) -> Vertex:
44
+ """ Devuelve el vértice correspondiente al otro extremo de la arista.
45
+
46
+ Args:
47
+ v (Vertex): Si v es el vértice destino, devuelve el de origen.
48
+ Caso contrario devuelve el de destino.
49
+
50
+ Returns:
51
+ Vertex: vértice al otro extremo de la arista.
52
+ """
53
+ return self._destination if v is self._origin else self._origin
54
+
55
+
56
+
57
+
@@ -0,0 +1,343 @@
1
+ from typing import Any, List, Dict, Set, Generator
2
+ from .vertex import Vertex
3
+ from .edge import Edge
4
+
5
+
6
+ class Graph:
7
+ """ Implementación de un grafo simple utilizando mapeo de adyacencia."""
8
+
9
+ #########################################################################
10
+ # MÉTODOS PÚBLICOS
11
+ #########################################################################
12
+
13
+ def __init__(self, is_directed: bool) -> None:
14
+ """ Configura una nueva instancia de Graph con dos diccionarios Python.
15
+ Si el grafo es dirigido los dos diccionarios serán diferentes,
16
+ caso contrario uno es un alias del otro. Es decir, son referencias al
17
+ mismo objeto diccionario.
18
+
19
+ Args:
20
+ is_directed (bool): Indica si el grafo es dirigido o no.
21
+ """
22
+ self._outgoing: Dict[Vertex, Dict[Vertex, Edge]] = {}
23
+ self._incoming: Dict[Vertex, Dict[Vertex, Edge]] = {} if is_directed else self._outgoing
24
+
25
+ def __str__(self) -> str:
26
+ """Concatena en un string todos los datos de vértices y arcos.
27
+
28
+ Returns:
29
+ str: concatenación resultante de analizar todos los vértices y arcos.
30
+ Si el grafo está vacío entonces retorna el string Graph()
31
+ """
32
+ if not self.vertex_count():
33
+ return "Graph()"
34
+
35
+ res = ""
36
+
37
+ for v in self._outgoing.keys():
38
+ res += f"\n -> {str(v)}: { [str(edge) for edge in self._outgoing[v].values()] }" + "\n"
39
+
40
+ return f"Graph():{res}"
41
+
42
+ def is_directed(self) -> bool:
43
+ """ Indica si el grafo es dirigido o no.
44
+
45
+ Returns:
46
+ bool: True si _incoming referencia al mismo objeto que referencia _outgoing.
47
+ """
48
+ return self._incoming is not self._outgoing
49
+
50
+ def vertex_count(self) -> int:
51
+ """ Devuelve la cantidad de vértices del grafo.
52
+
53
+ Returns:
54
+ int: devuelve la cantidad de vértices del grafo.
55
+ """
56
+ return len(self._outgoing.keys())
57
+
58
+ def vertices(self) -> List[Any]:
59
+ """ Devuelve una lista con todos los vértices del grafo.
60
+
61
+ Returns:
62
+ List[Any]: Lista formada por todos los vértices del grafo.
63
+ """
64
+ return [key for key in self._outgoing.keys()]
65
+
66
+ def edge_count(self) -> int:
67
+ """ Devuelve la cantidad de aristas del grafo.
68
+
69
+ Returns:
70
+ int: devuelve la cantidad de aristas del grafo.
71
+ """
72
+ # Suma la cantidad de items en el diccionario outgoing para cada uno de los vértices.
73
+ total = sum(len(self._outgoing[v]) for v in self._outgoing)
74
+ # Si el grafo es dirigido entonces devuelve el total calculado en la línea anterior.
75
+ # En caso que el grafo sea no dirigido, nos aseguramos de no contar dos veces los vértices.
76
+ return total if self.is_directed() else total // 2
77
+
78
+ def edges(self) -> Set[Edge]:
79
+ """ Devuelve todas las aristas del grafo.
80
+
81
+ Returns:
82
+ Set[Edge]: Evita la repetición de aristas utilizando un conjunto.
83
+ """
84
+ # En un grafo no dirigido evita informar dos veces los aristas.
85
+ result: Set[Edge] = set()
86
+
87
+ # Para cada uno de los vértices, toma las aristas y las agrega al conjunto resultado.
88
+ for secondary_map in self._outgoing.values():
89
+
90
+ result.update(secondary_map.values())
91
+
92
+ return result
93
+
94
+ def get_edge(self, u: Vertex, v: Vertex) -> Edge:
95
+ """ Devuelve la arista que conecta u y v.
96
+
97
+ Args:
98
+ u (Vertex): vértice origen
99
+ v (Vertex): vértice destino
100
+
101
+ Returns:
102
+ Edge: arista que conecta u y v (en ese órden)
103
+ """
104
+ # Siempre busco en _outgoing en el órden de los parámetros.
105
+
106
+ self._validate_vertex(u)
107
+ self._validate_vertex(v)
108
+
109
+ return self._outgoing[u].get(v) # type: ignore
110
+
111
+ def degree(self, v: Vertex, outgoing: bool = True) -> int:
112
+ """ Devuelve la cantidad de aristas entrantes/salientes de v.
113
+
114
+ Args:
115
+ v (Vertex): vértice del cuál se analizarán la cantidad de aristas.
116
+ outgoing (bool, optional): True si se tendrán en cuenta las aristas de salida.
117
+ Caso contrario los de entrada. Valor por defecto True.
118
+
119
+ Returns:
120
+ int: número de aristas que entran/salen de v.
121
+ """
122
+ # Determino el diccionario sobre el que hacer el análisis y se lo asigno a adj.
123
+ self._validate_vertex(v)
124
+
125
+ adj = self._outgoing if outgoing else self._incoming
126
+
127
+ # Devuelvo la cantidad de entradas en el diccionario.
128
+ return len(adj[v])
129
+
130
+ def incident_edges(self, v: Vertex, outgoing: bool = True) -> Generator[Edge, None, None]:
131
+ """ Devuelve un generator con todos las aristas que entran / salen de v.
132
+
133
+ Args:
134
+ v (Vertex): vértice del que se quieren obtener las aristas
135
+ outgoing (bool, optional): True si se quieren obtener las aristas que salen de v.
136
+ Caso contrario los de entrada. Valor por defecto True.
137
+
138
+ Yields:
139
+ Generator[Edge, None, None]: devuelve generator con todos las aristas que entran/salen de v.
140
+ """
141
+ # Determino el diccionario sobre el que se quiere hacer el análisis.
142
+
143
+ self._validate_vertex(v)
144
+
145
+ adj = self._outgoing if outgoing else self._incoming
146
+
147
+ # Obtengo todos las aristas del diccionario referenciado por adj.
148
+ for edge in adj[v].values():
149
+ yield edge
150
+
151
+ def insert_vertex(self, x: Any) -> Vertex:
152
+ """ Crea un vértice x en el grafo y lo devuelve.
153
+
154
+ Args:
155
+ x (Any): elemento a almacenar en el vértice.
156
+
157
+ Returns:
158
+ Vertex: _description_
159
+ """
160
+ # Creo el vértice
161
+ v = Vertex(x)
162
+ # Defino un diccionario para v en _outgoing.
163
+ self._outgoing[v] = {}
164
+ # Si el grafo es dirigido hago lo mismo en _incoming.
165
+ if self.is_directed():
166
+ self._incoming[v] = {}
167
+ # Devuelvo v.
168
+ return v
169
+
170
+ def insert_edge(self, u: Vertex, v: Vertex, x: Any | None) -> None:
171
+ """ Inserta una arista entre con origen u, destino v y elemento x.
172
+
173
+ Args:
174
+ u (Vertex): vértice origen.
175
+ v (Vertex): vértice destino.
176
+ x (Any | None): información a almacenar en la arista.
177
+ """
178
+ # Creo la arista
179
+ e = Edge(u, v, x)
180
+ # Lo registro en _outgoing e _incoming.
181
+ self._outgoing[u][v] = e
182
+ if not self.is_directed(): # Diferente a la bibliografía. Aquí está corregido.
183
+ self._incoming[v][u] = e
184
+
185
+
186
+ def dfs(self, u: Vertex) -> Dict[Vertex, Edge | None]:
187
+ """ Realiza una búsqueda primero en profundidad (DFS) el el grafo comenzando por el nodo u.
188
+
189
+ Args:
190
+ u (Vertex): vértice por donde comenzar la búsqueda.
191
+
192
+ Returns:
193
+ Dict [Vertex, Edge | None]: Diccionario resultante con el camino compuesto por los vértices visitados.
194
+ """
195
+ # Defino un diccionario (que va a ser el resultado) con u como vértice donde comienza el recorrido.
196
+ result: Dict[Vertex, Edge | None] = {u: None}
197
+ # Hago el recorrido quedando el resultado en result.
198
+ self._dfs(u, result)
199
+ # Retorno result.
200
+ return result
201
+
202
+ def construct_path(self, u: Vertex, v: Vertex, discovered: Dict[Vertex, Edge]) -> List[Vertex]:
203
+ """Devuelve una lista con los vértices pertenecientes al camino desde u a v ó
204
+ una lista vacía si v no es alcanzable desde u.
205
+
206
+ Args:
207
+ u (Vertex): vértice al principio del camino.
208
+ v (Vertex): vértice al final del camino.
209
+ discovered (Dict[Vertex, Edge]): es el diccionario resultante de una llamada previa a dfs iniciada en u.
210
+
211
+ Returns:
212
+ List[Vertex]: camino de u a v.
213
+ """
214
+ path : List[Vertex] = []
215
+
216
+ if v in discovered:
217
+
218
+ path.append(v) # Construímos una lista desde v hasta u y luego la invertimos al final del algoritmo.
219
+ walk = v # Definimos v como walk.
220
+
221
+ while walk is not u: # Si walk no es u
222
+ e = discovered[walk] # Obtenemos la arista para walk.
223
+ parent = e.opposite(walk) # Pido el opuesto a walk en la arista e.
224
+ path.append(parent) # Lo agrego a la lista resultado.
225
+ walk = parent # Ahora parent es el nuevo walk.
226
+
227
+ path.reverse() # Invierto la lista resultado.
228
+
229
+ return path
230
+
231
+ def dfs_complete(self) -> Dict[Vertex, Edge | None]:
232
+ """ Lleva a cabo una búsqueda primero en profundidad para todo el grafo y
233
+ retorna el diccionario forest. El resultado mapea cada vértice a la arista que
234
+ fue usada para descubrirlo. Los vértices que mapean a None son las raíces
235
+ del árbol DFS.
236
+
237
+ Returns:
238
+ Dict[Vertex, Edge | None]: Diccionario resultado que mapea el vértice junto
239
+ con la arista que se utilizó para descubrirlo.
240
+ """
241
+ forest : Dict[Vertex, Edge | None] = {}
242
+ # Obtengo una lista con todos los vértices del grafo y los proceso uno a uno.
243
+ for u in self.vertices():
244
+ # Si ya no lo encontré
245
+ if u not in forest:
246
+ # Indico que hay que comenzar una nueva DFS a partir de u.
247
+ forest[u] = None
248
+ self._dfs(u, forest)
249
+ return forest
250
+
251
+ def bfs(self, s : Vertex) -> Dict[Vertex, Edge | None]:
252
+ """ Implementa un trayecto del tipo búsqueda primero en anchura.
253
+
254
+ Args:
255
+ s (Vertex): vértice desde donde se inicia el trayecto.
256
+
257
+ Returns:
258
+ Dict[Vertex, Edge | None]: Retorna un diccionario con todos los
259
+ vértices y los arcos utilizandos para llegar a ellos.
260
+ """
261
+
262
+ # Defino discovered como un diccionario que en principio solo tiene el vértice de inicio.
263
+ discovered : Dict[Vertex, Edge | None] = {s : None}
264
+ self._bfs(s, discovered)
265
+ return discovered
266
+
267
+ def bfs_complete(self) -> Dict[Vertex, Edge | None]:
268
+ """ Lleva a cabo una búsqueda primero en anchura en todo el grafo
269
+ Como resultao
270
+
271
+ Returns:
272
+ Dict[Vertex, Edge | None]: diccionario con todos los vétices encontrados y los
273
+ arcos que se siguieron para encontrarlos. Las raíces son marcadas con arco None.
274
+ """
275
+ forest = {}
276
+ # Para todos los vértices..
277
+ for u in self.vertices():
278
+ # Si ya no fueron procesados
279
+ if u not in forest:
280
+ # Inicio un recorrido en anchura a partir de u.
281
+ forest[u] = None
282
+ self._bfs(u, forest)
283
+ return forest
284
+
285
+ #########################################################################
286
+ # MÉTODOS NO PÚBLICOS
287
+ #########################################################################
288
+ def _validate_vertex(self, v: Vertex) -> None :
289
+ """Verifica que v sea un vértice de este grafo.
290
+
291
+ Args:
292
+ v (Vertex): Vértice que se pretende analizar.
293
+
294
+ Raises:
295
+ TypeError: si el vértice no es una instancia de Vertex
296
+ ValueError: si el vértice no pertence a este grafo.
297
+ """
298
+ if not isinstance(v, Vertex):
299
+ raise TypeError('Se esperaba un objeto de clase Vertex')
300
+
301
+ if v not in self._outgoing:
302
+ raise ValueError('El vértice no pertenece a este grafo.')
303
+
304
+ def _dfs(self, u : Vertex, discovered : Dict[Vertex, Edge | None]) -> None:
305
+ """ Realiza la búsqueda utilizando un algoritmo recursivo y dejando los nodos
306
+ visitados en discovered.
307
+
308
+ Args:
309
+ u (Vertex): vértice sobre el que se van a analizar las aristas.
310
+ discovered (Dict[Vertex, Edge]): camino con todos los nodos ya visitados.
311
+ """
312
+ # Para el vértice u obtengo las aristas.
313
+ for e in self.incident_edges(u):
314
+ # Obtengo el vértice opuesto a u para la arista e
315
+ v = e.opposite(u)
316
+ # Si v no estaba en el diccionario de los vértices visitados.
317
+ if v not in discovered:
318
+ # Lo inserto en discovered.
319
+ discovered[v] = e
320
+ # Recursivamente continúo haciendo la búsqueda por v.
321
+ self._dfs(v, discovered)
322
+
323
+ def _bfs(self, s : Vertex, discovered : Dict[Vertex, Edge | None]) -> None:
324
+ """Lleva a cabo una búsqueda primero en anchura de la porción aún no descubierta
325
+ de un grafo iniciando por s.
326
+ Los vértices descubiertos serán agregados al diccionario discovered como resultado.
327
+
328
+ Args:
329
+ s (Vertex): vértice de inicio.
330
+ discovered (Dict[Vertex, Edge]): es un diccionario que mapea cada vértice
331
+ con el arco a través del que se lo encontró.
332
+ """
333
+ level : List[Vertex] = [s] # El primer nivel incluye solo a s
334
+ while len(level) > 0:
335
+ next_level : List[Vertex] = [] # preparamos para reunir los nuevos vértices.
336
+ for u in level:
337
+ # Para cada arco que sale de u
338
+ for e in self.incident_edges(u):
339
+ v = e.opposite(u) # obtengo el vértice en el otro extremo.
340
+ if v not in discovered: # Si el vértice v ya no fue descubierto.
341
+ discovered[v] = e # e es el arco a partir del cual descubrí v.
342
+ next_level.append(v) # agendo v para comenzar a explorar luego.
343
+ level = next_level # Hago el próximo nivel el actual.
@@ -0,0 +1,28 @@
1
+ from typing import Any
2
+
3
+ class Vertex:
4
+ """ Representa el vértice de un grafo. """
5
+ __slots__ = '_element'
6
+
7
+ def __init__(self, x: Any) -> None:
8
+ self._element = x
9
+
10
+ @property
11
+ def element(self) -> Any:
12
+ return self._element
13
+
14
+ @element.setter
15
+ def element(self, value: Any) -> None:
16
+ self._element = value
17
+
18
+ def __repr__(self) -> str:
19
+ return str(self)
20
+
21
+ def __str__(self) -> str:
22
+ return str(self._element)
23
+
24
+ def __eq__(self, other: 'Vertex') -> bool:
25
+ return id(self) == id(other)
26
+
27
+ def __hash__(self) -> int:
28
+ return hash( id(self) )
@@ -0,0 +1,4 @@
1
+ from .stacks import ArrayStack, LinkedStack
2
+ from .queues import ArrayQueue, LinkedQueue
3
+ from .lists import LinkedList
4
+ from .list_node import ListNode
@@ -0,0 +1,9 @@
1
+ from typing import Any, Optional
2
+
3
+ class ListNode:
4
+
5
+ __slots__ = "element", "next"
6
+
7
+ def __init__(self, element : Any, next : Optional['ListNode'] = None) -> None:
8
+ self.element = element
9
+ self.next : Optional['ListNode'] = next
@@ -0,0 +1 @@
1
+ from .linked_list import LinkedList
@@ -0,0 +1,226 @@
1
+ from typing import Any, Optional, Iterable
2
+ from ..list_node import ListNode
3
+
4
+ class LinkedList:
5
+ """Implementación de una Lista utilizando representación por enlaces."""
6
+
7
+ def __init__(self) -> None:
8
+ """Crea una lista vacía"""
9
+ self._header: ListNode = ListNode(None, None)
10
+ self._size: int = 0
11
+
12
+ def __len__(self) -> int:
13
+ """Indica la cantidad de elementos de la estructura.
14
+
15
+ Returns:
16
+ int: entero que indica la cantidad actual de elementos almacenados en la lista.
17
+ """
18
+ return self._size
19
+
20
+ def __str__(self) -> str:
21
+ """Concatena en un único string todos los elementos de la lista.
22
+
23
+ Returns:
24
+ str: string con todos los elementos que contiene la lista.
25
+ """
26
+ #Si la estructura está vacía => retorno siempre lo mismo.
27
+ if self.is_empty():
28
+ return "LinkedList()"
29
+
30
+ res = "" # inicializo resultado con el string vacío
31
+
32
+ actual = self._header.next # Inicio con el primer nodo de la estructura.
33
+
34
+ while actual: # Si el elemento actual no es None
35
+ # Lo proceso
36
+ res += str(actual.element) + ", "
37
+ # Continúo con el siguiente.
38
+ actual = actual.next
39
+
40
+ # Quito las dos últimas posiciones del string.
41
+ res = res[:-2]
42
+
43
+ return f"LinkedList({res})"
44
+
45
+ def __contains__(self, item: Any) -> bool:
46
+ """Indica si un elemento forma parte de la estructura.
47
+
48
+ Args:
49
+ item (Any): el elemento a buscar en la estructura.
50
+
51
+ Returns:
52
+ bool: Devuelve True en caso de encontrar el elemento, caso contrario False.
53
+ """
54
+ return self.index_of(item) >= 0 # Si index_of devuelve un valor mayor o igual que cero => encontré el valor que estaba buscando.
55
+
56
+ def __getitem__(self, key: int) -> Optional[Any]:
57
+ """Devuelve el elemento ubicado en la posición key de la lista.
58
+
59
+ Args:
60
+ key (int): índice a buscar en la estructura.
61
+
62
+ Raises:
63
+ Exception: Arroja excepción si la estructura está vacía o si el índice está fuera de rango.
64
+
65
+ Returns:
66
+ Any: Devuelve el valor almacenado por la posición indicada por key, caso contrario, None.
67
+ """
68
+ if self.is_empty():
69
+ raise Exception("Operación no permitida si la estructura está vacía.")
70
+
71
+ if abs(key) >= self._size:
72
+ raise Exception("Índice fuera de rango. No se puede continuar.")
73
+
74
+ # Si el índice es negativo, calculo el valor que le corresponde
75
+ if key < 0:
76
+ key = len(self) - abs(key)
77
+
78
+ i = 0
79
+ actual = self._header.next # Inicio en el primer nodo.
80
+ while actual: # Si existe
81
+ if i == key: # Si el valor de i coincide con el de key
82
+ return actual.element #Devuelvo el valor del nodo en el que estoy ubicado.
83
+ actual = actual.next # Caso contrario continúo con el siguiente nodo.
84
+ i += 1
85
+
86
+ return None
87
+
88
+ def __setitem__(self, key: int, value: Any) -> None:
89
+ """Establece como nuevo elemento value para la posición de lista indicada por key.
90
+
91
+ Args:
92
+ key (int): posición que se va a modificar.
93
+ value (Any): nuevo elemento que va a ocupar la posición indicada por key.
94
+
95
+ Raises:
96
+ Exception: Arroja excepciones si la estructura está vacía o si el índice está fuera de rango.
97
+ """
98
+ if self.is_empty():
99
+ raise Exception("Operación no permitida si la estructura está vacía.")
100
+
101
+ if abs(key) >= self._size:
102
+ raise Exception("Índice fuera de rango. No se puede continuar.")
103
+
104
+ # Si el índice es negativo, calculo el valor que le corresponde
105
+ if key < 0:
106
+ key = len(self) - abs(key)
107
+
108
+ i = 0
109
+
110
+ actual = self._header.next # Inicio en el primer nodo
111
+
112
+ while actual: # Si el nodo existe...
113
+ if i == key: # Verifico si el índice coincide con el pasado por parámetro
114
+ actual.element = value
115
+ return
116
+ actual = actual.next
117
+ i += 1
118
+
119
+ def __delitem__(self, key: int) -> None:
120
+ """Elimina de la estructura el valor ubicado en la posición indicada por key.
121
+
122
+ Args:
123
+ key (int): índice del elemento que se va a quitar de la estructura.
124
+
125
+ Raises:
126
+ Exception: Arroja excepción si la estructura está vacía o si el índice está fuera de rango.
127
+ """
128
+ if self.is_empty():
129
+ raise Exception("Operación no permitida si la estructura está vacía.")
130
+
131
+ if abs(key) >= self._size:
132
+ raise Exception("Índice fuera de rango. No se puede continuar.")
133
+
134
+ # Si el índice es negativo, calculo el valor que le corresponde
135
+ if key < 0:
136
+ key = len(self) - abs(key)
137
+
138
+ i = 0
139
+
140
+ previo = None
141
+ actual = self._header.next # Arranco en el primer nodo de la lista.
142
+ while actual: # Si existe...
143
+ if i == key: # Si el i es igual a key => corto.
144
+ break
145
+ previo = actual # Guardo el valor de actual en previo.
146
+ actual = actual.next # Continúo con el siguiente nodo.
147
+ i += 1
148
+
149
+ # Si previo existe => elimino el nodo actual haciendo que el siguiente del previo sea igual al siguiente de actual
150
+ if previo:
151
+ previo.next = actual.next # type: ignore
152
+ else:
153
+ # Si previo no existe => se está queriendo eliminar el primer nodo.
154
+ self._header.next = actual.next # type: ignore
155
+
156
+ self._size -= 1
157
+
158
+ def __iter__(self) -> Iterable[Any]:
159
+ """Crea un iterador sobre la lista.
160
+
161
+ Yields:
162
+ _type_: _description_
163
+ """
164
+ # Se ubica en el primer nodo.
165
+ actual = self._header.next
166
+
167
+ #Si existe un nodo
168
+ while actual:
169
+ #Devuelve el elemento del nodo actual
170
+ yield actual.element
171
+
172
+ #Continúa con el siguiente.
173
+ actual = actual.next
174
+
175
+ def is_empty(self) -> bool:
176
+ """ Indica si la estructura está vacía o no.
177
+
178
+ Returns:
179
+ bool: Devuelve True si la lista no tiene ningún elemento, caso contrario False.
180
+ """
181
+ return self._size == 0
182
+
183
+ def append(self, elem: Any) -> None:
184
+ """ Agrega un elemento al final de la estructura.
185
+
186
+ Args:
187
+ elem (Any): el elemento que va a quedar ubicado al final de la lista.
188
+ """
189
+ nuevo_nodo = ListNode(elem)
190
+ # Si la lista está vacía => el nuevo nodo es el primero.
191
+ if self.is_empty():
192
+ self._header.next = nuevo_nodo
193
+ else:
194
+ # Caso contrario => me muevo hasta el final...
195
+ actual = self._header
196
+
197
+ while actual.next:
198
+ actual = actual.next
199
+
200
+ # Agrego el nuevo nodo como siguiente del último.
201
+ actual.next = nuevo_nodo
202
+
203
+ self._size += 1
204
+
205
+ def index_of(self, elem: Any) -> int:
206
+ """ Devuelve la ubicación elem en la estructura.
207
+
208
+ Args:
209
+ elem (Any): El elemento a buscar.
210
+
211
+ Returns:
212
+ int: Devuelve la posición de elem en la estructura, en caso de no encontrarlo, -1.
213
+ """
214
+ i = 0
215
+
216
+ actual = self._header.next # Me ubico en el primer nodo
217
+
218
+ while actual:
219
+ # Si el elemento de actual es el que estoy buscando devuelvo el valor de i.
220
+ if actual.element == elem:
221
+ return i
222
+
223
+ actual = actual.next # Continúo con el siguiente nodo.
224
+ i += 1
225
+
226
+ return -1
@@ -0,0 +1,2 @@
1
+ from .array_queue import ArrayQueue
2
+ from .linked_queue import LinkedQueue