-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMysteryUnweightedGraphImplementation.java
More file actions
212 lines (195 loc) · 7.16 KB
/
MysteryUnweightedGraphImplementation.java
File metadata and controls
212 lines (195 loc) · 7.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
/**
* An implementation of the Unweighted Graph ADT. This class can
* represent both directed and undirected graphs, but the choice must be made
* at construction time, and is final.
* Technically, this implementation supports self-loops; it makes no effort to
* prevent these.
* Any method that takes one or more vertex IDs as arguments may throw an
* IndexOutOfBoundsException if any input ID is out of bounds.
*
* @author Jadrian Miles
* @author Anna Rafferty
*/
public class MysteryUnweightedGraphImplementation implements UnweightedGraph {
private List<List<Integer>> adj;
private final boolean undirected;
/** Default constructor: an empty directed graph. */
public MysteryUnweightedGraphImplementation() {
this(true, 0);
}
/** Constructs an empty graph with the specified directedness. */
public MysteryUnweightedGraphImplementation(boolean directed) {
this(directed, 0);
}
/** Constructs a graph with N vertices and the specified directedness. */
public MysteryUnweightedGraphImplementation(boolean directed, int N) {
adj = new ArrayList<List<Integer>>();
undirected = !directed;
for(int i = 0; i < N; i++) {
adj.add(new ArrayList<Integer>());
}
}
/** Adds a new vertex.
* @return the ID of the added vertex.
*/
public int addVertex() {
adj.add(new ArrayList<Integer>());
return adj.size() - 1;
}
// The return value, which we'll call i, indicates the presence or absence
// of an edge in the list "edges" whose endpoint is "goal".
// If i >= 0, edges.get(i) is the desired edge.
// If i < 0, j = (-i - 1) is the index in edges at which the edge
// should be inserted in order to keep the edge list sorted.
private static int findEdge(List<Integer> edges, int goal) {
// Search using iterative binary search, essentially copied from the JDK
// java.util.Arrays (specifically: http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html )
int l = 0;
int r = edges.size() - 1;
while(l <= r) {
int m = l + (r-l)/2;
int v = edges.get(m);
if(v < goal) {
l = m + 1;
} else if(v > goal) {
r = m - 1;
} else {
return m;
}
}
return -(l + 1);
}
/** Adds an edge between two vertices.
* In an undirected graph, this has the same effect as addEdge(end, begin).
* @return false if the edge was already in the graph.
*/
public boolean addEdge(int begin, int end) {
List<Integer> edges = adj.get(begin);
if(edges == null || end < 0 || end >= adj.size()) {
throw new IndexOutOfBoundsException();
}
int i = findEdge(edges, end);
if(i >= 0) {
// This edge is already in the graph.
return false;
}
edges.add(-i-1, end);
if(undirected && (begin != end)) {
// We have to add the edge in the other direction too.
// The (begin == end) case is a self-loop; we there's no "other
// direction in this case.
// Since (begin != end) and we assured above that (begin, end) was
// not already in the graph, we know that (end, begin) is also not
// already in the graph. So we know i will be negative; we don't
// have to check.
i = findEdge(adj.get(end), begin);
adj.get(end).add(-i-1, begin);
}
return true;
}
/** Checks whether an edge exists between two vertices.
* In an undirected graph, this returns the same as hasEdge(end, begin).
* @return true if there is an edge from begin to end.
*/
public boolean hasEdge(int begin, int end) {
List<Integer> edges = adj.get(begin);
if(edges == null || end < 0 || end >= adj.size()) {
throw new IndexOutOfBoundsException();
}
return (findEdge(edges, end) >= 0);
}
/** Returns the out-degree of the specified vertex. */
public int getDegree(int v) {
List<Integer> edges = adj.get(v);
if(edges == null) {
throw new IndexOutOfBoundsException();
}
return edges.size();
}
/** Returns the in-degree of the specified vertex. */
public int getInDegree(int v) {
if(v < 0 || v >= adj.size()) {
throw new IndexOutOfBoundsException();
}
// To count the in-degree, we have to look for edges to v from *all*
// vertices. (Including v! Self-loops are allowed.)
int d = 0;
for(List<Integer> edges : adj) {
for(Integer e : edges) {
if(e.equals(v)) {
d++;
}
}
}
return d;
}
// Wrapper class around List<Integer>, to provide a read-only iterator.
private class NeighborCollection implements Iterable<Integer> {
private List<Integer> neighbors;
public NeighborCollection(List<Integer> edges) {
neighbors = edges;
}
public Iterator<Integer> iterator() {
return new NeighborIterator(neighbors);
}
// Read-only iterator.
private class NeighborIterator implements Iterator<Integer> {
private Iterator<Integer> it;
public NeighborIterator(List<Integer> edges) {
it = edges.iterator();
}
public boolean hasNext() {
return it.hasNext();
}
public Integer next() {
return it.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}
/** Returns an iterator over the neighbors of the specified vertex.
* In particular, the vertex u is included in the returned iterator's
* sequence if and only if there is an edge from v to u in the graph.
*/
public Iterable<Integer> getNeighbors(int v) {
List<Integer> neighbors = adj.get(v);
if(neighbors == null) {
throw new IndexOutOfBoundsException();
}
return new NeighborCollection(neighbors);
}
/** Returns the number of vertices in the graph. */
public int numVerts() {
return adj.size();
}
/** Returns the number of edges in the graph.
* The result does *not* double-count edges in undirected graphs.
*/
public int numEdges() {
int m = 0;
for(List<Integer> edges : adj) {
m += edges.size();
}
if(undirected) {
return m/2;
}
return m;
}
/** Returns true if the graph is directed. */
public boolean isDirected() {
return !undirected;
}
/** Returns true if there are no vertices in the graph. */
public boolean isEmpty() {
return adj.isEmpty();
}
/** Removes all vertices and edges from the graph. */
public void clear() {
adj.clear();
}
}