CRISTIAN BARBERO PÉREZ
Christian Velázquez
Efraín Hernández García
Domina la representación de grafos en JavaScript con tres enfoques prácticos y legibles. Aquí verás cómo modelar conexiones entre nodos usando arrays, objetos y matrices con ceros y unos. El objetivo: una representación visual clara que te permita construir la lógica de la estructura de datos sin enredos.
¿Cómo se representan grafos en código de forma clara?
Para el ejemplo, se trabaja un grafo no dirigido con nodos 0, 1, 2 y 3. Las conexiones son: 0–2, 2–3, 2–1 y 1–3. La explicación compara tres formatos: lista de aristas (edge list), lista de adyacencia (adjacent list) y matriz de adyacencia (adjacent matrix).
¿Qué diferencias clave hay entre edge list, adjacent list y matrix?
Estos modelos mapearán el mismo grafo, pero con estructuras distintas. Cada opción enfatiza algo distinto: simplicidad, vecindad por índice o cálculo con matriz.
¿Cómo funciona un edge list en JavaScript?
Representa solo las conexiones como pares de nodos. Es conciso y directo.
// Edge list (grafo no dirigido) const graph = [ [0, 2], [2, 3], [2, 1], [1, 3] ];
¿Cómo se crea un adjacent list con arrays u objetos?
Agrupa vecinos por índice. Se puede modelar como array de arrays o como objeto (hash table) para mayor claridad visual.
// Adjacent list con array de arrays (índice = nodo) const graph = [ [2], // 0 → 2 [2, 3], // 1 → 2, 3 [0, 1, 3], // 2 → 0, 1, 3 [1, 2] // 3 → 1, 2 ];
// Adjacent list con objeto (*hash table*) const graph = { 0: [2], 1: [2, 3], 2: [0, 1, 3], 3: [1, 2] };
¿Cuándo conviene un adjacent matrix con 0 y 1?
Cuando necesitas una matriz que indique con 1 si hay conexión y 0 si no. Útil para escenarios que requieran cálculo matricial o grafos ponderados.
// Adjacent matrix con array de arrays const graph = [ // 0 1 2 3 [0, 0, 1, 0], // 0 [0, 0, 1, 1], // 1 [1, 1, 0, 1], // 2 [0, 1, 1, 0] // 3 ];
// Adjacent matrix con objeto anidado (*hash table*) const graph = { 0: { 0: 0, 1: 0, 2: 1, 3: 0 }, 1: { 0: 0, 1: 0, 2: 1, 3: 1 }, 2: { 0: 1, 1: 1, 2: 0, 3: 1 }, 3: { 0: 0, 1: 1, 2: 1, 3: 0 } };
¿Cuál será la estrategia para el ejercicio en JavaScript?
Se trabajará un grafo no dirigido usando adjacent list. La implementación elegida será con objeto (hash table) por su claridad al leer índices y vecinos. Esto facilita entender la estructura objetivo y construir la lógica paso a paso.
¿Te animas a codificar tu versión y compartirla? Cuéntame en los comentarios cómo lo resolviste y qué decisiones tomaste.
CRISTIAN BARBERO PÉREZ
Christian Velázquez
Efraín Hernández García
Carlos Mazzaroli
Ariel González Falcón
Carlos Eduardo Gomez García
Agustín Ravena
Jimmy Buriticá Londoño
Alexis Villalva
Ana Rotela Cabrera
Fernando Quinteros Gutierrez
Ana Rotela Cabrera
María Antonia Lobato
Sebastian Galindo
Usuario anónimo
UserLuis Eduardo
Max Andy Diaz Neyra
Juan Manuel Fallo
walter Jorge
Felipe Moreno
Andres Velasquez
Jasubi Lehem Piñeyro Legaspi
David Hernandez
Esteban Javier Gold Muros
Oliverio Velázquez Cayata
Cesar Gabriel Mejias Farreras
Ariel Ezequiel Biazzo Genua
Jasubi Lehem Piñeyro Legaspi
Dave Juanes
Sergio Javier Lopez Olivera
Diego Rene Casallas Diaz
La matriz de adyacencia es simétrica cuando el grafo es no dirigido, y no tiene por que serlo cuando es dirigido.
Es posible que dos nodos estén conectados por más de una arista, entonces en la matriz de adyacencia habrá que colocar el número de conexiones entre esos nodos.
Además la diagonal principal de la matriz representa la conexión de un nodo consigo mismo, que en ocasiones existe.
El siguiente ejemplo es del curso de Matemáticas Discretas:
Además se puede representar un grafo ponderado con esta matriz, donde el valor representa el coste de ir de un nodo a otro:
Si no hay conexión entre nodos es imposible ir directamente, por eso el coste es infinito. Y el coste para ir desde un nodo hasta sí mismo es nulo, por eso en la diagonal principal solo hay ceros.
Gracias.
Muy buen aporte respecto a esta clase!
El profe en el minuto 12:09: +Vamos a usar con el adjacent list con objetos. por que? por que es lo que a mi me gusta.
venia a por este comentario jajajaja
Hay 3 formas de representar grafos: . Edge List: Te dice por medio de pares, quiénes son esos nodos que tienen conexión entre sí, es como si los juntáramos diciendo "este con este, este con este y este con este", lo normal cuando pareamos cosas. . Adjacent List: Te dice qué elementos conoce a cuáles, para ponerlo en contexto, imagina a las personas que tu conoces, tú conoces a "n" cantidad de personas, pues básicamente esa sería tu primer índice del arreglo en un adjancent list, pero ten en cuenta que todas esas personas que tú conoces también conocen a otras personas, de entre ellas tú, así que esa sería tu segunda posición del arreglo, todas las personas que otra persona conoce. . Adjancent Matrix: Esto es un poquito más de imaginación, el Adjancent Matrix te pone a todas las personas que existen en una lista y te empieza a preguntar: ¿Conoces a esta persona? Si sí entonces la pone en 1, si no entonces la pone en 0, y así por cada persona que exista, cada índice representa a una persona, por eso digo que necesitas imaginartelas.
Muy buen resumen
Edge List
Gracias
Que buen resumen
📊 Representando grafos en código
Apuntes
Nuestro grafo se vera de la siguiente forma
/* 2 - 0 / \ 1 - 3 */
Tenemos diferentes formas de representar este grafico
Edge List
const graph = [ [0, 2], [2, 3], [2, 1], [1, 3] ];
Adjacent List
// ===== INDICES ===== // 0 1 2 3 const graph = [[2], [2, 3], [0, 1, 3], [1, 2]];
const graph = { 0: [2], 1: [2, 3], 2: [0, 1, 3], 3: [1, 2] };
Adjacent Matrix
const graph = [ // ==== NODOS ==== // 1 2 3 4 [0, 0, 1, 0], // 0 [0, 0, 1, 1], // 1 [1, 1, 0, 1], // 2 [0, 1, 1, 0] // 3 ];
const graph = { 0: [0, 0, 1, 0], 1: [0, 0, 1, 1], 2: [1, 1, 0, 1], 3: [0, 1, 1, 0] };
Espectacular resumen, como en cada clase.
const graphAdjacentList [ [3,4,6], [1,5,6], [1,5,8], [3,4], [1,3], [4] ]; const graphAdjacentList { 1: [3,4,6], 3: [1,5,6], 4: [1,5,8], 5: [3,4], 6: [1,3], 8: [4], };
Espero que esté bien así, si no es así, porfa, me corrigen......thanks ; )
Esta es mi propuesta de Graph, por si alguien quiere detallarla :p.
class NodeGraph { constructor(value) { this.value = value; this.connections = []; } } class Graph { constructor(){ this.graph = {}; // En el constructor inicializamos el hash table donde van a almacenarse todos los nodos y sus conexiones this.length = 0; } insertNode(value) { // insertNode() nos va a permitir insertar un nodo const newNode = new NodeGraph(value); // Crear un nodo y guardarlo if (this.graph[value]) return console.error(new Error('Ya existe ese nodo')) // Si ya existe el nodo que se va a añadir, regresa un error this.graph[value] = newNode.connections; // La key del graph se va a asignar con el valor pasado en el parametro, y se van a establecer las conexiones como value this.length++; // Agregamos el registro return this; } setConnections(node1, [...node2]) { // setConnections() nos va a permitir hacer los Edge de cada uno de los nodos, recibe una array de segundo parametro si se quiere añadir mas de una conexion if (!this.graph[node1]) return console.error(new Error('No existe ningun nodo inicial con ese valor')); // Si no existe el nodo pasado por el primer parametro, regresa un error let validNodes = []; // Esta array se hace para dejar los nodos que si se puedan conectar, ya que pueden haber nodos que no se puedan conectar node2.forEach(node => { // Vamos a recorrer la array que nos pasaron en el segundo parametro if (node === node1) return console.error(new Error('No de puede hacer conexion con el mismo nodo!')) // Si en las conexiones esta el mismo nodo del primer parametro, entonces retorna un error if (!this.graph[node]) return console.error(new Error(`El nodo ${node} no se encontró para hacer la conexion.`)) // Si no existe alguna conexion en nuestra Hash Table de nodos, entonces regresa un error if (this.graph[node].find(connections => node1 === connections)) return console.error(new Error(`Ya existe una conexion con el nodo: ${node}`)); // Si alguna de las conexiones ya tiene una conexion con el nodo del primer parametro, regresa un error this.graph[node].push(node1); // Si pasa todas las validaciones, agrega a el nodo de la conexion, el nodo pasado en el primer parametro validNodes.push(node); // Y vamos a añadir a la array de Nodos Validos el nodo que estabamos evaluando y paso todas las validaciones }) // Con este forEach() primero vamos establecer las conexiones en los nodos ajenos this.graph[node1].push(...validNodes); // Para que cuando ya se revisen todas las conexiones, solo se tenga que añadir los valores de los nodos que pasaron todas las validaciones return this; } deleteNode(node) { // deleteNode() nos va a permitir eliminar un nodo del Graph y de los nodos con los que tenía conexion const nodeDeleted = this.graph[node]; // Vamos a guardar el nodo que va a ser eliminado para retornarlo antes this.graph[node].forEach(connection => { // Vamos a recorrer todas las conexiones que tenía le nodo que vamos a eliminar this.graph[connection].forEach(connections => { // Cuando ya ubiquemos el nodo que tiene la conexion, vamos a recorrer las conexiones que tiene para encontrar el nodo que vamos a eliminar let index = -1; // Creamos un contador para el index, para poder eliminar el elemento de la array bien index++; // Como esta inicializado con -1, entonces vamos a añadirle uno para que quede en el index: 0. Recordemos que vamos a recorrer los elementos del nodo y necesitamos una referencia del index if (connections === node) this.graph[connection].splice(index, index+1); // Cuando encontremos el nodo que vamos a eliminar en las conexiones, vamos a eliminarlo con splice() }) }) // Cuando el nodo que se va a eliminar ya no existe en ninguna de sus conexiones... delete this.graph[node]; // Vamos a eliminar definitivamente el nodo this.length--; // Vamos a agregar el registro de que se elimino el nodo return `Nodo eliminado: ${node} : ${nodeDeleted}`; // Y vamos a retornar este mensaje } }
😃 La representación de grafos en código Dart es idéntica a JavaScript.
const graph = [ [0, 0, 1, 0], [0, 0, 1, 1], [1, 1, 0, 1], [0, 1, 1, 0], ];
import 'graph.dart'; void main(){ print(graph.length); print(graph.first); print(graph.last); }
no entendi
Son formas de representar los grafos. En Edge List: simplemente construimos pares que indican una arista. Cada elemento del arreglo es una conexion. . En Adjacent List aqui lo que haremos es representar a que nodos esta conectado cada nodo. Es mejor usar la representacion de objeto. . En Adjacent Matrix es un tanto más complejo, aqui debemos imaginar una matriz nxn donde n es el numero de nodos. Entonces colocamos los nodos en los lados, y en la celdas donde los nodos tienen conexion en el grafo va un uno, en caso contrario un cero. . Si deseas profundizar en grafos y arboles te recomiendo el curso de matematicas discretas, es de mucha ayuda y te sentiras en el cole.
Dejo este video del canal BettaTech explicando los grafos, muy claro! https://www.youtube.com/watch?v=23pdz9VtIBo&list=WL&index=15&ab_channel=BettaTech
Los grafos se pueden representar de varias maneras, cada una con sus ventajas:
Edge List: Sencilla y fácil de implementar, ideal para grafos dispersos. Sin embargo, no es eficiente para operaciones de búsqueda.
Adjacency List: Ahorra espacio, especialmente en grafos dispersos, y facilita la iteración sobre los vecinos de un nodo. Es bastante utilizada en aplicaciones prácticas.
Adjacency Matrix: Útil para grafos densos, permite una verificación rápida de conexiones. Consume más memoria, ya que se necesita un espacio cuadrático en relación al número de nodos.
La Adjacency List es la más comúnmente utilizada en la práctica por su eficiencia en términos de espacio y velocidad para la mayoría de los casos.
Por si alguno escucho este término y no sábe que es, como yo.
¿Qué significa que algo está Hardcodeado? El "hardcodeo" o "hardcodear" código es escribir código que deberia ser automatizado o código que debería ser obtenido por el usuario final y no por el desarrollador.
Esto es parecido a como se usa en IA?
Hola quisiera hacer esos grafos en Python alguien podría ayudarme? es que no sé que librerías usar :c
Buenas, usa lo mismo que acá; son estructuras básicas que todos los lenguajes comparten
No termino de entender la forma en la que se decide que nodos se conectan con cuales, esto se hace de forma algorítmica o se hardcodea?
Bueno este ejemplo es un grafo no dirigido, así que en teoría cualquier dirección debería ser valida.
Bueno, si hablas de grafos dirigidos, puedes buscar en youtube, pero se me hace la idea de que deberiamos programar cada grafo con un clase Node que posea un NEXT y asi concatenar y unir cada grafo, es una simple idea, pero en google hay un mar de informacion!
Me gusto el objeto binario
Creo que va por ahí
const ejercicio = { 1: [4,3,6], 3: [5,1,6], 4: [8,1,3,5], 5: [4,3], 6: [1,3], 8: [4], }
Solución al reto. Tiene un pequeño problema que es la redundancia hacia los mismos nodos en el array de adyacencia: . . .
class Node { constructor(value){ this.value = value; this.connections = []; } } class Graph { constructor(){ this.data = {}; this.vertices = 0; this.edges = 0; } get(item){ return this.data[item]; } connect(value, index = value){ //this method points to first searching and then creating the nodes regarding to the index if(this.vertices === 0){ //if there're no nodes in the graph const newNode = new Node(value); //instancing the node this.data[newNode.value] = newNode.connections; //inside data → key: node's value; value: node's connections array this.vertices++; //only increasing vertices due to the presence only one node in the graph return this; //returning graph } if(value == index){ //when both values are the same the method returns null return null; } if(this.data[index]){ //checking if such node to append the new node exists if(!this.data[index].some(i => i == value)){ //each data[index] corresponds with an array of the other nodes that is connected. Method 'some' will return a boolean if there's any value in that array that is equal to the given value in the connect method //'!' will make the validation to be "if there's no reference equal to the value provided in the array of such object's connections do the next thing" ↓ if(this.data[value]){ //if the node exists already this.data[index].push(value); //update of index's-node connections array this.edges++; //updating edges, as the given node of given value already exists } else { const newNode = new Node(value); //this case the node's value provided doesn't exists, so it must be instanced this.data[index].push(newNode.value); //updating index's-node connections array with the value of the new node newNode.connections.push(index); // updating connections array of new node with the index (pointed node) this.data[newNode.value] = newNode.connections; //inside data: new key → new node value; with value → new node connections array this.edges++; this.vertices++; //updating new edges and vertices } } } else { return null; //when the given index doesn't exists } return this; //return the graph } } const myGraph = new Graph();
La estructura parece bien, pero creo esta bien raro
class Node { constructor(value) { this.date = value; this.length=0 /*Edge: Nodos enlaces */ this.edges={}; } pushEdge(edge){ this.edges[edge.date]=edge; this.length++; } } class Graph { constructor() { this.nodes = {}; this.length = 0 } pushNode(newNode) { this.nodes[newNode.date] = newNode; this.length++; } }