Skip to content
This repository has been archived by the owner on Jan 1, 2023. It is now read-only.

Commit

Permalink
New graph types.
Browse files Browse the repository at this point in the history
  • Loading branch information
gchallen committed Oct 31, 2021
1 parent f8105c5 commit 71fc34e
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 50 deletions.
119 changes: 102 additions & 17 deletions src/main/java/cs1/graphs/UnweightedGraph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class Node<T>(var value: T, val nonce: Int) {
}

override fun hashCode() = Objects.hash(value, nonce)

override fun toString(): String {
return "Node(value=$value)"
}
}

class GraphNode<T> @JvmOverloads constructor(val value: T, val nonce: Int, var neighbors: Set<GraphNode<T>> = setOf()) {
Expand All @@ -21,6 +25,10 @@ class GraphNode<T> @JvmOverloads constructor(val value: T, val nonce: Int, var n
}

override fun hashCode() = Objects.hash(value, nonce)

override fun toString(): String {
return "GraphNode(value=$value)"
}
}

private fun <T> GraphNode<T>.find(): Set<GraphNode<T>> = mutableSetOf<GraphNode<T>>().also { nodes ->
Expand Down Expand Up @@ -49,7 +57,11 @@ fun <T> Map<Node<T>, Set<Node<T>>>.toGraphNodes(random: Random, checkUndirected:
forEach { (node, neighbors) ->
node.neighbors = neighbors
}
check(keys.first().find() == mapping.values.toSet()) { "Graph is not connected" }
if (checkUndirected) {
check(keys.first().find() == mapping.values.toSet()) { "Graph is not connected" }
} else {
check(keys.find { it.find() == mapping.values.toSet() } != null) { "Graph is not connected" }
}
keys.forEach { node ->
check(node !in node.neighbors) { "Graph contains a self-edge" }
if (checkUndirected) {
Expand Down Expand Up @@ -96,7 +108,7 @@ class UnweightedGraph<T> private constructor(edges: Map<GraphNode<T>, Set<GraphN
val edges = edges
get() {
if (edgesLocked) {
throw IllegalAccessException()
throw IllegalAccessException("edges may not be accessed in this graph")
}
return field
}
Expand All @@ -115,11 +127,23 @@ class UnweightedGraph<T> private constructor(edges: Map<GraphNode<T>, Set<GraphN

override fun hashCode() = Objects.hash(_edges)

val node = edges.keys.minByOrNull { it.nonce }!!
val node: GraphNode<T>
get() {
if (nodeLocked) {
throw IllegalAccessException("node may not be accessed in this graph")
}
return _edges.keys.minByOrNull { it.nonce }!!
}
private var nodeLocked = false
fun lockNode(): UnweightedGraph<T> {
nodeLocked = true
return this
}

val nodes: Set<GraphNode<T>>
get() {
if (nodesLocked) {
throw IllegalAccessException()
throw IllegalAccessException("nodes may not be accessed in this graph")
}
return _edges.keys
}
Expand All @@ -131,12 +155,14 @@ class UnweightedGraph<T> private constructor(edges: Map<GraphNode<T>, Set<GraphN

@Suppress("TooManyFunctions")
companion object {
@JvmOverloads
@JvmStatic
fun <T> singleNodeGraph(value: T, random: Random = Random()) =
fun <T> singleNodeGraph(value: T, random: Random = Random(124)) =
UnweightedGraph(mapOf(Node(value, 0) to setOf()), random, true)

@JvmOverloads
@JvmStatic
fun <T> twoNodeUndirectedGraph(first: T, second: T, random: Random = Random()): UnweightedGraph<T> {
fun <T> twoNodeUndirectedGraph(first: T, second: T, random: Random = Random(124)): UnweightedGraph<T> {
val mapping = mapOf(0 to Node(first, 0), 1 to Node(second, 1))
return UnweightedGraph(
mapOf(
Expand All @@ -148,8 +174,9 @@ class UnweightedGraph<T> private constructor(edges: Map<GraphNode<T>, Set<GraphN
)
}

@JvmOverloads
@JvmStatic
fun <T> twoNodeDirectedGraph(first: T, second: T, random: Random = Random()): UnweightedGraph<T> {
fun <T> twoNodeDirectedGraph(first: T, second: T, random: Random = Random(124)): UnweightedGraph<T> {
val mapping = mapOf(0 to Node(first, 0), 1 to Node(second, 1))
return UnweightedGraph(
mapOf(
Expand All @@ -161,8 +188,9 @@ class UnweightedGraph<T> private constructor(edges: Map<GraphNode<T>, Set<GraphN
)
}

@JvmOverloads
@JvmStatic
fun <T> linearUndirectedGraph(list: List<T>, random: Random = Random()): UnweightedGraph<T> {
fun <T> linearUndirectedGraph(list: List<T>, random: Random = Random(124)): UnweightedGraph<T> {
require(list.size >= 2) { "List has fewer than two elements" }
val mapping = list.mapIndexed { i, it -> i to Node(it, i) }.toMap()
val edges = mapping.values.associateWith { mutableSetOf<Node<T>>() }
Expand All @@ -173,8 +201,9 @@ class UnweightedGraph<T> private constructor(edges: Map<GraphNode<T>, Set<GraphN
return UnweightedGraph(edges, random, true)
}

@JvmOverloads
@JvmStatic
fun <T> linearDirectedGraph(list: List<T>, random: Random = Random()): UnweightedGraph<T> {
fun <T> linearDirectedGraph(list: List<T>, random: Random = Random(124)): UnweightedGraph<T> {
require(list.size >= 2) { "List has fewer than two elements" }
val mapping = list.mapIndexed { i, it -> i to Node(it, i) }.toMap()
val edges = mapping.values.associateWith { mutableSetOf<Node<T>>() }
Expand All @@ -184,8 +213,64 @@ class UnweightedGraph<T> private constructor(edges: Map<GraphNode<T>, Set<GraphN
return UnweightedGraph(edges, random, false)
}

@JvmOverloads
@JvmStatic
fun <T> crossUndirectedGraph(
first: List<T>,
second: List<T>,
random: Random = Random(124)
): UnweightedGraph<T> {
require(first.size >= 2 && second.size > 2) { "List has fewer than two elements" }
val mapping = (first + second).mapIndexed { i, it -> i to Node(it, i) }.toMap()
val edges = mapping.values.associateWith { mutableSetOf<Node<T>>() }
for (i in 0 until (first.size - 1)) {
edges[mapping[i]]!! += mapping[i + 1]!!
edges[mapping[i + 1]]!! += mapping[i]!!
}
val secondIndices = (first.size until (first.size + second.size)).toList()
assert(secondIndices.size == second.size)
val secondFirstHalf = secondIndices.subList(0, secondIndices.size / 2)
val secondSecondHalf = secondIndices.subList(secondIndices.size / 2, secondIndices.size)

for (i in secondFirstHalf.first() until secondFirstHalf.last()) {
edges[mapping[i]]!! += mapping[i + 1]!!
edges[mapping[i + 1]]!! += mapping[i]!!
}
for (i in secondSecondHalf.first() until secondSecondHalf.last()) {
edges[mapping[i]]!! += mapping[i + 1]!!
edges[mapping[i + 1]]!! += mapping[i]!!
}

val midPoint = first.size / 2
edges[mapping[midPoint]]!! += mapping[secondFirstHalf.last()]!!
edges[mapping[midPoint]]!! += mapping[secondSecondHalf.first()]!!
edges[mapping[secondFirstHalf.last()]]!! += mapping[midPoint]!!
edges[mapping[secondSecondHalf.first()]]!! += mapping[midPoint]!!
return UnweightedGraph(edges, random, true)
}

@JvmOverloads
@JvmStatic
fun <T> randomTreeUndirectedGraph(list: List<T>, random: Random = Random()): UnweightedGraph<T> {
require(list.size >= 2) { "List has fewer than two elements" }
val mapping = list.mapIndexed { i, it -> i to Node(it, i) }.toMap()
val edges = mapping.values.associateWith { mutableSetOf<Node<T>>() }
for (i in list.indices) {
val previous = ((i + 1) until list.size).toList()
if (previous.isEmpty()) {
continue
}
for (j in previous.shuffled(random).take(1)) {
edges[mapping[i]]!! += mapping[j]!!
edges[mapping[j]]!! += mapping[i]!!
}
}
return UnweightedGraph(edges, random, true)
}

@JvmOverloads
@JvmStatic
fun <T> circleUndirectedGraph(list: List<T>, random: Random = Random()): UnweightedGraph<T> {
fun <T> circleUndirectedGraph(list: List<T>, random: Random = Random(124)): UnweightedGraph<T> {
require(list.size >= 2) { "List has fewer than two elements" }
val mapping = list.mapIndexed { i, it -> i to Node(it, i) }.toMap()
val edges = mapping.values.associateWith { mutableSetOf<Node<T>>() }
Expand All @@ -198,8 +283,9 @@ class UnweightedGraph<T> private constructor(edges: Map<GraphNode<T>, Set<GraphN
return UnweightedGraph(edges, random, true)
}

@JvmOverloads
@JvmStatic
fun <T> circleDirectedGraph(list: List<T>, random: Random = Random()): UnweightedGraph<T> {
fun <T> circleDirectedGraph(list: List<T>, random: Random = Random(124)): UnweightedGraph<T> {
require(list.size >= 2) { "List has fewer than two elements" }
val mapping = list.mapIndexed { i, it -> i to Node(it, i) }.toMap()
val edges = mapping.values.associateWith { mutableSetOf<Node<T>>() }
Expand All @@ -210,8 +296,9 @@ class UnweightedGraph<T> private constructor(edges: Map<GraphNode<T>, Set<GraphN
return UnweightedGraph(edges, random, false)
}

@JvmOverloads
@JvmStatic
fun <T> fullyConnectedGraph(list: List<T>, random: Random = Random()): UnweightedGraph<T> {
fun <T> fullyConnectedGraph(list: List<T>, random: Random = Random(124)): UnweightedGraph<T> {
require(list.size >= 2) { "List has fewer than two elements" }
val mapping = list.mapIndexed { i, it -> i to Node(it, i) }.toMap()
val edges = mapping.values.associateWith { mutableSetOf<Node<T>>() }
Expand All @@ -224,24 +311,22 @@ class UnweightedGraph<T> private constructor(edges: Map<GraphNode<T>, Set<GraphN
return UnweightedGraph(edges, random, true)
}

@JvmOverloads
@JvmStatic
fun <T> randomUndirectedGraph(list: List<T>, random: Random = Random()): UnweightedGraph<T> {
require(list.size >= 2) { "List has fewer than two elements" }
val mapping = list.mapIndexed { i, it -> i to Node(it, i) }.toMap()
val edges = mapping.values.associateWith { mutableSetOf<Node<T>>() }
for (i in list.indices) {
val previous = ((i + 1) until list.size).toList()
if (previous.isEmpty()) {
continue
}
for (j in previous.shuffled(random).take(random.nextInt(previous.size) + 1)) {
for (j in (list.indices - i).shuffled(random).take(random.nextInt(list.size) + 1)) {
edges[mapping[i]]!! += mapping[j]!!
edges[mapping[j]]!! += mapping[i]!!
}
}
return UnweightedGraph(edges, random, true)
}

@JvmOverloads
@JvmStatic
fun <T> randomDirectedGraph(list: List<T>, random: Random = Random()): UnweightedGraph<T> {
require(list.size >= 2) { "List has fewer than two elements" }
Expand Down
83 changes: 51 additions & 32 deletions src/test/java/com/example/Example.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,59 @@
import cs1.graphs.GraphNode;
import cs1.graphs.UnweightedGraph;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Example {
public static <T> int size(UnweightedGraph<T> graph) {
return graph.getEdges().size();
}

public static <T> int sizeWithNodes(UnweightedGraph<T> graph) {
return graph.getNodes().size();
}

public static <T> int size(GraphNode<T> node) {
Set<GraphNode<T>> nodes = new HashSet<>();
traverse(node, nodes);
return nodes.size();
}

public static int sum(UnweightedGraph<Integer> graph) {
return graph.getEdges().keySet().stream().mapToInt(GraphNode::getValue).sum();
}

public static int sum(GraphNode<Integer> node) {
Set<GraphNode<Integer>> nodes = new HashSet<>();
traverse(node, nodes);
return nodes.stream().mapToInt(GraphNode::getValue).sum();
}

private static <T> void traverse(GraphNode<T> node, Set<GraphNode<T>> visited) {
visited.add(node);
for (GraphNode<T> neighbor : node.getNeighbors()) {
if (!(visited.contains(neighbor))) {
traverse(neighbor, visited);
}
}
}
public static <T> int size(UnweightedGraph<T> graph) {
return graph.getEdges().size();
}

public static <T> int sizeWithNodes(UnweightedGraph<T> graph) {
return graph.getNodes().size();
}

public static <T> int size(GraphNode<T> node) {
Set<GraphNode<T>> nodes = new HashSet<>();
traverse(node, nodes);
return nodes.size();
}

public static int sum(UnweightedGraph<Integer> graph) {
return graph.getEdges().keySet().stream().mapToInt(GraphNode::getValue).sum();
}

public static int sum(GraphNode<Integer> node) {
Set<GraphNode<Integer>> nodes = new HashSet<>();
traverse(node, nodes);
return nodes.stream().mapToInt(GraphNode::getValue).sum();
}

private static <T> void traverse(GraphNode<T> node, Set<GraphNode<T>> visited) {
visited.add(node);
for (GraphNode<T> neighbor : node.getNeighbors()) {
if (!(visited.contains(neighbor))) {
traverse(neighbor, visited);
}
}
}

public static <T> boolean hasCycle(UnweightedGraph<T> graph) {
return hasCycle(graph.getNode(), new ArrayList<>());
}

private static <T> boolean hasCycle(GraphNode<T> node, List<GraphNode<T>> visited) {
if (visited.contains(node)) {
return visited.indexOf(node) != visited.size() - 2;
}
visited.add(node);
for (GraphNode<T> neighbor : node.getNeighbors()) {
if (hasCycle(neighbor, new ArrayList<>(visited))) {
return true;
}
}
return false;
}
}
Loading

0 comments on commit 71fc34e

Please sign in to comment.