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.
- data_structures/__init__.py +6 -0
- data_structures/graphs/__init__.py +3 -0
- data_structures/graphs/edge.py +57 -0
- data_structures/graphs/graph.py +343 -0
- data_structures/graphs/vertex.py +28 -0
- data_structures/linear/__init__.py +4 -0
- data_structures/linear/list_node.py +9 -0
- data_structures/linear/lists/__init__.py +1 -0
- data_structures/linear/lists/linked_list.py +226 -0
- data_structures/linear/queues/__init__.py +2 -0
- data_structures/linear/queues/array_queue.py +97 -0
- data_structures/linear/queues/linked_queue.py +114 -0
- data_structures/linear/stacks/__init__.py +2 -0
- data_structures/linear/stacks/array_stack.py +65 -0
- data_structures/linear/stacks/array_stack_generics.py +64 -0
- data_structures/linear/stacks/linked_stack.py +88 -0
- data_structures/linear/util.py +8 -0
- data_structures/maps_dicts_hash/__init__.py +4 -0
- data_structures/maps_dicts_hash/chain_hashmap.py +114 -0
- data_structures/maps_dicts_hash/hashmap_base.py +148 -0
- data_structures/maps_dicts_hash/map_base.py +53 -0
- data_structures/maps_dicts_hash/probe_hashmap.py +123 -0
- data_structures/maps_dicts_hash/unsorted_table_map.py +105 -0
- data_structures/priority_queues/__init__.py +2 -0
- data_structures/priority_queues/array_heap.py +192 -0
- data_structures/priority_queues/priority_queue_base.py +50 -0
- data_structures/search_trees/__init__.py +1 -0
- data_structures/search_trees/linked_binary_search_tree.py +87 -0
- data_structures/trees/__init__.py +2 -0
- data_structures/trees/binary_tree_node.py +47 -0
- data_structures/trees/linked_binary_tree.py +403 -0
- python_ed_fcad_uner-0.1.0.dist-info/METADATA +34 -0
- python_ed_fcad_uner-0.1.0.dist-info/RECORD +35 -0
- python_ed_fcad_uner-0.1.0.dist-info/WHEEL +4 -0
- 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,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 @@
|
|
|
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
|