All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.waioeka.graph.DisjointSet.scala Maven / Gradle / Ivy

There is a newer version: 0.0.6
Show newest version
/*
 * Copyright (c) 2016
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.waioeka.graph

import Graph.{Edge, Vertex}

import scala.annotation.tailrec


/**
  * Simple implementation of a Disjoint-set data structure.  Union by rank optimisation is
  * implemented, but path compression is not.
  *
  * @param parents        the map of vertex to parent.
  * @param ranks          the depth of the node in the set (given no path compression).
  * @param numComponents  the number of connected components.
  */
case class DisjointSet(parents: Map[Vertex,Vertex], ranks: Map[Vertex,Int], numComponents: Int) {
  /**
    * Find the root parent of a vertex.
    *
    * @param v  the vertex.
    * @return   the root parent of the vertex.
    */
  @tailrec
  final def find(v: Vertex): Vertex = {
    val p = parents(v)
    if (p == v) v else find(p)
  }

  /**
    * Adds a set of edges to the disjoint set.
    *
    * @param edges  the set of edges to add to the set.
    * @return a new disjoint set.
    */
  def union(edges: Iterable[Edge]) = edges.foldLeft(this)((acc, v) => acc.union0(v))

  /**
    * Adds an edge to the disjoint set.
    *
    * @param e  the edge to add.
    * @return   the new disjoint set.
    */
  private def union0(e: Edge): DisjointSet = {
    val (xp, yp) = (find(e.from), find(e.to))
    (xp, yp) match {
      case (x,y) if x == y => this
      case (x,y) if ranks(x) < ranks(y) => DisjointSet(parents.updated(xp, yp), ranks, numComponents -1)
      case (x,y) if ranks(x) > ranks(y) => DisjointSet(parents.updated(yp, xp), ranks, numComponents -1)
      case _ =>
        val currentRank = ranks(xp)
        DisjointSet(parents.updated(yp, xp), ranks.updated(xp, currentRank + 1), numComponents -1)
    }
  }
}

/**
  * Companion object for the `UnionFind` object.
  */
object DisjointSet {
  /**
    * Creates a new `DisjointSet` object given a number of vertices.
    *
    * @param numVertices  the number of vertices in the graph.
    * @return a union find object where each vertex is its own component.
    */
  def apply(numVertices: Int): DisjointSet = {
    require(numVertices>1,"Number of vertices must be greater than one.")
    DisjointSet((1 to numVertices).map(x=>(x,x)).toMap,(1 to numVertices).map((_,1)).toMap, numVertices)
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy