Construir un grafo no dirigido en JavaScript es más simple de lo que parece: con una representación tipo JSON List (objeto con arrays), un constructor claro y dos métodos (addVertex y addEdge), obtienes una estructura sólida para practicar lógica y conexiones entre datos. Aquí verás los pasos exactos, el código mínimo y cómo validar las relaciones entre nodos.
¿Cómo modelar un grafo no dirigido con adjacent list en JavaScript?
Un grafo se compone de vértices (nodos) y bordes (conexiones o edges). La clase inicia sin valor y usa un objeto para mapear cada nodo a un array con sus vecinos. Además, mantiene un contador de nodos para ubicar por índice.
¿Qué hace el constructor y cómo se representa con JSON List?
Crea la instancia del grafo sin valor inicial.
Define dis.nodos para el conteo de vértices.
Define dis.jsonList como objeto: cada clave es un nodo y su valor es un array de conexiones.
classGrafo{constructor(){this.nodos=0;this.jsonList={};// representación tipo *JSON List*: objeto de arrays}addVertex(nodo){this.jsonList[nodo]=[];// cada vértice inicia con un array vacíothis.nodos++;}addEdge(n1, n2){// grafo no dirigido: se agregan ambas conexiones con *push*this.jsonList[n1].push(n2);this.jsonList[n2].push(n1);}}
¿Qué implica usar índices y arrays en la estructura?
Cada nodo es una clave del objeto: índice legible y directo.
Cada array lista a qué nodos conecta ese vértice.
Con push agregas vecinos sin duplicar estructura.
¿Cómo agregar vértices y bordes (edges) paso a paso?
Primero se crean los vértices; luego se conectan con bordes. La verificación final confirma que la adjacent list refleja exactamente las conexiones esperadas.
const g =newGrafo();// 1) Agregar vértices (vértices: 1, 3, 4, 5, 6, 8)[1,3,4,5,6,8].forEach(v=> g.addVertex(v));// 2) Agregar bordes para un grafo no dirigido// conexiones validadas posteriormente en la *adjacent list*g.addEdge(1,6);g.addEdge(1,3);g.addEdge(1,4);g.addEdge(3,5);g.addEdge(3,6);g.addEdge(4,5);g.addEdge(4,8);console.log(g.jsonList);/*
{
1: [6, 3, 4],
3: [5, 6, 1],
4: [1, 5, 8],
5: [3, 4],
6: [3, 1],
8: [4]
}
*/
¿Cómo validar la adjacent list final por índice?
1 conecta con 6, 3, 4.
3 conecta con 5, 6, 1.
4 conecta con 1, 5, 8.
5 conecta con 3, 4.
6 conecta con 3, 1.
8 conecta con 4.
En un grafo no dirigido, cada borde aparece en ambos sentidos: la lógica de addEdge asegura la simetría agregando con push en los dos arrays involucrados.
¿Qué habilidades y conceptos refuerzas con este ejercicio?
Este ejercicio potencia tu comprensión práctica de estructuras de datos y su implementación en código sencillo pero preciso. Se trabaja desde la instanciación de clases y el constructor, hasta la representación con índices y arrays.
¿Qué habilidades técnicas aplicas en JavaScript?
Diseño de una clase con estado interno: nodos y JSON List.
Modelado de grafos con objeto de arrays: representación tipo adjacent list.
Implementación de métodos: addVertex para crear vértices y addEdge para conectar nodos.
Mutación controlada de colecciones con push.
Verificación manual de conexiones por índice: lectura y comparación rápida.
¿Qué conceptos clave quedan claros?
Grafo no dirigido: cada conexión es bidireccional por definición de edge.
Vértices vs. bordes: primero agregas nodos, luego conectas relaciones.
Lógica incremental: construir, probar, validar y ajustar con pocas líneas.
Simplicidad efectiva: la claridad de la representación facilita depuración y expansión.
¿Te animas a probar una variante? puedes intentar un grafo dirigido o crear nuevas conexiones y comentar cómo cambió tu adjacent list. ¡Comparte tus resultados y dudas!
Construcción de Grafos: Método Lista de Adyacencia en JavaScript
Construcción de Grafos: Método Lista de Adyacencia en JavaScript
Hace ya un buen tiempo hice un curso de estructuras de datos en Python, y en unos de los ejercicios nos daban un grafo, en el cual nos pedían crear tres funciones las cuales retornaran un edge list, un adjacency list y un adjacency matrix. Me tomé el tiempo de pasar el código de Python a JS, aquí lo dejo
classNode{constructor(value){this.value= value
this.edges=[]}}classEdge{constructor(nodeFrom, nodeTo){this.nodeFrom= nodeFrom
this.nodeTo= nodeTo
}}classGraph{constructor(nodes =[], edges =[]){this.nodes= nodes
this.edges= edges
}insertNode(value){this.nodes.push(newNode(value))}insertEdge(fromValue, toValue){let fromFound =nulllet toFound =nullthis.nodes.forEach(node=>{if(fromValue === node.value){ fromFound = node
}if(toValue === node.value){ toFound = node
}})if(!fromFound){ fromFound =newNode(fromValue)this.nodes.push(fromFound)}if(!toFound){ toFound =newNode(toValue)this.nodes.push(toFound)}const newEdge =newEdge(fromFound, toFound) fromFound.edges.push(newEdge) toFound.edges.push(newEdge)this.edges.push(newEdge)}getEdgeList(){const edgeList =[]for(let edge ofthis.edges){ edgeList.push([edge.nodeFrom.value, edge.nodeTo.value])}return edgeList
}getAdjacencyList(){const maxIndex =this.edges.length// Create a list with maxIndex +1 elements, all nullconst adjacencyList =Array.from(newArray(maxIndex+1),x=>null)for(let edge ofthis.edges){if(adjacencyList[edge.nodeFrom.value]){ adjacencyList[edge.nodeFrom.value].push(edge.nodeTo.value)}else{ adjacencyList[edge.nodeFrom.value]=[edge.nodeTo.value]}}return adjacencyList
}getAdjacencyMatrix(){const len =this.nodes.length+1//Create a matrix of dimensions len x len, filled with zeros const adjacencyMatrix =Array.from(newArray(len),x=>newArray(len).fill(0))for(let edge ofthis.edges){ adjacencyMatrix[edge.nodeFrom.value][edge.nodeTo.value]=1}return adjacencyMatrix
}}const graph =newGraph()graph.insertEdge(1,2)graph.insertEdge(1,3)graph.insertEdge(1,4)graph.insertEdge(3,4)// Should be [[1, 2], [1, 3], [1, 4], [3, 4]]console.log(graph.getEdgeList())// Should be [ null, [ 2, 3, 4 ], null, [ 4 ], null ]console.log(graph.getAdjacencyList())// Should be [[0,0,0,0,0],[0,0,1,1,1],[0,0,0,0,0],[0,0,0,0,1],[0,0,0,0,0]]console.log(graph.getAdjacencyMatrix())
El grafo de tipo adjancentList lo que haces es, como vimos, generar una lista de a quiénes conoce tal nodo.
.
Lo genial de este grafo es que si tú necesitas saber qué otros nodos están conectados a algún nodo específico simplemente tienes que obtener el índice que contiene a dicho nodo y eso te devolverá a los nodos con los cuales este se conecta 👀
lo entendí, pero no supe para que me sirve eso, toca seguir investigando los grafos, buen curso cositas avanzadas para mí, pero no sabría donde poner en practica
Lo puedes utilizar en machine learning y backend. Al final son estructuras de datos.
En realidad tanto las pilas, colas, listas enlazadas y arboles son eso, grafos.
Hice una clase de utilidad para crear un diccionario que cuando una propiedad se acceda y esta no exista, entonces cree automáticamente la entrada con un array.
classDefaultArrayDict{constructor(){returnnewProxy({},{get:(target, name)=>{if(!(name in target)){ target[name]=[];}return target[name];},});}}classGraph{constructor(){this.nodes=0;this.adjacentList=newDefaultArrayDict();}addEdge(node1, node2){this.adjacentList[node1].push(node2);this.adjacentList[node2].push(node1);}}const myGraph =newGraph();
De esta forma me ahorro la función llamada addVertex.
No tiene relevancia. Lo hace porque en este ejercicio quiere nombrar los nodos. No hacer alguna operación con los números. Pero, que buena observación, Pablo. :)
Esta interesante esta forma de crear un grafo, pero aun no estoy muy seguro como se podría recorrer. Seguiré estudiando el código, pero mientras aquí dejo mi aporte:
classGraph{constructor(){this.nodes=0;this.edgesAdjList={};}addVertex(node){this.edgesAdjList[node]=[];this.nodes++;}insert(node1, node2){//Si el node1 o node2 no existen, entonces se creanif(!this.edgesAdjList[node1])this.addVertex(node1);if(!this.edgesAdjList[node2])this.addVertex(node2);this.edgesAdjList[node1].push(node2);this.edgesAdjList[node2].push(node1);}}const myGraph =newGraph();
Tengo un problema y es que quiero evitar que se creen nodos sin conexiones, pero no en encuentro una forma correcta de ocultar la función addVertex() . ¿Algún consejo?
Añadí la comprobación de si existen los nodos antes de añadir la conexión entre ambos y en caso de que uno de ellos no exista se añade automáticamente.
Increible, no se me hubiera ocurrido para nada y parece ser simple y mas con esa explicacion tan digerible. Aun asi no lograria imaginar la cantidad de formas que hay de hacer todos los tipos de grafos
Para hacerlo dirigido imagino que es no hacer push en ambos sino solo en el camino ( 1 -> 2 solo hacer push a nodo1 )
Me encanto el tema, quisiera aprender a utilizarlos digamos en backend, por ejemplo si creo una mini red social cómo conectar a los usuarios que se agreguen (No Dirigido) y así. Me parece muy interesante de aplicar. Excelente curso 10/10
Código del ejercicio
Incluye los vértices y bordes.
.
classGraph{constructor(){this.nodes=0;this.adjacentList={};}/** Adicionar un Vertice */addVertex(node){this.adjacentList[node]=[];this.nodes++;}/** Adicionar un Borde */addEdge(node1, node2){this.adjacentList[node1].push(node2);this.adjacentList[node2].push(node1);}}const myGraph =newGraph();// VerticesmyGraph.addVertex('1');myGraph.addVertex('3');myGraph.addVertex('4');myGraph.addVertex('5');myGraph.addVertex('6');myGraph.addVertex('8');// BordesmyGraph.addEdge('1','6');myGraph.addEdge('1','4');myGraph.addEdge('1','3');myGraph.addEdge('3','5');myGraph.addEdge('3','6');myGraph.addEdge('4','5');myGraph.addEdge('4','8');
Código para la construcción de un Grafo no direccionado utilizando una Matriz adyacente.
classGraph{constructor(){this.nodes=0;this.adjacentMatrix={};}addVertex(node){if(this.adjacentMatrix[node]){returnnewError("Existing vertex");}this.adjacentMatrix[node]=[];const adjacentMatrixLength =Object.keys(this.adjacentMatrix).length;for(let key inthis.adjacentMatrix){for(let i =0; i < adjacentMatrixLength; i++){if(this.adjacentMatrix[key].length!== adjacentMatrixLength){this.adjacentMatrix[key].push("0");}}}this.nodes++;returnthis;}addEdge(node1, node2){if(!this.adjacentMatrix[node1]||!this.adjacentMatrix[node2]){returnnewError("One of the vertices entered doesn't exist");}const adjacentMatrixKeys =Object.keys(this.adjacentMatrix);for(let i =0; i <this.nodes; i++){if(adjacentMatrixKeys[i]== node1){this.adjacentMatrix[node2][i]="1";}if(adjacentMatrixKeys[i]== node2){this.adjacentMatrix[node1][i]="1";}}returnthis;}}const myGraph =newGraph();
Les comparto mi implementación del código de la clase usando JSDocs:
/*
* A graph can be represent in many ways, let suppose we have the following graph:
* 2 - 0
* | \
* | \
* 1 - 3
*
* we can represent it as:
* 1. Edge list:
* const graph = [
* [0, 2],
* [1, 2],
* [1, 3],
* [2, 3]
* ]
*
* 2. Adjacent list
* - with arrays:
* const graph = [[2], [2, 3], [0, 1, 3], [1, 2]]
* - with an object:
* const graph = {
* 0: [2],
* 1: [2, 3],
* 2: [0, 1, 3],
* 3: [1, 2]
* }
*
* 3. Adjacent matrix:
* - with arrays:
* const graph = [
* [0, 0, 1, 0],
* [0, 0, 1, 1],
* [1, 1, 0, 1],
* [0, 1, 1, 0],
* ]
* - with an object:
* const graph = {
* 0: [0, 0, 1, 0],
* 1: [0, 0, 1, 1],
* 2: [1, 1, 0, 1],
* 3: [0, 1, 1, 0],
* }
*/classGraph{constructor(){/**
* @type{Number} number of nodes from the graph
* @public */this.nodes=0/**
* @type{Object<number,[number]>} an object which every key is a node and each value is an array of nodes to which this node is connected
* @public */this.adjacentList={}}/**
* @param{Number}value node of the Graph
*/addVertex(value){this.adjacentList[value]=[]this.nodes++}/**
* @param{Number}node1 * @param{Number}node2 */addEdges(node1, node2){this.adjacentList[node1].push(node2)this.adjacentList[node2].push(node1)}}