关于图的一些简单算法2021年4月18日16:59:45

本文介绍了图的邻接表和邻接矩阵两种表示方法,并展示了如何用Java实现图的创建、广度优先搜索(BFS)、深度优先搜索(DFS)、拓扑排序以及Prim和Kruskal最小生成树算法。重点讲解了图的节点和边集的实现,以及宽度优先遍历和深度优先遍历的实现细节。
摘要由CSDN通过智能技术生成

1. 图的表示方法

  • 邻接表法
  • 邻接矩阵法

2. 推荐图的表达

// 图  点集和边集
public class Graph {
//   key 表示点的编号,Node 表示实际的点
	public HashMap<Integer,Node> nodes;
	public HashSet<Edge> edges;

	public Graph() {
		this.nodes = new HashMap<>();
		this.edges = new HashSet<>();
	}
}
//边集
public class Edge {
	//weight  边上的权值
	public int weight;
	//from  从哪个点出来
	public Node from;
	//to  到哪个点去
	public Node to;

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

// 点集
public class Node {
	// 点上的值 value  若是A点B点 可定义为String
	public int value;
	// in  一个点的入度
	public int in;
	// out  一个点的出度
	public int out;
	// nexts  由当前点出发 由它发散出去的边 他的直接邻居有哪些点
	public ArrayList<Node> nexts;
	// edges 属于该点的边有哪条
	public ArrayList<Edge> edges;

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


	/**
	 * matrix 所有的边
	 * N * 3矩阵
	 *[from   to   weight]  顺序可能会改变
	 * @param matrix
	 * @author liujh
	 * @since 2021/4/14 20:11
	 * @return com.ljh.datastruct.Graph
	 */
	public static Graph createGraph(Integer[][] matrix){

		Graph graph = new Graph();

		for (int i = 0; i < matrix.length; i++){
			Integer from = matrix[i][0];
			Integer to = matrix[i][1];
			Integer weigth = 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(weigth,fromNode,toNode);
			fromNode.nexts.add(toNode);
			fromNode.out++;
			toNode.in++;
			fromNode.edges.add(newEdge);
			graph.edges.add(newEdge);
		}
		return graph;
	}


/**
	 *
	 *
	 * 图的宽度优先遍历(又称广度优先搜索)   从node出发 进行宽度优先遍历
	 *  1. 利用队列实现
	 *  2. 从源节点开始依次按照宽度进队列,然后弹出
	 *  3. 每弹出一个节点,把该节点所有 没有 进过队列的邻接节点放入队列
	 *  4. 直到队列变空
	 * @param node node
	 * @author liujh
	 * @since 2021/4/14 20:40
	 */
	public static void bfs(Node node) {

		if (node == null) {
			return;
		}
		Queue<Node> queue = new LinkedList<>();
		HashSet<Node> set = new HashSet<>();

		queue.add(node);
		set.add(node);
		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);
				}
			}
		}
	}

/**
	 * 深度优先遍历
	 * 1.利用栈实现
	 * 2.从源节点开始把节点按照深度放入栈,然后弹出
	 * 3.每弹出一个点,把该节点下一个没有进过栈的邻接节点放入栈
	 * 4.直到栈变空
	 *
	 * @param node node
	 * @author liujh
	 * @since 2021/4/14 21:07
	 */
	public static void dfs(Node node) {
		if (node == null) {
			return;
		}
		Stack<Node> stack = new Stack<>();
		HashSet<Node> 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;
				}
			}
		}
	}
/**
	 * 拓扑排序
	 * * 适用范围: 要求有向图  且有入度为0的节点  且 没有环
	 *
	 * @param graph graph
	 * @return java.util.List<com.ljh.datastruct.Node>
	 * @author liujh
	 * @since 2021/4/18 14:20
	 */
	// direct graph and  no  loop
	public static List<Node> sortedTopology(Graph graph) {
		// key : 某一个node
		// value :  剩余的入度
		HashMap<Node, Integer> inMap = new HashMap<>();
		// 入度为0的点才能进入这个队列
		Queue<Node> zeroInQueue = new LinkedList<>();
		for (Node node : graph.nodes.values()) {
			inMap.put(node, node.in);
			if (node.in == 0) {
				zeroInQueue.add(node);
			}
		}
		// 拓扑排序的结果 依次加入result
		List<Node> 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 算法    生成最小生成树  按边考虑 从小到大 加上 看有没有形成环
	// 适用范围: 要求无向图


	//prim 算法  生成最小生成树
	// 使用范围 要求无向图

public static class EdgeComparator implements Comparator<Edge>{
		@Override
		public int compare(Edge o1, Edge o2) {
			return o1.weight-o2.weight;
		}
	}

	public static Set<Edge> primMST(Graph graph) {

		// 解锁的边进入小根堆
		PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
		Set<Node> set = new HashSet<>();
		// 依次挑选的边放在result 集合
		Set<Edge> result = new HashSet<>();

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



	// 暂略 
	// Dijkstra 算法  
	// 适用范围  可以有权值为负数的边   但是: 不能有 权值 累加和为负数的环

public static HashMap<Node,Integer> dijkstra1(Node head) {
			// 从head出发到所有点的最小距离
			// key: 从head出发到达的key
		    // value: 从head出发到达key的最小距离
			// 如果在表中 没有T的记录,含义是从head出发到T这个点的距离为正无穷
			// distanceMap  开始节点 到所有节点的距离
		HashMap<Node,Integer> distanceMap = new HashMap<>();
		distanceMap.put(head,0);
		// 已经求过的距离的节点, 存在selectNodes中, 以后再也不碰
		HashSet<Node> selectNodes = new HashSet<>();
		Node minNode = getMinDistanceAndUnSelectedNode(distanceMap,selectNodes);

		while (minNode != null) {
			int distance = distanceMap.get(minNode);
			for (Edge edge: minNode.edges) {
				Node toNode = edge.to;
				if (!distanceMap.containsKey(toNode)){
					distanceMap.put(toNode,distance + edge.weight);
				}
				distanceMap.put(edge.to,Math.min(distanceMap.get(toNode),distance+edge.weight));
			}
			selectNodes.add(minNode);
			minNode = getMinDistanceAndUnSelectedNode(distanceMap,selectNodes);
		}
		return distanceMap;
	}
	/**
	 * 在distanceMap 中选择最小的距离的一条记录 第二个参数洗数据  已经选过的点别要
	 *   两小时处 从新捋
	 * @param distanceMap
	 * @param touchedNodes
	 * @author liujh
	 * @since 2021/5/5 21:28
	 * @return com.ljh.datastruct.graph.Node
	 */
	private static Node getMinDistanceAndUnSelectedNode(HashMap<Node, Integer> distanceMap, HashSet<Node> touchedNodes) {

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





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值