* Complexity: O(1) space, and O(n) time. + *
+ * Arrays with zero or one element are left unchanged. * - * @param theArray the array to shuffle + * @param theArray the array to shuffle; modified in place */ public static void shuffle(final int[] theArray) { // if it's 1 or 0 items, just return diff --git a/src/main/java/io/github/diegourban/algorithms/datastructure/binarytree/BinaryTreeNode.java b/src/main/java/io/github/diegourban/algorithms/datastructure/binarytree/BinaryTreeNode.java index cbda92c..6e3c70f 100644 --- a/src/main/java/io/github/diegourban/algorithms/datastructure/binarytree/BinaryTreeNode.java +++ b/src/main/java/io/github/diegourban/algorithms/datastructure/binarytree/BinaryTreeNode.java @@ -1,20 +1,44 @@ package io.github.diegourban.algorithms.datastructure.binarytree; +/** + * A node of a binary tree holding an {@code int} value. The tree is built by linking nodes through the public + * {@link #left} and {@link #right} fields. + */ public class BinaryTreeNode { + /** The value stored in this node. */ public int value; + /** The left child, or {@code null} if there is none. */ public BinaryTreeNode left; + /** The right child, or {@code null} if there is none. */ public BinaryTreeNode right; + /** + * Creates a leaf node with the given value. + * + * @param value the value to store + */ public BinaryTreeNode(final int value) { this.value = value; } + /** + * Sets a new leaf node as the left child, replacing any existing left subtree. + * + * @param leftValue the value of the new left child + * @return the new left child + */ public BinaryTreeNode insertLeft(final int leftValue) { this.left = new BinaryTreeNode(leftValue); return this.left; } + /** + * Sets a new leaf node as the right child, replacing any existing right subtree. + * + * @param rightValue the value of the new right child + * @return the new right child + */ public BinaryTreeNode insertRight(final int rightValue) { this.right = new BinaryTreeNode(rightValue); return this.right; diff --git a/src/main/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedList.java b/src/main/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedList.java index 3e62bb9..9600a71 100644 --- a/src/main/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedList.java +++ b/src/main/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedList.java @@ -2,12 +2,24 @@ import io.github.diegourban.algorithms.datastructure.Element; +/** + * A list of {@link Element}s implemented as a doubly linked chain of {@link LinkedListNode}s, keeping references to + * the first and last nodes and a running size. Positions are zero-based. + */ public class LinkedList { private LinkedListNode first = null; private LinkedListNode last = null; private int totalElements = 0; + /** Creates an empty list. */ + public LinkedList() {} + + /** + * Inserts an element at the start of the list. + * + * @param element the element to insert + */ public void addAtStart(final Element element) { if (this.totalElements == 0) { final LinkedListNode newNode = new LinkedListNode(element); @@ -22,6 +34,11 @@ public void addAtStart(final Element element) { this.totalElements++; } + /** + * Appends an element at the end of the list. + * + * @param element the element to append + */ public void addAtEnd(final Element element) { if (this.totalElements == 0) { addAtStart(element); @@ -35,6 +52,18 @@ public void addAtEnd(final Element element) { } } + /** + * Inserts an element at the given position, shifting later elements one position back. + *
+ * If the list is empty the element is added regardless of {@code position}. If {@code position} equals + * {@link #size()} the element is appended. Otherwise the element is linked after the node at + * {@code position - 1}, so inserting at position {@code 0} of a non-empty list is not supported. + * + * @param position the position for the new element, from {@code 1} to {@link #size()} on a non-empty list + * @param element the element to insert + * @throws IllegalArgumentException if the list is not empty and {@code position - 1} is not an occupied position + * (including {@code position == 0}) + */ public void add(final int position, final Element element) { if (this.totalElements == 0) { addAtStart(element); @@ -68,14 +97,31 @@ private LinkedListNode getNode(final int position) { return current; } + /** + * Returns the first node, giving direct access to the chain. + * + * @return the first node, or {@code null} if the list is empty + */ public LinkedListNode getFirst() { return this.first; } + /** + * Returns the element at the given position by walking from the first node. + * + * @param position the zero-based position + * @return the element at {@code position} + * @throws IllegalArgumentException if {@code position} is negative or not less than {@link #size()} + */ public Element get(final int position) { return this.getNode(position).getElement(); } + /** + * Removes the first element. + * + * @throws IllegalStateException if the list is empty + */ public void removeFromStart() { if (this.totalElements == 0) { throw new IllegalStateException("Lista vazia"); @@ -89,6 +135,10 @@ public void removeFromStart() { } } + /** + * Removes the last element. Unlike {@link #removeFromStart()}, calling this on an empty list fails with a + * {@code NullPointerException} rather than an {@code IllegalStateException}. + */ public void removeFromEnd() { if (this.totalElements == 1) { this.removeFromStart(); @@ -100,6 +150,20 @@ public void removeFromEnd() { } } + /** + * Removes the element at the given position. + *
+ * Position {@code 0} delegates to {@link #removeFromStart()} and position {@code size() - 1} to + * {@link #removeFromEnd()}; any other position unlinks the node after {@code position - 1}. + *
+ * Positions are not fully validated: on a non-empty list, {@code position == size()} fails with a
+ * {@code NullPointerException}, as does {@code -1} on an empty list.
+ *
+ * @param position the zero-based position, from {@code 0} to {@code size() - 1}
+ * @throws IllegalStateException if {@code position} is {@code 0} and the list is empty
+ * @throws IllegalArgumentException if {@code position} is negative or greater than {@link #size()}, except
+ * {@code -1} on an empty list
+ */
public void remove(final int position) {
if (position == 0) {
this.removeFromStart();
@@ -116,10 +180,21 @@ public void remove(final int position) {
}
}
+ /**
+ * Returns the number of elements.
+ *
+ * @return the number of elements in the list
+ */
public int size() {
return this.totalElements;
}
+ /**
+ * Tells whether the list holds an element equal to the given one, comparing with {@link Element#equals(Object)}.
+ *
+ * @param element the element to look for
+ * @return {@code true} if an equal element is found
+ */
public boolean contains(final Element element) {
LinkedListNode current = this.first;
while (current != null) {
@@ -131,6 +206,12 @@ public boolean contains(final Element element) {
return false;
}
+ /**
+ * Returns the element names in order, each followed by a comma, inside brackets, for example {@code "[a,b,]"};
+ * an empty list gives {@code "[]"}.
+ *
+ * @return the string form of the list
+ */
@Override
public String toString() {
if (this.totalElements == 0) {
diff --git a/src/main/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedListNode.java b/src/main/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedListNode.java
index 32f9f0e..11eef7f 100644
--- a/src/main/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedListNode.java
+++ b/src/main/java/io/github/diegourban/algorithms/datastructure/linkedlist/LinkedListNode.java
@@ -2,6 +2,9 @@
import io.github.diegourban.algorithms.datastructure.Element;
+/**
+ * A node of a doubly linked {@link LinkedList}, holding an {@link Element} and links to the previous and next nodes.
+ */
public class LinkedListNode {
private Element element;
@@ -9,35 +12,77 @@ public class LinkedListNode {
private LinkedListNode previous;
private LinkedListNode next;
+ /**
+ * Creates a node with no previous or next node.
+ *
+ * @param element the element to hold
+ */
public LinkedListNode(final Element element) {
this(element, null);
}
+ /**
+ * Creates a node linked forward to {@code next}. The previous link is left {@code null}, and {@code next} is not
+ * updated to point back.
+ *
+ * @param element the element to hold
+ * @param next the next node, or {@code null}
+ */
public LinkedListNode(final Element element, final LinkedListNode next) {
this.element = element;
this.next = next;
}
+ /**
+ * Returns the element held by this node.
+ *
+ * @return the element
+ */
public Element getElement() {
return element;
}
+ /**
+ * Replaces the element held by this node.
+ *
+ * @param element the new element
+ */
public void setElement(final Element element) {
this.element = element;
}
+ /**
+ * Returns the next node.
+ *
+ * @return the next node, or {@code null} if this is the last node
+ */
public LinkedListNode getNext() {
return next;
}
+ /**
+ * Sets the next node.
+ *
+ * @param next the next node, or {@code null}
+ */
public void setNext(final LinkedListNode next) {
this.next = next;
}
+ /**
+ * Returns the previous node.
+ *
+ * @return the previous node, or {@code null} if none is linked
+ */
public LinkedListNode getPrevious() {
return previous;
}
+ /**
+ * Sets the previous node.
+ *
+ * @param previous the previous node, or {@code null}
+ */
public void setPrevious(final LinkedListNode previous) {
this.previous = previous;
}
diff --git a/src/main/java/io/github/diegourban/algorithms/datastructure/queue/Queue.java b/src/main/java/io/github/diegourban/algorithms/datastructure/queue/Queue.java
index dba0c9b..329323f 100644
--- a/src/main/java/io/github/diegourban/algorithms/datastructure/queue/Queue.java
+++ b/src/main/java/io/github/diegourban/algorithms/datastructure/queue/Queue.java
@@ -3,18 +3,41 @@
import io.github.diegourban.algorithms.datastructure.Element;
import java.util.LinkedList;
+/**
+ * A first-in, first-out queue of {@link Element}s backed by a {@link java.util.LinkedList}.
+ */
public class Queue {
private final LinkedList
+ * The position is not validated: a position not less than {@link #size()} removes the last element instead, and
+ * a negative position, or any call on an empty vector, fails with an {@code ArrayIndexOutOfBoundsException}.
+ *
+ * @param position the zero-based position, from {@code 0} to {@code size() - 1}
+ */
public void remove(final int position) {
for (int i = position; i < this.totalElements - 1; i++) {
this.elements[i] = this.elements[i + 1];
@@ -51,6 +86,13 @@ public void remove(final int position) {
this.elements[this.totalElements] = null;
}
+ /**
+ * Tells whether the vector holds an element equal to the given one, calling {@code element.equals} on each stored
+ * element.
+ *
+ * @param element the element to look for; must not be {@code null}
+ * @return {@code true} if an equal element is found
+ */
public boolean contains(final Element element) {
for (int i = 0; i < this.totalElements; i++) {
if (element.equals(this.elements[i])) {
@@ -61,6 +103,11 @@ public boolean contains(final Element element) {
return false;
}
+ /**
+ * Returns the number of elements.
+ *
+ * @return the number of elements in the vector
+ */
public int size() {
return this.totalElements;
}
@@ -75,6 +122,12 @@ private void guaranteesSpace() {
}
}
+ /**
+ * Returns the whole backing array, including the unused {@code null} slots, in the form
+ * {@code "Vector{elements=[a, b, null, ...]}"}.
+ *
+ * @return the string form of the vector
+ */
@Override
public String toString() {
return "Vector{" + "elements=" + Arrays.toString(elements) + '}';
diff --git a/src/main/java/io/github/diegourban/algorithms/io/Reader.java b/src/main/java/io/github/diegourban/algorithms/io/Reader.java
index c806c7d..a78e850 100644
--- a/src/main/java/io/github/diegourban/algorithms/io/Reader.java
+++ b/src/main/java/io/github/diegourban/algorithms/io/Reader.java
@@ -6,6 +6,15 @@
import java.io.InputStream;
/**
+ * Fast, buffered reader of ASCII tokens and lines from an {@link InputStream}, for competitive programming.
+ *
+ * Input is read in 64 KiB chunks and decoded byte by byte, so only single-byte (ASCII) input is handled correctly.
+ * The token methods do no validation: any non-digit byte ends a number, and nothing checks for overflow.
+ *
+ * End of input: the first read past the end yields an end marker; any read after that throws {@link EOFException}.
+ * In practice the token methods throw {@code EOFException} when no further token remains, while {@link #readLine()}
+ * first returns whatever is left (possibly an empty string) and only throws on the following call.
+ *
* Solution extracted from
* http://www.geeksforgeeks.org/fast-io-in-java-in-competitive-programming/
*/
@@ -16,12 +25,29 @@ public class Reader {
private final byte[] buffer;
private int bufferPointer, bytesRead;
+ /**
+ * Creates a reader over the given stream. Nothing is read until the first read method is called.
+ *
+ * @param input the stream to read from; it is closed by {@link #close()}
+ */
public Reader(final InputStream input) {
din = new DataInputStream(input);
buffer = new byte[BUFFER_SIZE];
bufferPointer = bytesRead = 0;
}
+ /**
+ * Reads the next non-empty line.
+ *
+ * Leading {@code '\n'} and {@code '\r'} bytes are skipped, so blank lines are never returned; the line ends at
+ * the next {@code '\n'} or {@code '\r'}, which is consumed but not included. Lines longer than 10,000 bytes are
+ * not supported.
+ *
+ * @return the line without its terminator; the remaining text, or an empty string, if the input ends first
+ * @throws EOFException if a previous read already reached the end of the input
+ * @throws ArrayIndexOutOfBoundsException if the line is longer than 10,000 bytes
+ * @throws IOException if the underlying stream fails
+ */
public String readLine() throws IOException {
final byte[] buf = new byte[10000]; // line length
int cnt = 0;
@@ -39,6 +65,16 @@ public String readLine() throws IOException {
return new String(buf, 0, cnt);
}
+ /**
+ * Reads the next whitespace-separated integer, with an optional leading {@code '-'}.
+ *
+ * Bytes up to and including {@code ' '} are skipped first. Digits are read until the first non-digit byte,
+ * which is consumed. Values outside the {@code int} range overflow silently.
+ *
+ * @return the integer read
+ * @throws EOFException if the input ends before a token is found
+ * @throws IOException if the underlying stream fails
+ */
public int nextInt() throws IOException {
int ret = 0;
byte c = read();
@@ -63,6 +99,13 @@ public int nextInt() throws IOException {
return ret;
}
+ /**
+ * Reads the next character that is not whitespace (any byte above {@code ' '}).
+ *
+ * @return the character read
+ * @throws EOFException if the input ends before such a character is found
+ * @throws IOException if the underlying stream fails
+ */
public char nextChar() throws IOException {
byte c = read();
@@ -73,6 +116,15 @@ public char nextChar() throws IOException {
return (char) c;
}
+ /**
+ * Reads the next whitespace-separated long, with an optional leading {@code '-'}.
+ *
+ * Parses like {@link #nextInt()}; values outside the {@code long} range overflow silently.
+ *
+ * @return the long read
+ * @throws EOFException if the input ends before a token is found
+ * @throws IOException if the underlying stream fails
+ */
public long nextLong() throws IOException {
long ret = 0;
byte c = read();
@@ -97,6 +149,16 @@ public long nextLong() throws IOException {
return ret;
}
+ /**
+ * Reads the next whitespace-separated decimal number, such as {@code -12.5}.
+ *
+ * Accepts an optional leading {@code '-'}, digits and an optional {@code '.'} followed by digits. Exponent
+ * notation is not supported. The byte that ends the number is consumed.
+ *
+ * @return the number read
+ * @throws EOFException if the input ends before a token is found
+ * @throws IOException if the underlying stream fails
+ */
public double nextDouble() throws IOException {
double ret = 0;
double div = 1;
@@ -128,6 +190,13 @@ public double nextDouble() throws IOException {
return ret;
}
+ /**
+ * Discards the next {@code n} bytes.
+ *
+ * @param n the number of bytes to discard; nothing happens if it is zero or negative
+ * @throws EOFException if a read is attempted after the end of the input was already reached
+ * @throws IOException if the underlying stream fails
+ */
public void skip(final int n) throws IOException {
for (int i = 0; i < n; i++) {
read();
@@ -156,6 +225,11 @@ private boolean isEOF() {
return bytesRead == -1;
}
+ /**
+ * Closes the underlying stream. Calling it again has no effect.
+ *
+ * @throws IOException if closing the underlying stream fails
+ */
public void close() throws IOException {
if (din != null) {
din.close();
@@ -163,6 +237,11 @@ public void close() throws IOException {
}
}
+ /**
+ * Tells whether {@link #close()} has been called.
+ *
+ * @return {@code true} if this reader is closed, {@code false} otherwise
+ */
public boolean isClosed() {
return din == null;
}
diff --git a/src/main/java/io/github/diegourban/algorithms/math/Combinatorics.java b/src/main/java/io/github/diegourban/algorithms/math/Combinatorics.java
index 19abf97..77efc07 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/Combinatorics.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/Combinatorics.java
@@ -2,25 +2,32 @@
import java.math.BigInteger;
+/**
+ * Combinatorics: binomial coefficients.
+ */
public final class Combinatorics {
private Combinatorics() {}
/**
- * Pascal's Triangle
- *
+ * Binomial coefficient "n choose m", read from Pascal's Triangle
+ *
+ * Builds row {@code n} of the triangle with exact {@link BigInteger} arithmetic: O(n²) time and O(n) space.
*
* Solution extracted and adapted from
* http://www.brpreiss.com/books/opus5/html/page460.html.
*
* @param n the row number (0-indexed)
* @param m the column number (0-indexed)
- * @return
+ * @return the number of ways to choose {@code m} items out of {@code n}; zero if {@code m} is negative or greater
+ * than {@code n}
*/
public static BigInteger binomialCoefficient(final int n, final int m) {
if (m < 0 || m > n) {
diff --git a/src/main/java/io/github/diegourban/algorithms/math/Geometry.java b/src/main/java/io/github/diegourban/algorithms/math/Geometry.java
index 7d6ac5d..fd18210 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/Geometry.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/Geometry.java
@@ -2,6 +2,11 @@
import java.util.Arrays;
+/**
+ * Plane and solid geometry: distances, circles, polygons, cylinders and triangles.
+ *
+ * The circle and cylinder methods approximate pi as {@code 3.14}, not {@link Math#PI}.
+ */
public final class Geometry {
private Geometry() {}
@@ -23,7 +28,7 @@ public static double distance(final int x1, final int y1, final int x2, final in
}
/**
- * Calculates the area of a circle from its radius
+ * Calculates the area of a circle from its radius, using pi = 3.14
*
* @param radius the radius of the circle
* @return the area of the circle
@@ -33,7 +38,7 @@ public static double areaOfCircleFromRadius(final double radius) {
}
/**
- * Calculates the area of a circle from its diameter
+ * Calculates the area of a circle from its diameter, using pi = 3.14
*
* @param diameter the diameter of the circle
* @return the area of the circle
@@ -54,7 +59,7 @@ public static double radiusOfCircleFromDiameter(final double diameter) {
}
/**
- * Calculates the radius of a circle from its area
+ * Calculates the radius of a circle from its area, using pi = 3.14
*
* @param area the area of the circle
* @return the radius of the circle
@@ -64,7 +69,7 @@ public static double radiusOfCircleFromArea(final double area) {
}
/**
- * Calculates the number of diagonals of an n-sided polygon
+ * Calculates the number of diagonals of an n-sided polygon, {@code sides * (sides - 3) / 2}
*
* @param sides the number of sides of the polygon
* @return the amount of diagonals
@@ -74,7 +79,7 @@ public static long diagonalsOfPolygonWithSides(final long sides) {
}
/**
- * Calculates the height of a cylinder based on its volume and base radius
+ * Calculates the height of a cylinder based on its volume and base radius, using pi = 3.14
*
* @param volume the cylinder volume
* @param baseRadius the cylinder base radius
@@ -85,17 +90,30 @@ public static double heightOfCylinder(final double volume, final double baseRadi
}
/**
- * Checks if the sides a, b and c can form a valid triangle
+ * Checks if the sides a, b and c can form a valid triangle: each side must be shorter than the sum of the
+ * other two (strict triangle inequality, so degenerate triangles are rejected)
*
* @param a side a
* @param b side b
* @param c side c
- * @return true if the sides form a valid triangle, false otherwise
+ * @return {@code true} if the sides form a valid triangle, {@code false} otherwise
*/
public static boolean isValidTriangle(final double a, final double b, final double c) {
return (a + b > c) && (b + c > a) && (a + c > b);
}
+ /**
+ * Classifies a triangle by how many of its sides are equal.
+ *
+ * The sides are compared exactly ({@code ==}) and are not checked to form a valid triangle; use
+ * {@link #isValidTriangle(double, double, double)} first.
+ *
+ * @param a side a
+ * @param b side b
+ * @param c side c
+ * @return {@link TriangleType#EQUILATERAL} if all three sides are equal, {@link TriangleType#ISOSCELES} if exactly
+ * two are, {@link TriangleType#ESCALENE} otherwise
+ */
public static TriangleType checkTriangleType(final double a, final double b, final double c) {
final double[] array = new double[3];
array[0] = a;
@@ -118,6 +136,18 @@ public static TriangleType checkTriangleType(final double a, final double b, fin
return TriangleType.ESCALENE;
}
+ /**
+ * Checks if the sides form a right ("rectangle") triangle, that is if the square of the longest side equals the
+ * sum of the squares of the other two (Pythagorean theorem).
+ *
+ * The comparison is exact ({@code ==}), so sides whose squares are not exactly representable as {@code double},
+ * such as {@code 1, 1, Math.sqrt(2)}, may be rejected. The sides are not checked to form a valid triangle.
+ *
+ * @param a side a
+ * @param b side b
+ * @param c side c
+ * @return {@code true} if the sides satisfy the Pythagorean theorem, {@code false} otherwise
+ */
public static boolean isRectangleTriangle(final double a, final double b, final double c) {
final double[] array = new double[3];
array[0] = a;
diff --git a/src/main/java/io/github/diegourban/algorithms/math/Measurement.java b/src/main/java/io/github/diegourban/algorithms/math/Measurement.java
index 5a7be66..37f9f2a 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/Measurement.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/Measurement.java
@@ -1,11 +1,16 @@
package io.github.diegourban.algorithms.math;
+/**
+ * Unit conversions between miles and kilometers.
+ *
+ * The two factors are not exact inverses of each other, so a round trip does not return exactly the original value.
+ */
public final class Measurement {
private Measurement() {}
/**
- * Converts miles to kilometers
+ * Converts miles to kilometers, using 1 mile = 1.609344 km
*
* @param miles the amount of miles to convert
* @return the amount of miles converted to kilometers
@@ -15,7 +20,7 @@ public static double milesToKilometers(final double miles) {
}
/**
- * Converts kilometers to miles
+ * Converts kilometers to miles, using 1 km = 0.621371 miles
*
* @param kilometers the amount of kilometers to convert
* @return the amount of kilometers converted to miles
diff --git a/src/main/java/io/github/diegourban/algorithms/math/NumberTheory.java b/src/main/java/io/github/diegourban/algorithms/math/NumberTheory.java
index fd1a5c1..8297bf7 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/NumberTheory.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/NumberTheory.java
@@ -11,10 +11,14 @@ private NumberTheory() {}
/**
* Least common multiple (lcm) of two numbers.
+ *
+ * Computed as {@code a * (b / gcdLoop(a, b))}; intended for positive numbers. The result overflows silently if it
+ * does not fit in a {@code long}.
*
- * @param a
- * @param b
+ * @param a the first number
+ * @param b the second number
* @return The least common multiple of two numbers.
+ * @throws ArithmeticException if both {@code a} and {@code b} are zero
*/
public static long lcm(final long a, final long b) {
return a * (b / gcdLoop(a, b));
@@ -23,8 +27,10 @@ public static long lcm(final long a, final long b) {
/**
* Least common multiple (lcm) of an array of numbers.
*
- * @param input array of numbers
- * @return The least common multiple of an array of numbers.
+ * @param input array of numbers; must not be empty
+ * @return The least common multiple of an array of numbers; the element itself for a one-element array.
+ * @throws ArrayIndexOutOfBoundsException if {@code input} is empty
+ * @see #lcm(long, long)
*/
public static long lcm(final long[] input) {
long result = input[0];
@@ -37,9 +43,12 @@ public static long lcm(final long[] input) {
*
* Also known as greatest common factor (gcf), highest common factor (hcf),
* greatest common measure (gcm) or highest common divisor
+ *
+ * Correct for non-negative input. The loop only runs while {@code b} is positive, so a negative {@code b}
+ * returns {@code a} unchanged; {@code gcdLoop(a, 0)} returns {@code a}.
*
- * @param a
- * @param b
+ * @param a the first number
+ * @param b the second number
* @return The greatest common divisor of two numbers.
*/
public static long gcdLoop(final long a, final long b) {
@@ -58,10 +67,13 @@ public static long gcdLoop(final long a, final long b) {
*
* Also known as greatest common factor (gcf), highest common factor (hcf),
* greatest common measure (gcm) or highest common divisor
+ *
+ * Correct for non-negative input. Negative input is accepted, but the result may be negative (Java's {@code %}
+ * keeps the sign of the dividend), so it can differ in sign from {@link #gcdLoop(long, long)}.
*
- * @param a
- * @param b
- * @return The greatest common divisor of two numbers.
+ * @param a the first number
+ * @param b the second number
+ * @return The greatest common divisor of two numbers; {@code a} if {@code b} is zero.
*/
public static long gcdRecursive(final long a, final long b) {
return b == 0 ? a : gcdRecursive(b, a % b);
@@ -71,9 +83,11 @@ public static long gcdRecursive(final long a, final long b) {
* Checks if a number is prime.
*
* Based on https://en.wikipedia.org/wiki/Primality_test
+ *
+ * Trial division by 2, 3 and numbers of the form 6k ± 1 up to the square root of {@code n}: O(√n) time.
*
- * @param n
- * @return true if the number n is prime, false otherwise
+ * @param n the number to test
+ * @return {@code true} if the number n is prime, {@code false} otherwise (including for {@code n <= 1})
*/
public static boolean isPrime(final int n) {
if (n <= 1) {
@@ -97,9 +111,12 @@ public static boolean isPrime(final int n) {
/**
* Lists the prime factors of a number.
+ *
+ * Uses trial division up to the square root of {@code number}: O(√n) time.
*
- * @param number
- * @return The list of prime factors of a number.
+ * @param number the number to factorise
+ * @return The list of prime factors of a number, in ascending order and repeated by multiplicity (12 gives
+ * {@code [2, 2, 3]}); empty for numbers below 2.
*/
public static List
+ * The sign of {@code number} is ignored. The number {@code 0} is treated as having no digits, so
+ * {@code containsDigit(0, 0)} returns {@code false}.
*
* @param number the number to check
* @param digit the digit expected to find in the number
- * @return true if the number contains the digit, false otherwise
+ * @return {@code true} if the number contains the digit, {@code false} otherwise
*/
public static boolean containsDigit(final long number, final long digit) {
long thisNumber = number >= 0 ? number : -number;
@@ -30,10 +36,14 @@ public static boolean containsDigit(final long number, final long digit) {
/**
* Checks if a number contains repeating digits
+ *
+ * Only consecutive repetitions count: {@code 1221} repeats {@code 2} twice, {@code 1212} has no repetition.
+ * Zero and negative numbers always return {@code false}, as does a {@code times} below 2.
*
* @param number the number
* @param times amount of times a digit must repeat
- * @return true if the number contains a digit repeating at least the expected amount of times, false otherwise
+ * @return {@code true} if the number contains a digit repeating consecutively at least the expected amount of
+ * times, {@code false} otherwise
*/
public static boolean containsRepeatingDigits(final long number, final int times) {
long theNumber = number;
@@ -57,10 +67,15 @@ public static boolean containsRepeatingDigits(final long number, final int times
}
/**
- * Converts a number to an array
+ * Converts a number to an array of its digits, least significant digit first: {@code 123} becomes
+ * {@code [3, 2, 1]}.
+ *
+ * Intended for non-negative numbers that fit in an {@code int}: each step truncates the number to {@code int}
+ * before taking the last digit, so larger values give wrong digits, and a negative number gives negative digits
+ * plus an extra trailing {@code 0} for the minus sign.
*
* @param number the number to convert
- * @return an array with each digit of the number
+ * @return an array with each digit of the number, in reverse order
*/
public static int[] toArray(final long number) {
long theNumber = number;
@@ -78,9 +93,11 @@ public static int[] toArray(final long number) {
/**
* Find the length of the int using divide and conquer
+ *
+ * Intended for non-negative numbers: any negative number returns 1.
*
* @param number some number
- * @return the length of the number
+ * @return the number of decimal digits of the number, from 1 to 10
*/
public static int length(final int number) {
if (number < 100000) {
diff --git a/src/main/java/io/github/diegourban/algorithms/math/Sequences.java b/src/main/java/io/github/diegourban/algorithms/math/Sequences.java
index 087dcf8..54f4064 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/Sequences.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/Sequences.java
@@ -13,14 +13,15 @@ private Sequences() {}
/**
* Calculates the nth Fibonacci number.
- * Works with n <= 46
+ * Works with n <= 46; larger values overflow {@code int} silently and return a wrong result.
*
* Solution: Using a bottom-up approach, starting with the 0th fibonacci number and iteratively computing subsequent numbers until we get to n.
*
- * Complexity: N time and O(1) space.
+ * Complexity: O(n) time and O(1) space.
*
- * @param n the nth number in the Fibonacci sequence to calculate
+ * @param n the nth number in the Fibonacci sequence to calculate, with {@code fibonacci(0) == 0}
* @return the nth Fibonacci number
+ * @throws IllegalArgumentException if {@code n} is negative
*/
public static int fibonacci(final int n) {
// edge cases:
@@ -52,10 +53,14 @@ public static int fibonacci(final int n) {
/**
* Calculates the nth Fibonacci number.
- * Works with n <= 67
+ * Works with n <= 67
+ *
+ * Uses Binet's formula in {@code double} arithmetic, so the result is approximate (for example
+ * {@code 55.000000000000014} for n = 10). Rounded with {@link Math#round(double)}, it equals the exact value for
+ * 0 <= n <= 67. Negative {@code n} is not rejected and gives a meaningless result.
*
* @param n the nth number in the Fibonacci sequence to calculate
- * @return the nth Fibonacci number
+ * @return an approximation of the nth Fibonacci number
*/
public static double fibonacciSimpleBinetFormula(final int n) {
if (n == 0) {
@@ -68,7 +73,11 @@ public static double fibonacciSimpleBinetFormula(final int n) {
/**
* Calculates the nth Fibonacci number.
- * Works with n <= 71
+ * Works with n <= 71
+ *
+ * Uses Binet's formula, with the powers computed in {@code double} and the subtraction and division done in
+ * {@link BigDecimal}. The result is exact for 0 <= n <= 71 and drifts from the true value after that.
+ * Negative {@code n} is not rejected and gives a meaningless result.
*
* @param n the nth number in the Fibonacci sequence to calculate
* @return the nth Fibonacci number
diff --git a/src/main/java/io/github/diegourban/algorithms/math/Statistics.java b/src/main/java/io/github/diegourban/algorithms/math/Statistics.java
index 5098b6c..d852c62 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/Statistics.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/Statistics.java
@@ -2,6 +2,9 @@
import java.util.Arrays;
+/**
+ * Descriptive statistics over arrays of {@code double}: mean, median, mode and mean absolute deviation.
+ */
public final class Statistics {
private Statistics() {}
@@ -10,8 +13,8 @@ private Statistics() {}
* An average is the sum of a list of numbers divided by the number of
* numbers in the list.
*
- * @param data
- * @return
+ * @param data the values to average
+ * @return the arithmetic mean of {@code data}, or {@code 0.0} if it is empty
*/
public static double mean(final double[] data) {
if (data.length == 0) {
@@ -27,10 +30,14 @@ public static double mean(final double[] data) {
}
/**
- * The median is the value separating the higher half of a data sample
+ * The median is the value separating the higher half of a data sample from the lower half.
+ *
+ * Note: {@code data} is sorted in place, so the caller's array is modified. Pass a copy to keep the original
+ * order.
*
- * @param data
- * @return
+ * @param data the values; must not be empty, and is sorted ascending as a side effect
+ * @return the middle value for an odd number of values, or the mean of the two middle values for an even number
+ * @throws ArrayIndexOutOfBoundsException if {@code data} is empty
*/
public static double median(final double[] data) {
Arrays.sort(data);
@@ -46,9 +53,12 @@ public static double median(final double[] data) {
/**
* The mode is the value that appears most often in a set of data.
+ *
+ * Values are compared with {@code ==}. Runs in O(n²) time.
*
- * @param data
- * @return
+ * @param data the values
+ * @return the most frequent value; on a tie, the one that appears first in {@code data}; {@code 0.0} if
+ * {@code data} is empty
*/
public static double mode(final double[] data) {
double maxValue = 0;
@@ -70,10 +80,11 @@ public static double mode(final double[] data) {
/**
* The average absolute deviation (or mean absolute deviation) of a data set
- * is the average of the absolute deviations from a central point
+ * is the average of the absolute deviations from a central point; here the central point is the
+ * {@linkplain #mean(double[]) mean}.
*
- * @param data
- * @return the Average Absolute Deviation from data
+ * @param data the values
+ * @return the Average Absolute Deviation from data; {@code NaN} if {@code data} is empty
*/
public static double meanAbsoluteDeviation(final double[] data) {
final double mean = mean(data);
diff --git a/src/main/java/io/github/diegourban/algorithms/math/TriangleType.java b/src/main/java/io/github/diegourban/algorithms/math/TriangleType.java
index 41c7d91..e7df295 100644
--- a/src/main/java/io/github/diegourban/algorithms/math/TriangleType.java
+++ b/src/main/java/io/github/diegourban/algorithms/math/TriangleType.java
@@ -1,8 +1,15 @@
package io.github.diegourban.algorithms.math;
+/**
+ * Classification of a triangle by how many of its sides are equal, as returned by
+ * {@link Geometry#checkTriangleType(double, double, double)}.
+ */
public enum TriangleType {
+ /** All three sides are equal. */
EQUILATERAL("Equilatero"),
+ /** No two sides are equal (scalene). */
ESCALENE("Escaleno"),
+ /** Exactly two sides are equal. */
ISOSCELES("Isoceles");
private final String name;
@@ -11,6 +18,11 @@ private TriangleType(final String name) {
this.name = name;
}
+ /**
+ * Returns the display name of this type, in Portuguese.
+ *
+ * @return the display name, such as {@code "Equilatero"}
+ */
public String getName() {
return name;
}
diff --git a/src/main/java/io/github/diegourban/algorithms/sorting/InsertionSort.java b/src/main/java/io/github/diegourban/algorithms/sorting/InsertionSort.java
index 3fa938c..01ccbd6 100644
--- a/src/main/java/io/github/diegourban/algorithms/sorting/InsertionSort.java
+++ b/src/main/java/io/github/diegourban/algorithms/sorting/InsertionSort.java
@@ -1,10 +1,14 @@
package io.github.diegourban.algorithms.sorting;
/**
- * Insertion Sort Algorithm
+ * Insertion Sort Algorithm: moves each element left past larger ones until it is in place;
+ * O(n²) time (O(n) on already sorted input) and O(1) extra space.
*/
public class InsertionSort implements Sorter {
+ /** Creates an insertion sort. */
+ public InsertionSort() {}
+
@Override
public void sort(final int[] items) {
sort(items, items.length);
diff --git a/src/main/java/io/github/diegourban/algorithms/sorting/MergeSort.java b/src/main/java/io/github/diegourban/algorithms/sorting/MergeSort.java
index 2a2779d..3ee1e38 100644
--- a/src/main/java/io/github/diegourban/algorithms/sorting/MergeSort.java
+++ b/src/main/java/io/github/diegourban/algorithms/sorting/MergeSort.java
@@ -1,10 +1,13 @@
package io.github.diegourban.algorithms.sorting;
/**
- * Merge sort algorithm
+ * Merge sort algorithm: recursively sorts each half and merges them; O(n log n) time and O(n) extra space.
*/
public class MergeSort implements Sorter {
+ /** Creates a merge sort. */
+ public MergeSort() {}
+
@Override
public void sort(final int[] items) {
sort(items, 0, items.length);
diff --git a/src/main/java/io/github/diegourban/algorithms/sorting/QuickSort.java b/src/main/java/io/github/diegourban/algorithms/sorting/QuickSort.java
index b34a40a..9ebf87d 100644
--- a/src/main/java/io/github/diegourban/algorithms/sorting/QuickSort.java
+++ b/src/main/java/io/github/diegourban/algorithms/sorting/QuickSort.java
@@ -1,10 +1,15 @@
package io.github.diegourban.algorithms.sorting;
/**
- * Quick sort algorithm
+ * Quick sort algorithm: partitions around the last element (Lomuto scheme) and recurses on both sides;
+ * O(n log n) average and O(n²) worst-case time (for example on already sorted input), O(log n) average and O(n)
+ * worst-case stack space.
*/
public class QuickSort implements Sorter {
+ /** Creates a quick sort. */
+ public QuickSort() {}
+
@Override
public void sort(final int[] items) {
sort(items, 0, items.length);
diff --git a/src/main/java/io/github/diegourban/algorithms/sorting/SelectionSort.java b/src/main/java/io/github/diegourban/algorithms/sorting/SelectionSort.java
index 2cdde3a..11f8ba8 100644
--- a/src/main/java/io/github/diegourban/algorithms/sorting/SelectionSort.java
+++ b/src/main/java/io/github/diegourban/algorithms/sorting/SelectionSort.java
@@ -1,10 +1,14 @@
package io.github.diegourban.algorithms.sorting;
/**
- * Selection Sort algorithm
+ * Selection Sort algorithm: repeatedly swaps the smallest remaining element into place; O(n²) time in
+ * every case and O(1) extra space.
*/
public class SelectionSort implements Sorter {
+ /** Creates a selection sort. */
+ public SelectionSort() {}
+
@Override
public void sort(final int[] items) {
sort(items, items.length);
diff --git a/src/main/java/io/github/diegourban/algorithms/string/StringUtils.java b/src/main/java/io/github/diegourban/algorithms/string/StringUtils.java
index b7c92ea..8db1e12 100644
--- a/src/main/java/io/github/diegourban/algorithms/string/StringUtils.java
+++ b/src/main/java/io/github/diegourban/algorithms/string/StringUtils.java
@@ -2,10 +2,23 @@
import java.util.*;
+/**
+ * String checks: palindromes and anagrams.
+ */
public final class StringUtils {
private StringUtils() {}
+ /**
+ * Checks if a string reads the same forwards and backwards.
+ *
+ * The comparison is exact, character by character: case, spaces and punctuation all count, so {@code "Aba"} is
+ * not a palindrome. Runs in O(n) time.
+ *
+ * @param theString the string to check
+ * @return {@code true} if the string is a palindrome; {@code false} otherwise, including for {@code null} and the
+ * empty string
+ */
public static boolean isPalindrome(final String theString) {
if (theString == null || theString.isEmpty()) {
return false;
@@ -25,6 +38,17 @@ public static boolean isPalindrome(final String theString) {
return true;
}
+ /**
+ * Checks if two strings are anagrams: the same characters, each the same number of times, in any order.
+ *
+ * The comparison ignores case (both strings are lower-cased first) but counts spaces and punctuation. Runs in
+ * O(n log n) time by sorting the characters of both strings.
+ *
+ * @param a the first string; must not be {@code null}
+ * @param b the second string; must not be {@code null}
+ * @return {@code true} if the strings are anagrams of each other, {@code false} otherwise
+ * @throws NullPointerException if {@code a} or {@code b} is {@code null}
+ */
public static boolean isAnagram(final String a, final String b) {
if (a.length() != b.length()) {
return false;
* 1
* 1 1
* 1 2 1
* 1 3 3 1
* 1 4 6 4 1
+ *
+ *