匈牙利算法java版本

匈牙利算法java版本

  • 匈牙利算法java版本
    • 介绍
      • 名词解释
        • 二分图
        • 匹配
        • 最大匹配
        • 完美匹配
        • 交替路
        • 增广路
    • 实现代码java版本
      • APPjava
      • GraphMatchjava

介绍

匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名。匈牙利算法是基于Hall定理中充分性证明的思想,它是二部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法。(摘自百度百科)

名词解释

二分图:

二分图:简单来说,如果图中点可以被分为两组,并且使得所有边都跨越组的边界,则这就是一个二分图。准确地说:把一个图的顶点划分为两个不相交集 U 和 V ,使得每一条边都分别连接U 、 V 中的顶点。如果存在这样的划分,则此图为一个二分图,如图1。

匈牙利算法java版本_第1张图片

匹配:

在图论中,一个「匹配」(matching)是一个边的集合,其中任意两条边都没有公共顶点。例如,图 2 中红色的边就是图 2 的匹配。
我们定义匹配点、匹配边、未匹配点、非匹配边,它们的含义非常显然。例如图 2 中 1、2、4、5 为匹配点,其他顶点3、4为未匹配点;1-2为匹配边,其他边为非匹配边。
匈牙利算法java版本_第2张图片

最大匹配:

一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。图 3 是一个最大匹配,它包含 3 条匹配边。

匈牙利算法java版本_第3张图片

完美匹配:

如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。图 4 是一个完美匹配。显然,完美匹配一定是最大匹配(完美匹配的任何一个点都已经匹配,添加一条新的匹配边一定会与已有的匹配边冲突)。但并非每个图都存在完美匹配。

交替路:

从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边,形成的路径叫交替路,比如图3中的 5-6 -> 6-3 -> 3-4 -> 4-1 -> 1-2 。

匈牙利算法java版本_第4张图片

增广路:

1.有奇数条边。
2.起点在二分图的左半边,终点在右半边。
3.路径上的点一定是一个在左半边,一个在右半边,交替出现。
4.整条路径上没有重复的点。
5.起点和终点都是目前还没有匹配的点,而其它所有点都是已经配好对的。
6.路径上的所有第奇数条边都不在原匹配中,所有第偶数条边都出现在原匹配中。
7.最后,也是最重要的一条,把增广路径上的所有第奇数条边加入到原匹配中去,并把增广路径中的所有第偶数条边从原匹配中删除,这个操作称为增广路径的取反,则新的匹配数就比原匹配数增加了1个。

说人话版本就是,从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路(agumenting path)。例如,图4中的红色路径就是一条增广路,然后根据第七条定义,获得技术条边就是 5-6,3-4,1-2。

实现代码java版本

APP.java

匈牙利算法实现类。

package com.yixin.arithmetic.hungary;

import com.yixin.arithmetic.hungary.model.GraphMatch;

/**
 * @author yixin 
 * 匈牙利算法
 */
public class App {

    static int count = 5;

    public static void main(String[] args) {
        GraphMatch graphMatch = init();
        for(int i = 0 ; i < count ; i ++) {
            search(graphMatch, i);
            clearOnPathSign(graphMatch);
        }
        log(graphMatch);
    }


    /**
     * 初始化数据
     * @return
     */
    private static GraphMatch init(){
        GraphMatch graphMatch = new GraphMatch();
        int[][] edges = new int[count][count];
        edges[0][2] = 1;
        edges[1][0] = 1;
        edges[1][1] = 1;
        edges[2][2] = 1;
        edges[2][1] = 1;
        edges[2][3] = 1;
        edges[3][1] = 1;
        edges[3][2] = 1;
        edges[4][2] = 1;
        edges[4][4] = 1;
        edges[4][3] = 1;
        graphMatch.setEdges(edges);
        graphMatch.setOnPath(new boolean[count]);
        int[] pathAry = new int[count];
        for(int i = 0 ; i < pathAry.length ; i ++){
            pathAry[i] = -1;
        }
        graphMatch.setPath(pathAry);
        return graphMatch;
    }


    /**
     * 清空当前路径上遍历过的Y点
     * @param graphMatch
     */
    private static void clearOnPathSign(GraphMatch graphMatch){
        graphMatch.setOnPath(new boolean[count]);
    }


    /**
     * 对于某左侧节点X的查找
     * @param graphMatch 图的对象
     * @param xIndex X的索引位置
     * @return 是否成功
     */
    private static boolean search(GraphMatch graphMatch , Integer xIndex){

        for(int yIndex = 0 ; yIndex < count ; yIndex ++){
            //没有连线
            if(graphMatch.getEdges()[xIndex][yIndex] != 1 ){
                continue;
            }
            //已经检测过该节点
            if(graphMatch.getOnPath()[yIndex]){
                continue;
            }
            //设置该节点已经检测过
            graphMatch.getOnPath()[yIndex] = true;
            //当前Y节点是否是未覆盖点 或 是否在增广路径上
            //递归查找
            if(graphMatch.getPath()[yIndex] == -1 || search(graphMatch , graphMatch.getPath()[yIndex])){
                //设置增广路径
                graphMatch.getPath()[yIndex] = xIndex;
                return true;
            }
        }
        return false;
    }


    /**
     * 输出日志
     * @param graphMatch
     */
    private static void log(GraphMatch graphMatch){
        for(int i = 0 ; i < graphMatch.getPath().length ; i ++){
            System.out.println(graphMatch.getPath()[i] + "<--->" + i);
        }
    }
}

GraphMatch.java

图形的实体对象,用于保存节点、边、增广路信息。

package com.yixin.arithmetic.hungary.model;

/**
 * Created by yixin on 16/12/21.
 */
public class GraphMatch {

    private int[][] edges; //所有的连接边
    private boolean[] onPath; //当前查找路径的某节点是否已经被扫描过
    private int[] path; //增广路

    public int[][] getEdges() {
        return edges;
    }

    public void setEdges(int[][] edges) {
        this.edges = edges;
    }

    public boolean[] getOnPath() {
        return onPath;
    }

    public void setOnPath(boolean[] onPath) {
        this.onPath = onPath;
    }

    public int[] getPath() {
        return path;
    }

    public void setPath(int[] path) {
        this.path = path;
    }

}

你可能感兴趣的:(算法的乐趣)