算法习题之图的运算

算法习题之图的运算

  • 习题1 图的宽度优先遍历
  • 习题2 图的深度优先遍历
  • 习题3 图的拓扑排序算法
  • 习题4 最小生成树算法之Kruskal
  • 习题5 最小生成树算法之Prim
  • 习题6 Dijkstra算法
  • 习题7 NetworkDelayTime

1)由点的集合和边的集合构成

2)虽然存在有向图和无向图的概念,但实际上都可以用有向图来表达

3)边上可能带有权值

图结构的表达

1)邻接表法

2)邻接矩阵法

3)除此之外还有其他众多的方式

图的面试题如何搞定

1)先用自己最熟练的方式,实现图结构的表达

2)在自己熟悉的结构上,实现所有常用的图算法作为模板

3)把面试题提供的图结构转化为自己熟悉的图结构,再调用模板或改写即可

习题1 图的宽度优先遍历

// 从node出发,进行宽度优先遍历
	public static void bfs(Node start) {
		if (start == null) {
			return;
		}
		Queue queue = new LinkedList<>();
		HashSet set = new HashSet<>();
		queue.add(start);
		set.add(start);
		while (!queue.isEmpty()) {
			Node cur = queue.poll();
			System.out.println(cur.value);
			for (Node next : cur.nexts) {
				if (!set.contains(next)) {
					set.add(next);
					queue.add(next);
				}
			}
		}
	}
	
public class Node {
	public int value;
	public int in;
	public int out;
	public ArrayList nexts;
	public ArrayList edges;

	public Node(int value) {
		this.value = value;
		in = 0;
		out = 0;
		nexts = new ArrayList<>();
		edges = new ArrayList<>();
	}
}

习题2 图的深度优先遍历

public static void dfs(Node node) {
		if (node == null) {
			return;
		}
		Stack stack = new Stack<>();
		HashSet set = new HashSet<>();
		stack.add(node);
		set.add(node);
		System.out.println(node.value);
		while (!stack.isEmpty()) {
			Node cur = stack.pop();
			for (Node next : cur.nexts) {
				if (!set.contains(next)) {
					stack.push(cur);
					stack.push(next);
					set.add(next);
					System.out.println(next.value);
					break;
				}
			}
		}
	}
	
public class Node {
	public int value;
	public int in;
	public int out;
	public ArrayList nexts;
	public ArrayList edges;

	public Node(int value) {
		this.value = value;
		in = 0;
		out = 0;
		nexts = new ArrayList<>();
		edges = new ArrayList<>();
	}
}

图的拓扑排序算法

1)在图中找到所有入度为0的点输出
2)把所有入度为0的点在图中删掉,继续找入度为0的点输出,周而复始
3)图的所有点都被删除后,依次输出的顺序就是拓扑排序

要求:有向图且其中没有环
应用:事件安排、编译顺序

习题3 图的拓扑排序算法

public class Node {
	public int value;
	public int in;
	public int out;
	public ArrayList nexts;
	public ArrayList edges;

	public Node(int value) {
		this.value = value;
		in = 0;
		out = 0;
		nexts = new ArrayList<>();
		edges = new ArrayList<>();
	}

public static class DirectedGraphNode {
		public int label;
		public ArrayList neighbors;

		public DirectedGraphNode(int x) {
			label = x;
			neighbors = new ArrayList();
		}
	}

	// 提交下面的
	public static ArrayList topSort(ArrayList graph) {
		HashMap indegreeMap = new HashMap<>();
		for (DirectedGraphNode cur : graph) {
			indegreeMap.put(cur, 0);
		}
		for (DirectedGraphNode cur : graph) {
			for (DirectedGraphNode next : cur.neighbors) {
				indegreeMap.put(next, indegreeMap.get(next) + 1);
			}
		}
		Queue zeroQueue = new LinkedList<>();
		for (DirectedGraphNode cur : indegreeMap.keySet()) {
			if (indegreeMap.get(cur) == 0) {
				zeroQueue.add(cur);
			}
		}
		ArrayList ans = new ArrayList<>();
		while (!zeroQueue.isEmpty()) {
			DirectedGraphNode cur = zeroQueue.poll();
			ans.add(cur);
			for (DirectedGraphNode next : cur.neighbors) {
				indegreeMap.put(next, indegreeMap.get(next) - 1);
				if (indegreeMap.get(next) == 0) {
					zeroQueue.offer(next);
				}
			}
		}
		return ans;
	}

public static class DirectedGraphNode {
		public int label;
		public ArrayList neighbors;

		public DirectedGraphNode(int x) {
			label = x;
			neighbors = new ArrayList();
		}
	}

	// 提交下面的
	public static class Record {
		public DirectedGraphNode node;
		public int deep;

		public Record(DirectedGraphNode n, int o) {
			node = n;
			deep = o;
		}
	}

	public static class MyComparator implements Comparator {

		@Override
		public int compare(Record o1, Record o2) {
			return o2.deep - o1.deep;
		}
	}

	public static ArrayList topSort(ArrayList graph) {
		HashMap order = new HashMap<>();
		for (DirectedGraphNode cur : graph) {
			f(cur, order);
		}
		ArrayList recordArr = new ArrayList<>();
		for (Record r : order.values()) {
			recordArr.add(r);
		}
		recordArr.sort(new MyComparator());
		ArrayList ans = new ArrayList();
		for (Record r : recordArr) {
			ans.add(r.node);
		}
		return ans;
	}

	public static Record f(DirectedGraphNode cur, HashMap order) {
		if (order.containsKey(cur)) {
			return order.get(cur);
		}
		int follow = 0;
		for (DirectedGraphNode next : cur.neighbors) {
			follow = Math.max(follow, f(next, order).deep);
		}
		Record ans = new Record(cur, follow + 1);
		order.put(cur, ans);
		return ans;
	}
	
public static class DirectedGraphNode {
		public int label;
		public ArrayList neighbors;

		public DirectedGraphNode(int x) {
			label = x;
			neighbors = new ArrayList();
		}
	}

	// 提交下面的
	public static class Record {
		public DirectedGraphNode node;
		public long nodes;

		public Record(DirectedGraphNode n, long o) {
			node = n;
			nodes = o;
		}
	}

	public static class MyComparator implements Comparator {

		@Override
		public int compare(Record o1, Record o2) {
			return o1.nodes == o2.nodes ? 0 : (o1.nodes > o2.nodes ? -1 : 1);
		}
	}

	public static ArrayList topSort(ArrayList graph) {
		HashMap order = new HashMap<>();
		for (DirectedGraphNode cur : graph) {
			f(cur, order);
		}
		ArrayList recordArr = new ArrayList<>();
		for (Record r : order.values()) {
			recordArr.add(r);
		}
		recordArr.sort(new MyComparator());
		ArrayList ans = new ArrayList();
		for (Record r : recordArr) {
			ans.add(r.node);
		}
		return ans;
	}

	// 当前来到cur点,请返回cur点所到之处,所有的点次!
	// 返回(cur,点次)
	// 缓存!!!!!order   
	//  key : 某一个点的点次,之前算过了!
	//  value : 点次是多少
	public static Record f(DirectedGraphNode cur, HashMap order) {
		if (order.containsKey(cur)) {
			return order.get(cur);
		}
		// cur的点次之前没算过!
		long nodes = 0;
		for (DirectedGraphNode next : cur.neighbors) {
			nodes += f(next, order).nodes;
		}
		Record ans = new Record(cur, nodes + 1);
		order.put(cur, ans);
		return ans;
	}
	
// directed graph and no loop
	public static List sortedTopology(Graph graph) {
		// key 某个节点   value 剩余的入度
		HashMap inMap = new HashMap<>();
		// 只有剩余入度为0的点,才进入这个队列
		Queue zeroInQueue = new LinkedList<>();
		for (Node node : graph.nodes.values()) {
			inMap.put(node, node.in);
			if (node.in == 0) {
				zeroInQueue.add(node);
			}
		}
		List result = new ArrayList<>();
		while (!zeroInQueue.isEmpty()) {
			Node cur = zeroInQueue.poll();
			result.add(cur);
			for (Node next : cur.nexts) {
				inMap.put(next, inMap.get(next) - 1);
				if (inMap.get(next) == 0) {
					zeroInQueue.add(next);
				}
			}
		}
		return result;
	}

最小生成树算法之Kruskal

1)总是从权值最小的边开始考虑,依次考察权值依次变大的边
2)当前的边要么进入最小生成树的集合,要么丢弃
3)如果当前的边进入最小生成树的集合中不会形成环,就要当前边
4)如果当前的边进入最小生成树的集合中会形成环,就不要当前边
5)考察完所有边之后,最小生成树的集合也得到了

习题4 最小生成树算法之Kruskal

// Union-Find Set
	public static class UnionFind {
		// key 某一个节点, value key节点往上的节点
		private HashMap fatherMap;
		// key 某一个集合的代表节点, value key所在集合的节点个数
		private HashMap sizeMap;

		public UnionFind() {
			fatherMap = new HashMap();
			sizeMap = new HashMap();
		}
		
		public void makeSets(Collection nodes) {
			fatherMap.clear();
			sizeMap.clear();
			for (Node node : nodes) {
				fatherMap.put(node, node);
				sizeMap.put(node, 1);
			}
		}

		private Node findFather(Node n) {
			Stack path = new Stack<>();
			while(n != fatherMap.get(n)) {
				path.add(n);
				n = fatherMap.get(n);
			}
			while(!path.isEmpty()) {
				fatherMap.put(path.pop(), n);
			}
			return n;
		}

		public boolean isSameSet(Node a, Node b) {
			return findFather(a) == findFather(b);
		}

		public void union(Node a, Node b) {
			if (a == null || b == null) {
				return;
			}
			Node aDai = findFather(a);
			Node bDai = findFather(b);
			if (aDai != bDai) {
				int aSetSize = sizeMap.get(aDai);
				int bSetSize = sizeMap.get(bDai);
				if (aSetSize <= bSetSize) {
					fatherMap.put(aDai, bDai);
					sizeMap.put(bDai, aSetSize + bSetSize);
					sizeMap.remove(aDai);
				} else {
					fatherMap.put(bDai, aDai);
					sizeMap.put(aDai, aSetSize + bSetSize);
					sizeMap.remove(bDai);
				}
			}
		}
	}
	

	public static class EdgeComparator implements Comparator {

		@Override
		public int compare(Edge o1, Edge o2) {
			return o1.weight - o2.weight;
		}

	}

	public static Set kruskalMST(Graph graph) {
		UnionFind unionFind = new UnionFind();
		unionFind.makeSets(graph.nodes.values());
		// 从小的边到大的边,依次弹出,小根堆!
		PriorityQueue priorityQueue = new PriorityQueue<>(new EdgeComparator());
		for (Edge edge : graph.edges) { // M 条边
			priorityQueue.add(edge);  // O(logM)
		}
		Set result = new HashSet<>();
		while (!priorityQueue.isEmpty()) { // M 条边
			Edge edge = priorityQueue.poll(); // O(logM)
			if (!unionFind.isSameSet(edge.from, edge.to)) { // O(1)
				result.add(edge);
				unionFind.union(edge.from, edge.to);
			}
		}
		return result;
	}

public class Edge {
	public int weight;
	public Node from;
	public Node to;

	public Edge(int weight, Node from, Node to) {
		this.weight = weight;
		this.from = from;
		this.to = to;
	}

}

最小生成树算法之Prim

1)可以从任意节点出发来寻找最小生成树
2)某个点加入到被选取的点中后,解锁这个点出发的所有新的边
3)在所有解锁的边中选最小的边,然后看看这个边会不会形成环
4)如果会,不要当前边,继续考察剩下解锁的边中最小的边,重复3)
5)如果不会,要当前边,将该边的指向点加入到被选取的点中,重复2)
6)当所有点都被选取,最小生成树就得到了

习题5 最小生成树算法之Prim

public static class EdgeComparator implements Comparator {

		@Override
		public int compare(Edge o1, Edge o2) {
			return o1.weight - o2.weight;
		}

	}

	public static Set primMST(Graph graph) {
		// 解锁的边进入小根堆
		PriorityQueue priorityQueue = new PriorityQueue<>(new EdgeComparator());

		// 哪些点被解锁出来了
		HashSet nodeSet = new HashSet<>();
		
		
		
		Set result = new HashSet<>(); // 依次挑选的的边在result里

		for (Node node : graph.nodes.values()) { // 随便挑了一个点
			// node 是开始点
			if (!nodeSet.contains(node)) {
				nodeSet.add(node);
				for (Edge edge : node.edges) { // 由一个点,解锁所有相连的边
					priorityQueue.add(edge);
				}
				while (!priorityQueue.isEmpty()) {
					Edge edge = priorityQueue.poll(); // 弹出解锁的边中,最小的边
					Node toNode = edge.to; // 可能的一个新的点
					if (!nodeSet.contains(toNode)) { // 不含有的时候,就是新的点
						nodeSet.add(toNode);
						result.add(edge);
						for (Edge nextEdge : toNode.edges) {
							priorityQueue.add(nextEdge);
						}
					}
				}
			}
			// break;
		}
		return result;
	}

	// 请保证graph是连通图
	// graph[i][j]表示点i到点j的距离,如果是系统最大值代表无路
	// 返回值是最小连通图的路径之和
	public static int prim(int[][] graph) {
		int size = graph.length;
		int[] distances = new int[size];
		boolean[] visit = new boolean[size];
		visit[0] = true;
		for (int i = 0; i < size; i++) {
			distances[i] = graph[0][i];
		}
		int sum = 0;
		for (int i = 1; i < size; i++) {
			int minPath = Integer.MAX_VALUE;
			int minIndex = -1;
			for (int j = 0; j < size; j++) {
				if (!visit[j] && distances[j] < minPath) {
					minPath = distances[j];
					minIndex = j;
				}
			}
			if (minIndex == -1) {
				return sum;
			}
			visit[minIndex] = true;
			sum += minPath;
			for (int j = 0; j < size; j++) {
				if (!visit[j] && distances[j] > graph[minIndex][j]) {
					distances[j] = graph[minIndex][j];
				}
			}
		}
		return sum;
	}

public class Edge {
	public int weight;
	public Node from;
	public Node to;

	public Edge(int weight, Node from, Node to) {
		this.weight = weight;
		this.from = from;
		this.to = to;
	}

}

Dijkstra算法

1)Dijkstra算法必须指定一个源点
2)生成一个源点到各个点的最小距离表,一开始只有一条记录,即原点到自己的最小距离为0,源点到其他所有点的最小距离都为正无穷大
3)从距离表中拿出没拿过记录里的最小记录,通过这个点发出的边,更新源点到各个点的最小距离表,不断重复这一步
4)源点到所有的点记录如果都被拿过一遍,过程停止,最小距离表得到了

习题6 Dijkstra算法

public static HashMap dijkstra1(Node from) {
		HashMap distanceMap = new HashMap<>();
		distanceMap.put(from, 0);
		// 打过对号的点
		HashSet selectedNodes = new HashSet<>();
		Node minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
		while (minNode != null) {
			//  原始点  ->  minNode(跳转点)   最小距离distance
			int distance = distanceMap.get(minNode);
			for (Edge edge : minNode.edges) {
				Node toNode = edge.to;
				if (!distanceMap.containsKey(toNode)) {
					distanceMap.put(toNode, distance + edge.weight);
				} else { // toNode 
					distanceMap.put(edge.to, Math.min(distanceMap.get(toNode), distance + edge.weight));
				}
			}
			selectedNodes.add(minNode);
			minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
		}
		return distanceMap;
	}

	public static Node getMinDistanceAndUnselectedNode(HashMap distanceMap, HashSet touchedNodes) {
		Node minNode = null;
		int minDistance = Integer.MAX_VALUE;
		for (Entry entry : distanceMap.entrySet()) {
			Node node = entry.getKey();
			int distance = entry.getValue();
			if (!touchedNodes.contains(node) && distance < minDistance) {
				minNode = node;
				minDistance = distance;
			}
		}
		return minNode;
	}

	public static class NodeRecord {
		public Node node;
		public int distance;

		public NodeRecord(Node node, int distance) {
			this.node = node;
			this.distance = distance;
		}
	}

	public static class NodeHeap {
		private Node[] nodes; // 实际的堆结构
		// key 某一个node, value 上面堆中的位置
		private HashMap heapIndexMap;
		// key 某一个节点, value 从源节点出发到该节点的目前最小距离
		private HashMap distanceMap;
		private int size; // 堆上有多少个点

		public NodeHeap(int size) {
			nodes = new Node[size];
			heapIndexMap = new HashMap<>();
			distanceMap = new HashMap<>();
			size = 0;
		}

		public boolean isEmpty() {
			return size == 0;
		}

		// 有一个点叫node,现在发现了一个从源节点出发到达node的距离为distance
		// 判断要不要更新,如果需要的话,就更新
		public void addOrUpdateOrIgnore(Node node, int distance) {
			if (inHeap(node)) {
				distanceMap.put(node, Math.min(distanceMap.get(node), distance));
				insertHeapify(node, heapIndexMap.get(node));
			}
			if (!isEntered(node)) {
				nodes[size] = node;
				heapIndexMap.put(node, size);
				distanceMap.put(node, distance);
				insertHeapify(node, size++);
			}
		}

		public NodeRecord pop() {
			NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0]));
			swap(0, size - 1);
			heapIndexMap.put(nodes[size - 1], -1);
			distanceMap.remove(nodes[size - 1]);
			// free C++同学还要把原本堆顶节点析构,对java同学不必
			nodes[size - 1] = null;
			heapify(0, --size);
			return nodeRecord;
		}

		private void insertHeapify(Node node, int index) {
			while (distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index - 1) / 2])) {
				swap(index, (index - 1) / 2);
				index = (index - 1) / 2;
			}
		}

		private void heapify(int index, int size) {
			int left = index * 2 + 1;
			while (left < size) {
				int smallest = left + 1 < size && distanceMap.get(nodes[left + 1]) < distanceMap.get(nodes[left])
						? left + 1
						: left;
				smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest : index;
				if (smallest == index) {
					break;
				}
				swap(smallest, index);
				index = smallest;
				left = index * 2 + 1;
			}
		}

		private boolean isEntered(Node node) {
			return heapIndexMap.containsKey(node);
		}

		private boolean inHeap(Node node) {
			return isEntered(node) && heapIndexMap.get(node) != -1;
		}

		private void swap(int index1, int index2) {
			heapIndexMap.put(nodes[index1], index2);
			heapIndexMap.put(nodes[index2], index1);
			Node tmp = nodes[index1];
			nodes[index1] = nodes[index2];
			nodes[index2] = tmp;
		}
	}

	// 改进后的dijkstra算法
	// 从head出发,所有head能到达的节点,生成到达每个节点的最小路径记录并返回
	public static HashMap dijkstra2(Node head, int size) {
		NodeHeap nodeHeap = new NodeHeap(size);
		nodeHeap.addOrUpdateOrIgnore(head, 0);
		HashMap result = new HashMap<>();
		while (!nodeHeap.isEmpty()) {
			NodeRecord record = nodeHeap.pop();
			Node cur = record.node;
			int distance = record.distance;
			for (Edge edge : cur.edges) {
				nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance);
			}
			result.put(cur, distance);
		}
		return result;
	}

// 点结构的描述
public class Node {
	public int value;
	public int in;
	public int out;
	public ArrayList nexts;
	public ArrayList edges;

	public Node(int value) {
		this.value = value;
		in = 0;
		out = 0;
		nexts = new ArrayList<>();
		edges = new ArrayList<>();
	}
}

public class Edge {
	public int weight;
	public Node from;
	public Node to;

	public Edge(int weight, Node from, Node to) {
		this.weight = weight;
		this.from = from;
		this.to = to;
	}

}

习题7 NetworkDelayTime

// 方法一 : 普通堆 + 屏蔽已经计算过的点
	public static int networkDelayTime1(int[][] times, int n, int k) {
		ArrayList> nexts = new ArrayList<>();
		for (int i = 0; i <= n; i++) {
			nexts.add(new ArrayList<>());
		}
		for (int[] delay : times) {
			nexts.get(delay[0]).add(new int[] { delay[1], delay[2] });
		}
		PriorityQueue heap = new PriorityQueue<>((a, b) -> a[1] - b[1]);
		heap.add(new int[] { k, 0 });
		boolean[] used = new boolean[n + 1];
		int num = 0;
		int max = 0;
		while (!heap.isEmpty() && num < n) {
			int[] record = heap.poll();
			int cur = record[0];
			int delay = record[1];
			if (used[cur]) {
				continue;
			}
			used[cur] = true;
			num++;
			max = Math.max(max, delay);
			for (int[] next : nexts.get(cur)) {
				heap.add(new int[] { next[0], delay + next[1] });
			}
		}
		return num < n ? -1 : max;
	}

	// 方法二 : 加强堆的解法
	public static int networkDelayTime2(int[][] times, int n, int k) {
		ArrayList> nexts = new ArrayList<>();
		for (int i = 0; i <= n; i++) {
			nexts.add(new ArrayList<>());
		}
		for (int[] delay : times) {
			nexts.get(delay[0]).add(new int[] { delay[1], delay[2] });
		}
		Heap heap = new Heap(n);
		heap.add(k, 0);
		int num = 0;
		int max = 0;
		while (!heap.isEmpty()) {
			int[] record = heap.poll();
			int cur = record[0];
			int delay = record[1];
			num++;
			max = Math.max(max, delay);
			for (int[] next : nexts.get(cur)) {
				heap.add(next[0], delay + next[1]);
			}
		}
		return num < n ? -1 : max;
	}

	// 加强堆
	public static class Heap {
		public boolean[] used;
		public int[][] heap;
		public int[] hIndex;
		public int size;

		public Heap(int n) {
			used = new boolean[n + 1];
			heap = new int[n + 1][2];
			hIndex = new int[n + 1];
			Arrays.fill(hIndex, -1);
			size = 0;
		}

		public void add(int cur, int delay) {
			if (used[cur]) {
				return;
			}
			if (hIndex[cur] == -1) {
				heap[size][0] = cur;
				heap[size][1] = delay;
				hIndex[cur] = size;
				heapInsert(size++);
			} else {
				int hi = hIndex[cur];
				if (delay <= heap[hi][1]) {
					heap[hi][1] = delay;
					heapInsert(hi);
				}
			}
		}

		public int[] poll() {
			int[] ans = heap[0];
			swap(0, --size);
			heapify(0);
			used[ans[0]] = true;
			hIndex[ans[0]] = -1;
			return ans;
		}

		public boolean isEmpty() {
			return size == 0;
		}

		private void heapInsert(int i) {
			int parent = (i - 1) / 2;
			while (heap[i][1] < heap[parent][1]) {
				swap(i, parent);
				i = parent;
				parent = (i - 1) / 2;
			}
		}

		private void heapify(int i) {
			int l = (i * 2) + 1;
			while (l < size) {
				int smallest = l + 1 < size && heap[l + 1][1] < heap[l][1] ? (l + 1) : l;
				smallest = heap[smallest][1] < heap[i][1] ? smallest : i;
				if (smallest == i) {
					break;
				}
				swap(smallest, i);
				i = smallest;
				l = (i * 2) + 1;
			}
		}

		private void swap(int i, int j) {
			int[] o1 = heap[i];
			int[] o2 = heap[j];
			int o1hi = hIndex[o1[0]];
			int o2hi = hIndex[o2[0]];
			heap[i] = o2;
			heap[j] = o1;
			hIndex[o1[0]] = o2hi;
			hIndex[o2[0]] = o1hi;
		}

	}

模板

// matrix 所有的边
	// N*3 的矩阵
	// [weight, from节点上面的值,to节点上面的值]
	// 
	// [ 5 , 0 , 7]
	// [ 3 , 0,  1]
	// 
	public static Graph createGraph(int[][] matrix) {
		Graph graph = new Graph();
		for (int i = 0; i < matrix.length; i++) {
			 // 拿到每一条边, matrix[i] 
			int weight = matrix[i][0];
			int from = matrix[i][1];
			int to = matrix[i][2];
			if (!graph.nodes.containsKey(from)) {
				graph.nodes.put(from, new Node(from));
			}
			if (!graph.nodes.containsKey(to)) {
				graph.nodes.put(to, new Node(to));
			}
			Node fromNode = graph.nodes.get(from);
			Node toNode = graph.nodes.get(to);
			Edge newEdge = new Edge(weight, fromNode, toNode);
			fromNode.nexts.add(toNode);
			fromNode.out++;
			toNode.in++;
			fromNode.edges.add(newEdge);
			graph.edges.add(newEdge);
		}
		return graph;
	}

public class Graph {
	public HashMap nodes;
	public HashSet edges;
	
	public Graph() {
		nodes = new HashMap<>();
		edges = new HashSet<>();
	}
}

你可能感兴趣的:(图,算法,java,开发语言)