A graph is a set of vertices (a.k.a. nodes) connected by edges. You can model networks (maps, prerequisites, social links) with graphs.
u → v. Undirected: edges are two-way u — v.1.u and v share an edge. Degree of v: number of incident edges (for directed: in-degree / out-degree).O(n + m). Great for sparse graphs.n × n table where entry [i][j] is 1 (or weight) if edge i→j exists; else 0/∞. Space O(n²). Great for dense graphs and instant hasEdge(i,j).| Operation | Time | Space | Notes |
|---|---|---|---|
| BFS / DFS (all vertices) | O(n + m) | O(n) | Visit each vertex & edge once |
| Topological sort (Kahn) | O(n + m) | O(n) | Maintains queue of zero in-degree |
| Cycle detection (undirected) | O(n + m) | O(n) | DFS with parent |
| Cycle detection (directed) | O(n + m) | O(n) | DFS colors/stack |
| Bipartite check | O(n + m) | O(n) | BFS/DFS 2-color |
| Connected components | O(n + m) | O(n) | Using BFS/DFS or DSU |
| Adjacency matrix build | O(n²) fill + O(m) set | O(n²) | We animate the fill |
Need shortest paths (Dijkstra, A*, Bellman-Ford, BFS on unweighted)? See dijkstra.html (your lab).
from collections import deque, defaultdict
class Graph:
def __init__(self, n, directed=False):
self.n = n
self.dir = directed
self.adj = [[] for _ in range(n)] # (v, w) pairs if weighted
def add_edge(self, u, v, w=1):
self.adj[u].append((v, w))
if not self.dir:
self.adj[v].append((u, w))
def bfs(self, s):
vis=[False]*self.n; q=deque([s]); vis[s]=True; order=[]
while q:
u=q.popleft(); order.append(u)
for v,_ in self.adj[u]:
if not vis[v]: vis[v]=True; q.append(v)
return order
def dfs(self, s):
vis=[False]*self.n; order=[]
def go(u):
vis[u]=True; order.append(u)
for v,_ in self.adj[u]:
if not vis[v]: go(v)
go(s); return order
def topo_kahn(self):
indeg=[0]*self.n
for u in range(self.n):
for v,_ in self.adj[u]: indeg[v]+=1
from collections import deque
q=deque([i for i in range(self.n) if indeg[i]==0])
out=[]
while q:
u=q.popleft(); out.append(u)
for v,_ in self.adj[u]:
indeg[v]-=1
if indeg[v]==0: q.append(v)
return out if len(out)==self.n else None # None if cycle
def has_cycle_undirected(self):
vis=[False]*self.n
def go(u,p):
vis[u]=True
for v,_ in self.adj[u]:
if not vis[v]:
if go(v,u): return True
elif v!=p:
return True
return False
for i in range(self.n):
if not vis[i] and go(i,-1): return True
return False
def has_cycle_directed(self):
color=[0]*self.n # 0=white,1=gray,2=black
def go(u):
color[u]=1
for v,_ in self.adj[u]:
if color[v]==1: return True
if color[v]==0 and go(v): return True
color[u]=2; return False
return any(color[i]==0 and go(i) for i in range(self.n))
def bipartite(self):
color=[-1]*self.n
from collections import deque
for s in range(self.n):
if color[s]!=-1: continue
color[s]=0; q=deque([s])
while q:
u=q.popleft()
for v,_ in self.adj[u]:
if color[v]==-1:
color[v]=color[u]^1; q.append(v)
elif color[v]==color[u]:
return False, None
return True, color
def components(self):
vis=[False]*self.n; comps=[]
for s in range(self.n):
if vis[s]: continue
stack=[s]; vis[s]=True; cur=[]
while stack:
u=stack.pop(); cur.append(u)
for v,_ in self.adj[u]:
if not vis[v]: vis[v]=True; stack.append(v)
comps.append(cur)
return comps
class DSU:
def __init__(self, n):
self.p=list(range(n)); self.r=[0]*n
def find(self,x):
if self.p[x]!=x: self.p[x]=self.find(self.p[x])
return self.p[x]
def union(self,a,b):
ra,rb=self.find(a),self.find(b)
if ra==rb: return False
if self.r[ra]<self.r[rb]: ra,rb=rb,ra
self.p[rb]=ra
if self.r[ra]==self.r[rb]: self.r[ra]+=1
return True
networkx equivalents# import networkx as nx # G = nx.Graph() or nx.DiGraph() # G.add_edge(u, v, weight=w) # list(nx.bfs_tree(G, s)) # list(nx.dfs_preorder_nodes(G, s)) # list(nx.topological_sort(DAG)) # nx.is_directed_acyclic_graph(G) # nx.is_bipartite(G) # list(nx.connected_components(G))
import java.util.*;
class Graph {
final int n; final boolean dir;
final ArrayList<int[]>[] adj; // (v,w)
@SuppressWarnings("unchecked")
Graph(int n, boolean dir){
this.n=n; this.dir=dir;
adj=new ArrayList[n];
for(int i=0;i<n;i++) adj[i]=new ArrayList<>();
}
void addEdge(int u,int v,int w){
adj[u].add(new int[]{v,w});
if(!dir) adj[v].add(new int[]{u,w});
}
List<Integer> bfs(int s){
boolean[] vis=new boolean[n];
ArrayDeque<Integer> q=new ArrayDeque<>();
List<Integer> out=new ArrayList<>();
vis[s]=true; q.add(s);
while(!q.isEmpty()){
int u=q.poll(); out.add(u);
for(int[] e:adj[u]){ int v=e[0]; if(!vis[v]){ vis[v]=true; q.add(v); } }
}
return out;
}
List<Integer> dfs(int s){
boolean[] vis=new boolean[n];
List<Integer> out=new ArrayList<>();
Deque<Integer> st=new ArrayDeque<>();
st.push(s);
while(!st.isEmpty()){
int u=st.pop();
if(vis[u]) continue; vis[u]=true; out.add(u);
List<int[]> nbrs=adj[u];
for(int i=nbrs.size()-1;i>=0;i--){ int v=nbrs.get(i)[0]; if(!vis[v]) st.push(v); }
}
return out;
}
List<Integer> topoKahn(){
int[] indeg=new int[n];
for(int u=0;u<n;u++) for(int[] e:adj[u]) indeg[e[0]]++;
ArrayDeque<Integer> q=new ArrayDeque<>();
for(int i=0;i<n;i++) if(indeg[i]==0) q.add(i);
List<Integer> out=new ArrayList<>();
while(!q.isEmpty()){
int u=q.poll(); out.add(u);
for(int[] e:adj[u]){ int v=e[0]; if(--indeg[v]==0) q.add(v); }
}
return out.size()==n?out:null;
}
boolean hasCycleUndirected(){
boolean[] vis=new boolean[n];
for(int s=0;s<n;s++){
if(vis[s]) continue;
ArrayDeque<int[]> st=new ArrayDeque<>(); // (u,parent)
st.push(new int[]{s,-1});
while(!st.isEmpty()){
int[] t=st.pop(); int u=t[0],p=t[1];
if(vis[u]) continue; vis[u]=true;
for(int[] e:adj[u]){
int v=e[0];
if(!vis[v]) st.push(new int[]{v,u});
else if(v!=p) return true;
}
}
}
return false;
}
boolean hasCycleDirected(){
int[] color=new int[n]; // 0 W,1 G,2 B
for(int s=0;s<n;s++){
if(color[s]!=0) continue;
ArrayDeque<int[]> st=new ArrayDeque<>(); // (u,itIndex)
st.push(new int[]{s,0});
ArrayList<Iterator<int[]>> its=new ArrayList<>(Collections.nCopies(n,null));
while(!st.isEmpty()){
int[] t=st.peek(); int u=t[0]; int itIdx=t[1];
if(color[u]==0){ color[u]=1; its.set(u, adj[u].iterator()); }
Iterator<int[]> it=its.get(u);
if(it.hasNext()){
int v=it.next()[0];
if(color[v]==1) return true;
if(color[v]==0) st.push(new int[]{v,0});
}else{
color[u]=2; st.pop();
}
}
}
return false;
}
boolean bipartite(){
int[] col=new int[n];
Arrays.fill(col,-1);
for(int s=0;s<n;s++){
if(col[s]!=-1) continue;
ArrayDeque<Integer> q=new ArrayDeque<>();
col[s]=0; q.add(s);
while(!q.isEmpty()){
int u=q.poll();
for(int[] e:adj[u]){
int v=e[0];
if(col[v]==-1){ col[v]=col[u]^1; q.add(v); }
else if(col[v]==col[u]) return false;
}
}
}
return true;
}
List<List<Integer>> components(){
boolean[] vis=new boolean[n];
List<List<Integer>> comps=new ArrayList<>();
for(int s=0;s<n;s++){
if(vis[s]) continue;
ArrayDeque<Integer> st=new ArrayDeque<>();
st.push(s); vis[s]=true; List<Integer> cur=new ArrayList<>();
while(!st.isEmpty()){
int u=st.pop(); cur.add(u);
for(int[] e:adj[u]){ int v=e[0]; if(!vis[v]){ vis[v]=true; st.push(v); } }
}
comps.add(cur);
}
return comps;
}
}
class DSU{
int[] p, r;
DSU(int n){ p=new int[n]; r=new int[n]; for(int i=0;i<n;i++) p[i]=i; }
int find(int x){ return p[x]==x?x:(p[x]=find(p[x])); }
boolean union(int a,int b){
int ra=find(a), rb=find(b); if(ra==rb) return false;
if(r[ra]<r[rb]){ int t=ra; ra=rb; rb=t; }
p[rb]=ra; if(r[ra]==r[rb]) r[ra]++; return true;
}
}
// Queue: ArrayDeque<Integer> // Priority queue (not needed here, but for SSSP): new PriorityQueue<int[]>(Comparator.comparingInt(a->a[1]));
#include <bits/stdc++.h>
using namespace std;
struct Graph{
int n; bool dir; vector<vector<pair<int,int>>> adj;
Graph(int n,bool dir):n(n),dir(dir),adj(n){}
void add_edge(int u,int v,int w=1){
adj[u].push_back({v,w});
if(!dir) adj[v].push_back({u,w});
}
vector<int> bfs(int s){
vector<int> vis(n); queue<int> q; vector<int> out;
vis[s]=1; q.push(s);
while(!q.empty()){
int u=q.front(); q.pop(); out.push_back(u);
for(auto [v,w]:adj[u]) if(!vis[v]){ vis[v]=1; q.push(v); }
}
return out;
}
vector<int> dfs(int s){
vector<int> vis(n); vector<int> out; stack<int> st; st.push(s);
while(!st.empty()){
int u=st.top(); st.pop();
if(vis[u]) continue; vis[u]=1; out.push_back(u);
for(int i=(int)adj[u].size()-1;i>=0;--i){ int v=adj[u][i].first; if(!vis[v]) st.push(v); }
}
return out;
}
vector<int> topo_kahn(){
vector<int> indeg(n); for(int u=0;u<n;u++) for(auto [v,w]:adj[u]) indeg[v]++;
queue<int> q; for(int i=0;i<n;i++) if(indeg[i]==0) q.push(i);
vector<int> out;
while(!q.empty()){
int u=q.front(); q.pop(); out.push_back(u);
for(auto [v,w]:adj[u]) if(--indeg[v]==0) q.push(v);
}
if((int)out.size()!=n) return {};
return out;
}
bool has_cycle_undirected(){
vector<int> vis(n);
for(int s=0;s<n;s++){
if(vis[s]) continue;
stack<pair<int,int>> st; st.push({s,-1});
while(!st.empty()){
auto [u,p]=st.top(); st.pop();
if(vis[u]) continue; vis[u]=1;
for(auto [v,w]:adj[u]){
if(!vis[v]) st.push({v,u});
else if(v!=p) return true;
}
}
}
return false;
}
bool has_cycle_directed(){
vector<int> color(n); // 0W,1G,2B
function<bool(int)> go = [&](int u){
color[u]=1;
for(auto [v,w]:adj[u]){
if(color[v]==1) return true;
if(color[v]==0 && go(v)) return true;
}
color[u]=2; return false;
};
for(int i=0;i<n;i++) if(color[i]==0 && go(i)) return true;
return false;
}
bool bipartite(){
vector<int> col(n,-1);
for(int s=0;s<n;s++){
if(col[s]!=-1) continue;
queue<int> q; col[s]=0; q.push(s);
while(!q.empty()){
int u=q.front(); q.pop();
for(auto [v,w]:adj[u]){
if(col[v]==-1){ col[v]=col[u]^1; q.push(v); }
else if(col[v]==col[u]) return false;
}
}
}
return true;
}
vector<vector<int>> components(){
vector<int> vis(n); vector<vector<int>> comps;
for(int s=0;s<n;s++){
if(vis[s]) continue;
vector<int> cur; stack<int> st; st.push(s); vis[s]=1;
while(!st.empty()){
int u=st.top(); st.pop(); cur.push_back(u);
for(auto [v,w]:adj[u]) if(!vis[v]){ vis[v]=1; st.push(v); }
}
comps.push_back(cur);
}
return comps;
}
};
struct DSU{
vector<int> p,r;
DSU(int n):p(n),r(n,0){ iota(p.begin(),p.end(),0); }
int find(int x){ return p[x]==x?x:p[x]=find(p[x]); }
bool unite(int a,int b){
a=find(a); b=find(b); if(a==b) return false;
if(r[a]<r[b]) swap(a,b);
p[b]=a; if(r[a]==r[b]) r[a]++; return true;
}
};
// queue<int>, stack<int>, vector<vector<pair<int,int>>>, etc. // For shortest paths use priority_queue (see your dijkstra.html lab).
n.