diff --git a/README.md b/README.md index b8289c953..06ce5059e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,8 @@ This public repository contains the Java source code for the algorithms and clients in the textbook Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. -This is the official version—it is actively maintained and updated by the authors. +The official version at https://github.com/kevin-wayne/algs4 +is actively maintained and updated by the authors. The programs are organized in the package edu.princeton.cs.algs4. If you need only the class files (and not the source code), you can use algs4.jar instead. @@ -40,7 +41,7 @@ However, please do not store solutions to programming assignments in public repo ## Copyright -Copyright © 2000–2017 by Robert Sedgewick and Kevin Wayne. +Copyright © 2000–2023 by Robert Sedgewick and Kevin Wayne. ## License @@ -61,6 +62,9 @@ Some of the code in this repository has been translated to other languages: diff --git a/USING-MAVEN.md b/USING-MAVEN.md new file mode 100644 index 000000000..4befc4468 --- /dev/null +++ b/USING-MAVEN.md @@ -0,0 +1,118 @@ +This file provides brief instructions on how to add this repository to +a Maven build, from Eclipse, NetBeans, IntelliJ, or the command line. +Specifically, it provides instructions on creating a version of algs4.jar +that you can use as a dependency in your projects. + +These instructions assume that you already have installed Java 7 +(JDK 1.7) or above. + + +Using Maven in Eclipse for Java Developers +------------------------------------------ +If m2e (Maven plugin) is not built into Eclipse, follow these steps to install the plugin: + + * Open Eclipse. + * Go to Help -> Eclipse Marketplace. + * Search for Maven. + * Click "Install" button at "Maven Integration for Eclipse" or "m2e" section. + * Follow the instruction step by step., + +Restart Eclipse after installing m2e. + +Now you can import algs4 as "Maven Project" into Eclipse: + + * Open menu: File-> Import-> Maven-> Existing Maven Projects... + * Choose directory of algs4. + * Confirm import. + +To complete dependencies resolution after import: + * Right click on the project, choose Maven -> Update Project... + * Confirm project update. + +To build project in Eclipse: +Eclipse automatically builds the project every time it saved. +But if you want enforce build, do following: + * Right click on the project in Eclipse. + * Choose Run as... Maven build. + +Maven will put algs4-.jar in the directory /target. +You can use this jar as a dependency in your projects. + + +Using Maven in IntelliJ IDEA +---------------------------- +Import algs4 as "Maven Project" into IntelliJ IDEA: + +1. Open menu: File -> New -> Module from Existing Sources... +2. Choose directory of algs4. +3. Choose Import module from external model - Maven. +4. Finish. + +Last, edit `pom.xml` to add dependency: +```xml + + edu.princeton.cs + algs4 + 1.0.0.0 + +``` + +Using Maven in Netbeans +----------------------- + + +Using Maven from the Windows Command Prompt +------------------------------------------- +Download and install Maven by following the instructions at +https://maven.apache.org. + +Locate the installation directory for Maven, e.g., C:\ + +Locate the installation directory for Java, e.g., C:\Program Files\Java\ + +Set the following environment variables: + +set JAVA_HOME=C:\Program Files\Java\ +set PATH=%JAVA_HOME%\bin;%PATH% +set M2_HOME=C:\ +set PATH=%M2_HOME%\bin;%PATH% + +To create the algs4-.jar package and install it in the local +repository for reuse from other projects, change to the directory of +the algs4 repository and run Maven. + +cd +mvn clean install + +Maven will put algs4-.jar in the directory /target. +You can use this jar as a dependency in your projects. + + + +Using Maven from the Linux / Mac OS X bash shell +------------------------------------------------ +Download and install Maven, either by using your favorite package +manager (such as apt-get) or by following the instructions at +https://maven.apache.org. + +Locate the installation directory for Maven, e.g., /my/maven/ + +Locate the installation directory for Java, e.g., /my/java/ + +Set the following environment variables: + +export JAVA_HOME=/my/java/ +export PATH=$JAVA_HOME/bin:$PATH +export M2_HOME=/my/maven/ +export PATH=$M2_HOME/bin:$PATH + +To create the algs4-.jar package and install it in the local +repository for reuse from other projects, change to the directory of +the algs4 repository and run Maven. + +cd +mvn clean install + +Maven will put algs4-.jar in the directory /target. +You can use this jar as a dependency in your projects. + diff --git a/build.gradle b/build.gradle index c6147a48f..b4b11316e 100644 --- a/build.gradle +++ b/build.gradle @@ -78,6 +78,6 @@ bintray { } } -task wrapper(type: Wrapper) { - gradleVersion = '2.4' -} +wrapper { + gradleVersion = '2.6' +} \ No newline at end of file diff --git a/src/main/java/edu/princeton/cs/algs4/AVLTreeST.java b/src/main/java/edu/princeton/cs/algs4/AVLTreeST.java index 9c1adf7f6..4f9042251 100644 --- a/src/main/java/edu/princeton/cs/algs4/AVLTreeST.java +++ b/src/main/java/edu/princeton/cs/algs4/AVLTreeST.java @@ -811,7 +811,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Accumulator.java b/src/main/java/edu/princeton/cs/algs4/Accumulator.java index 6ea8fa09c..14fa53e98 100644 --- a/src/main/java/edu/princeton/cs/algs4/Accumulator.java +++ b/src/main/java/edu/princeton/cs/algs4/Accumulator.java @@ -22,13 +22,13 @@ * to floating-point roundoff error than the more straightforward * implementation based on saving the sum of the squares of the numbers. * This technique is due to - * B. P. Welford. + * B. P. Welford. * Each operation takes constant time in the worst case. * The amount of memory is constant - the data values are not stored. *

- * For additional documentation, - * see Section 1.2 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 1.2 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -52,7 +52,7 @@ public void addDataValue(double x) { n++; double delta = x - mu; mu += delta / n; - sum += (double) (n - 1) / n * delta * delta; + sum += ((double) (n - 1) / n) * delta * delta; } /** @@ -121,7 +121,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/AcyclicLP.java b/src/main/java/edu/princeton/cs/algs4/AcyclicLP.java index 479aac325..a67761834 100644 --- a/src/main/java/edu/princeton/cs/algs4/AcyclicLP.java +++ b/src/main/java/edu/princeton/cs/algs4/AcyclicLP.java @@ -3,20 +3,20 @@ * Execution: java AcyclicP V E * Dependencies: EdgeWeightedDigraph.java DirectedEdge.java Topological.java * Data files: https://algs4.cs.princeton.edu/44sp/tinyEWDAG.txt - * - * Computes longeset paths in an edge-weighted acyclic digraph. + * + * Computes longest paths in an edge-weighted acyclic digraph. * * Remark: should probably check that graph is a DAG before running * * % java AcyclicLP tinyEWDAG.txt 5 - * 5 to 0 (2.44) 5->1 0.32 1->3 0.29 3->6 0.52 6->4 0.93 4->0 0.38 - * 5 to 1 (0.32) 5->1 0.32 - * 5 to 2 (2.77) 5->1 0.32 1->3 0.29 3->6 0.52 6->4 0.93 4->7 0.37 7->2 0.34 - * 5 to 3 (0.61) 5->1 0.32 1->3 0.29 - * 5 to 4 (2.06) 5->1 0.32 1->3 0.29 3->6 0.52 6->4 0.93 - * 5 to 5 (0.00) - * 5 to 6 (1.13) 5->1 0.32 1->3 0.29 3->6 0.52 - * 5 to 7 (2.43) 5->1 0.32 1->3 0.29 3->6 0.52 6->4 0.93 4->7 0.37 + * 5 to 0 (2.44) 5->1 0.32 1->3 0.29 3->6 0.52 6->4 0.93 4->0 0.38 + * 5 to 1 (0.32) 5->1 0.32 + * 5 to 2 (2.77) 5->1 0.32 1->3 0.29 3->6 0.52 6->4 0.93 4->7 0.37 7->2 0.34 + * 5 to 3 (0.61) 5->1 0.32 1->3 0.29 + * 5 to 4 (2.06) 5->1 0.32 1->3 0.29 3->6 0.52 6->4 0.93 + * 5 to 5 (0.00) + * 5 to 6 (1.13) 5->1 0.32 1->3 0.29 3->6 0.52 + * 5 to 7 (2.43) 5->1 0.32 1->3 0.29 3->6 0.52 6->4 0.93 4->7 0.37 * ******************************************************************************/ @@ -28,15 +28,24 @@ * acyclic graphs (DAGs). The edge weights can be positive, negative, or zero. *

* This implementation uses a topological-sort based algorithm. - * The constructor takes time proportional to V + E, - * where V is the number of vertices and E is the number of edges. - * Each call to {@code distTo(int)} and {@code hasPathTo(int)} takes constant time; - * each call to {@code pathTo(int)} takes time proportional to the number of - * edges in the shortest path returned. + * The constructor takes Θ(V + E) time in the + * worst case, where V is the number of vertices and + * E is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the + * edge-weighted digraph). + *

+ * This correctly computes longest paths if all arithmetic performed is + * without floating-point rounding error or arithmetic overflow. + * This is the case if all edge weights are integers and if none of the + * intermediate results exceeds 252. Since all intermediate + * results are sums of edge weights, they are bounded by V C, + * where V is the number of vertices and C is the maximum + * absolute value of any edge weight. *

- * For additional documentation, - * see Section 4.4 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 4.4 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -79,7 +88,7 @@ private void relax(DirectedEdge e) { if (distTo[w] < distTo[v] + e.weight()) { distTo[w] = distTo[v] + e.weight(); edgeTo[w] = e; - } + } } /** @@ -158,7 +167,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/AcyclicSP.java b/src/main/java/edu/princeton/cs/algs4/AcyclicSP.java index 745e5343e..f90e9155f 100644 --- a/src/main/java/edu/princeton/cs/algs4/AcyclicSP.java +++ b/src/main/java/edu/princeton/cs/algs4/AcyclicSP.java @@ -7,14 +7,14 @@ * Computes shortest paths in an edge-weighted acyclic digraph. * * % java AcyclicSP tinyEWDAG.txt 5 - * 5 to 0 (0.73) 5->4 0.35 4->0 0.38 - * 5 to 1 (0.32) 5->1 0.32 - * 5 to 2 (0.62) 5->7 0.28 7->2 0.34 - * 5 to 3 (0.61) 5->1 0.32 1->3 0.29 - * 5 to 4 (0.35) 5->4 0.35 - * 5 to 5 (0.00) - * 5 to 6 (1.13) 5->1 0.32 1->3 0.29 3->6 0.52 - * 5 to 7 (0.28) 5->7 0.28 + * 5 to 0 (0.73) 5->4 0.35 4->0 0.38 + * 5 to 1 (0.32) 5->1 0.32 + * 5 to 2 (0.62) 5->7 0.28 7->2 0.34 + * 5 to 3 (0.61) 5->1 0.32 1->3 0.29 + * 5 to 4 (0.35) 5->4 0.35 + * 5 to 5 (0.00) + * 5 to 6 (1.13) 5->1 0.32 1->3 0.29 3->6 0.52 + * 5 to 7 (0.28) 5->7 0.28 * ******************************************************************************/ @@ -26,15 +26,24 @@ * graphs (DAGs). The edge weights can be positive, negative, or zero. *

* This implementation uses a topological-sort based algorithm. - * The constructor takes time proportional to V + E, - * where V is the number of vertices and E is the number of edges. - * Each call to {@code distTo(int)} and {@code hasPathTo(int)} takes constant time; - * each call to {@code pathTo(int)} takes time proportional to the number of - * edges in the shortest path returned. + * The constructor takes Θ(V + E) time in the + * worst case, where V is the number of vertices and + * E is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the + * edge-weighted digraph). *

- * For additional documentation, - * see Section 4.4 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * This correctly computes shortest paths if all arithmetic performed is + * without floating-point rounding error or arithmetic overflow. + * This is the case if all edge weights are integers and if none of the + * intermediate results exceeds 252. Since all intermediate + * results are sums of edge weights, they are bounded by V C, + * where V is the number of vertices and C is the maximum + * absolute value of any edge weight. + *

+ * For additional documentation, + * see Section 4.4 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -78,7 +87,7 @@ private void relax(DirectedEdge e) { if (distTo[w] > distTo[v] + e.weight()) { distTo[w] = distTo[v] + e.weight(); edgeTo[w] = e; - } + } } /** @@ -157,7 +166,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/AdjMatrixEdgeWeightedDigraph.java b/src/main/java/edu/princeton/cs/algs4/AdjMatrixEdgeWeightedDigraph.java index 8cbd6929f..797a2c4f4 100644 --- a/src/main/java/edu/princeton/cs/algs4/AdjMatrixEdgeWeightedDigraph.java +++ b/src/main/java/edu/princeton/cs/algs4/AdjMatrixEdgeWeightedDigraph.java @@ -5,7 +5,7 @@ * * An edge-weighted digraph, implemented using an adjacency matrix. * Parallel edges are disallowed; self-loops are allowed. - * + * ******************************************************************************/ package edu.princeton.cs.algs4; @@ -14,7 +14,7 @@ import java.util.NoSuchElementException; /** - * The {@code AdjMatrixEdgeWeightedDigraph} class represents a edge-weighted + * The {@code AdjMatrixEdgeWeightedDigraph} class represents an edge-weighted * digraph of vertices named 0 through V - 1, where each * directed edge is of type {@link DirectedEdge} and has a real-valued weight. * It supports the following two primary operations: add a directed edge @@ -41,14 +41,14 @@ public class AdjMatrixEdgeWeightedDigraph { private final int V; private int E; private DirectedEdge[][] adj; - + /** * Initializes an empty edge-weighted digraph with {@code V} vertices and 0 edges. * @param V the number of vertices * @throws IllegalArgumentException if {@code V < 0} */ public AdjMatrixEdgeWeightedDigraph(int V) { - if (V < 0) throw new IllegalArgumentException("number of vertices must be nonnegative"); + if (V < 0) throw new IllegalArgumentException("number of vertices must be non-negative"); this.V = V; this.E = 0; this.adj = new DirectedEdge[V][V]; @@ -63,14 +63,14 @@ public AdjMatrixEdgeWeightedDigraph(int V) { */ public AdjMatrixEdgeWeightedDigraph(int V, int E) { this(V); - if (E < 0) throw new IllegalArgumentException("number of edges must be nonnegative"); + if (E < 0) throw new IllegalArgumentException("number of edges must be non-negative"); if (E > V*V) throw new IllegalArgumentException("too many edges"); // can be inefficient while (this.E != E) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); - double weight = Math.round(100 * StdRandom.uniform()) / 100.0; + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); + double weight = 0.01 * StdRandom.uniformInt(0, 100); addEdge(new DirectedEdge(v, w, weight)); } } @@ -192,7 +192,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/WhiteFilter.java b/src/main/java/edu/princeton/cs/algs4/AllowFilter.java similarity index 71% rename from src/main/java/edu/princeton/cs/algs4/WhiteFilter.java rename to src/main/java/edu/princeton/cs/algs4/AllowFilter.java index 18677bf96..4f269496e 100644 --- a/src/main/java/edu/princeton/cs/algs4/WhiteFilter.java +++ b/src/main/java/edu/princeton/cs/algs4/AllowFilter.java @@ -1,25 +1,25 @@ /****************************************************************************** - * Compilation: javac WhiteFilter.java - * Execution: java WhiteFilter whitelist.txt < input.txt + * Compilation: javac AllowFilter.java + * Execution: java AllowFilter allowlist.txt < input.txt * Dependencies: SET In.java StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/35applications/tinyTale.txt - * https://algs4.cs.princeton.edu/35applications/list.txt - * - * Read in a whitelist of words from a file. Then read in a list of - * words from standard input and print out all those words that + * https://algs4.cs.princeton.edu/35applications/allowlist.txt + * + * Reads an allowlist of words from a file. Then readsa list of + * words from standard input and prints all those words that * are in the first file. - * - * % more tinyTale.txt - * it was the best of times it was the worst of times - * it was the age of wisdom it was the age of foolishness - * it was the epoch of belief it was the epoch of incredulity - * it was the season of light it was the season of darkness + * + * % more tinyTale.txt + * it was the best of times it was the worst of times + * it was the age of wisdom it was the age of foolishness + * it was the epoch of belief it was the epoch of incredulity + * it was the season of light it was the season of darkness * it was the spring of hope it was the winter of despair * - * % more list.txt - * was it the of - * - * % java WhiteFilter list.txt < tinyTale.txt + * % more list.txt + * was it the of + * + * % java AllowFilter list.txt < tinyTale.txt * it was the of it was the of * it was the of it was the of * it was the of it was the of @@ -31,21 +31,22 @@ package edu.princeton.cs.algs4; /** - * The {@code WhiteFilter} class provides a client for reading in a whitelist + * The {@code AllowFilter} class provides a client for reading in an allowlist * of words from a file; then, reading in a sequence of words from standard input, * printing out each word that appears in the file. - * It is useful as a test client for various symbol table implementations. + * It is useful as a test client for various symbol-table implementations. *

- * For additional documentation, see Section 3.5 of + * For additional documentation, + * see Section 3.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ -public class WhiteFilter { +public class AllowFilter { // Do not instantiate. - private WhiteFilter() { } + private AllowFilter() { } public static void main(String[] args) { SET set = new SET(); @@ -67,7 +68,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Whitelist.java b/src/main/java/edu/princeton/cs/algs4/Allowlist.java similarity index 68% rename from src/main/java/edu/princeton/cs/algs4/Whitelist.java rename to src/main/java/edu/princeton/cs/algs4/Allowlist.java index 331029258..58c0b13c3 100644 --- a/src/main/java/edu/princeton/cs/algs4/Whitelist.java +++ b/src/main/java/edu/princeton/cs/algs4/Allowlist.java @@ -1,22 +1,22 @@ /****************************************************************************** - * Compilation: javac Whitelist.java - * Execution: java Whitelist whitelist.txt < data.txt + * Compilation: javac Allowlist.java + * Execution: java Allowlist allowlist.txt < data.txt * Dependencies: StaticSetOfInts.java In.java StdOut.java * - * Data files: https://algs4.cs.princeton.edu/11model/tinyW.txt - * https://algs4.cs.princeton.edu/11model/tinyT.txt - * https://algs4.cs.princeton.edu/11model/largeW.txt - * https://algs4.cs.princeton.edu/11model/largeT.txt + * Data files: https://algs4.cs.princeton.edu/11model/tinyAllowlist.txt + * https://algs4.cs.princeton.edu/11model/tinyText.txt + * https://algs4.cs.princeton.edu/11model/largeAllowlist.txt + * https://algs4.cs.princeton.edu/11model/largeText.txt * - * Whitelist filter. + * Allowlist filter. * * - * % java Whitelist tinyW.txt < tinyT.txt + * % java Allowlist tinyAllowlist.txt < tinyText.txt * 50 * 99 * 13 * - * % java Whitelist largeW.txt < largeT.txt | more + * % java Allowlist largeAllowlist.txt < largeText.txt | more * 499569 * 984875 * 295754 @@ -30,24 +30,25 @@ package edu.princeton.cs.algs4; /** - * The {@code Whitelist} class provides a client for reading in + * The {@code Allowlist} class provides a client for reading in * a set of integers from a file; reading in a sequence of integers - * from standard input; and printing to standard output those - * integers not in the whitelist. + * from standard input; and printing to standard output those + * integers not in the allowlist. *

- * For additional documentation, see Section 1.2 of + * For additional documentation, + * see Section 1.2 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ -public class Whitelist { +public class Allowlist { // Do not instantiate. - private Whitelist() { } + private Allowlist() { } /** - * Reads in a sequence of integers from the whitelist file, specified as + * Reads in a sequence of integers from the allowlist file, specified as * a command-line argument. Reads in integers from standard input and * prints to standard output those integers that are not in the file. * @@ -58,7 +59,7 @@ public static void main(String[] args) { int[] white = in.readAllInts(); StaticSETofInts set = new StaticSETofInts(white); - // Read key, print if not in whitelist. + // Read key, print if not in allowlist. while (!StdIn.isEmpty()) { int key = StdIn.readInt(); if (!set.contains(key)) @@ -68,7 +69,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Alphabet.java b/src/main/java/edu/princeton/cs/algs4/Alphabet.java index c38ca69da..0bcba1662 100644 --- a/src/main/java/edu/princeton/cs/algs4/Alphabet.java +++ b/src/main/java/edu/princeton/cs/algs4/Alphabet.java @@ -256,7 +256,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/AmericanFlag.java b/src/main/java/edu/princeton/cs/algs4/AmericanFlag.java index 61111e8bd..c83b70dae 100644 --- a/src/main/java/edu/princeton/cs/algs4/AmericanFlag.java +++ b/src/main/java/edu/princeton/cs/algs4/AmericanFlag.java @@ -1,14 +1,14 @@ /****************************************************************************** * Compilation: javac AmericanFlag.java * Execution: java AmericanFlag < input.txt - * java AmericanFlag int < input-non-negative-ints.txt + * java AmericanFlag int < input-non-negative-ints.txt * Dependencies: StdIn.java StdOut.java Stack.java * Data files: https://algs4.cs.princeton.edu/51radix/words3.txt * https://algs4.cs.princeton.edu/51radix/shells.txt * * Sort an array of strings or integers in-place using American flag sort. * - * % java AmericanFlag < shells.txt + * % java AmericanFlag < shells.txt * are * by * sea @@ -30,7 +30,7 @@ /** * The {@code AmericanFlag} class provides static methods for sorting an - * array of extended ASCII strings or integers in-place using + * array of extended ASCII strings or integers in-place using * American flag sort. This is a non-recursive implementation. *

* For additional documentation, @@ -38,7 +38,7 @@ * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne * and * Engineering Radix Sort by McIlroy and Bostic. - * For a version that uses only one auxilary array, see {@link AmericanFlagX}. + * For a version that uses only one auxiliary array, see {@link AmericanFlagX}. * * @author Robert Sedgewick * @author Kevin Wayne @@ -47,12 +47,12 @@ public class AmericanFlag { private static final int BITS_PER_BYTE = 8; - private static final int BITS_PER_INT = 32; // each Java int is 32 bits + private static final int BITS_PER_INT = 32; // each Java int is 32 bits private static final int R = 256; // extend ASCII alphabet size private static final int CUTOFF = 15; // cutoff to insertion sort // do not instantiate - private AmericanFlag() { } + private AmericanFlag() { } // return dth character of s, -1 if d = length of string private static int charAt(String s, int d) { @@ -82,12 +82,12 @@ public static void sort(String[] a, int lo, int hi) { st.push(lo); st.push(hi); st.push(d); - + while (!st.isEmpty()) { d = st.pop(); hi = st.pop(); lo = st.pop(); - + if (hi <= lo + CUTOFF) { insertion(a, lo, hi, d); continue; @@ -103,12 +103,12 @@ public static void sort(String[] a, int lo, int hi) { first[0] = lo; for (int c = 0; c <= R; c++) { first[c+1] += first[c]; - - if (c > 0 && first[c+1]-1 > first[c]) { + + if (c > 0 && first[c+1]-1 > first[c]) { // add subproblem for character c (excludes sentinel c == 0) st.push(first[c]); st.push(first[c+1] - 1); - st.push(d+1); + st.push(d+1); } } @@ -125,7 +125,7 @@ public static void sort(String[] a, int lo, int hi) { } next[c]++; } - + // clear first[] and next[] arrays for (int c = 0; c < R+2; c++) { first[c] = 0; @@ -133,7 +133,7 @@ public static void sort(String[] a, int lo, int hi) { } } } - + // insertion sort a[lo..hi], starting at dth character private static void insertion(String[] a, int lo, int hi, int d) { for (int i = lo; i <= hi; i++) @@ -160,7 +160,7 @@ private static boolean less(String v, String w, int d) { /** * Rearranges the array of 32-bit integers in ascending order. - * Currently assumes that the integers are nonnegative. + * Currently, assumes that the integers are nonnegative. * * @param a the array to be sorted */ @@ -180,17 +180,17 @@ private static void sort(int[] a, int lo, int hi) { st.push(lo); st.push(hi); st.push(d); - + while (!st.isEmpty()) { d = st.pop(); hi = st.pop(); lo = st.pop(); - + if (hi <= lo + CUTOFF) { insertion(a, lo, hi, d); continue; } - + // compute frequency counts (need R = 256) int shift = BITS_PER_INT - BITS_PER_BYTE*d - BITS_PER_BYTE; for (int i = lo; i <= hi; i++) { @@ -202,12 +202,12 @@ private static void sort(int[] a, int lo, int hi) { first[0] = lo; for (int c = 0; c < R; c++) { first[c+1] += first[c]; - - if (d < 3 && first[c+1]-1 > first[c]) { + + if (d < 3 && first[c+1]-1 > first[c]) { // add subproblem for byte c st.push(first[c]); st.push(first[c+1] - 1); - st.push(d+1); + st.push(d+1); } } @@ -224,7 +224,7 @@ private static void sort(int[] a, int lo, int hi) { } next[c]++; } - + // clear first[] and next[] arrays for (int c = 0; c < R+1; c++) { first[c] = 0; @@ -246,7 +246,7 @@ private static void exch(int[] a, int i, int j) { a[i] = a[j]; a[j] = temp; } - + // is v less than w, starting at byte d private static boolean less(int v, int w, int d) { int mask = R - 1; // 0xFF; @@ -259,7 +259,7 @@ private static boolean less(int v, int w, int d) { } return false; } - + /** * Reads in a sequence of extended ASCII strings or non-negative ints from standard input; * American flag sorts them; @@ -289,7 +289,7 @@ public static void main(String[] args) { /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/AmericanFlagX.java b/src/main/java/edu/princeton/cs/algs4/AmericanFlagX.java index 8228f4af8..2d2e6c688 100644 --- a/src/main/java/edu/princeton/cs/algs4/AmericanFlagX.java +++ b/src/main/java/edu/princeton/cs/algs4/AmericanFlagX.java @@ -7,7 +7,7 @@ * * Sort an array of strings or integers in-place using American Flag sort. * - * % java AmericanFlagX < shells.txt + * % java AmericanFlagX < shells.txt * are * by * sea @@ -29,8 +29,8 @@ /** * The {@code AmericanFlagX} class provides static methods for sorting an - * array of extended ASCII strings or integers in-place using - * American Flag sort. This implementation is non-recursive and uses only + * array of extended ASCII strings or integers in-place using + * American Flag sort. This implementation is non-recursive and uses only * one auxiliary array. *

* For additional documentation, @@ -38,7 +38,7 @@ * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne * and * Engineering Radix Sort by McIlroy and Bostic. - * For a version that uses two auxilary arrays, see {@link AmericanFlag}. + * For a version that uses two auxiliary arrays, see {@link AmericanFlag}. * * @author Ivan Pesin */ @@ -48,7 +48,7 @@ public class AmericanFlagX { private static final int CUTOFF = 15; // cutoff to insertion sort // do not instantiate - private AmericanFlagX() { } + private AmericanFlagX() { } // return dth character of s, -1 if d = length of string private static int charAt(String s, int d) { @@ -77,7 +77,7 @@ public static void sort(String[] a, int lo, int hi) { st.push(lo); st.push(hi); st.push(d); - + while (!st.isEmpty()) { d = st.pop(); hi = st.pop(); @@ -94,22 +94,22 @@ public static void sort(String[] a, int lo, int hi) { count[c]++; } - // accumulate counts relative to a[0], so that + // accumulate counts relative to a[0], so that // count[c] is the number of keys <= c count[0] += lo; for (int c = 0; c < R; c++) { count[c+1] += count[c]; - - if (c > 0 && count[c+1]-1 > count[c]) { + + if (c > 0 && count[c+1]-1 > count[c]) { // add subproblem for character c (excludes sentinel c == 0) st.push(count[c]); st.push(count[c+1]-1); - st.push(d+1); + st.push(d+1); } } // permute data in place - // for details and proof see Knuth Theorem 5.1.2B and ch 5.2 excercise 13. + // for details and proof see Knuth Theorem 5.1.2B and ch 5.2 exercise 13. for (int r = hi; r >= lo; r--) { // locate element that must be shifted right of r @@ -122,20 +122,20 @@ public static void sort(String[] a, int lo, int hi) { // if r < lo the subarray is sorted. if (r < lo) break; - + // permute a[r] until correct element is in place while (--count[c] != r) { exch(a, r, count[c]); c = charAt(a[r], d) + 1; } } - + // clear count[] array for (int c = 0; c < R+1; c++) count[c] = 0; } } - + // insertion sort a[lo..hi], starting at dth character private static void insertion(String[] a, int lo, int hi, int d) { for (int i = lo; i <= hi; i++) @@ -159,7 +159,7 @@ private static boolean less(String v, String w, int d) { } return v.length() < w.length(); } - + /** * Reads in a sequence of extended ASCII strings or non-negative ints from standard input; * American flag sorts them; @@ -167,7 +167,7 @@ private static boolean less(String v, String w, int d) { * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { String[] a = StdIn.readAllStrings(); sort(a); // print results @@ -178,7 +178,7 @@ public static void main(String[] args) { /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Arbitrage.java b/src/main/java/edu/princeton/cs/algs4/Arbitrage.java index 2cf53e485..850184be1 100644 --- a/src/main/java/edu/princeton/cs/algs4/Arbitrage.java +++ b/src/main/java/edu/princeton/cs/algs4/Arbitrage.java @@ -35,6 +35,14 @@ * The running time is proportional to V3 in the * worst case, where V is the number of currencies. *

+ * This code is guaranteed to find an arbitrage opportunity in a + * currency exchange table (or report that no such arbitrage + * opportunity exists) under the assumption that all arithmetic + * performed is without floating-point rounding error or arithmetic + * overflow. Since the code computes the logarithms of the edge weights, + * floating-point rounding error will be present, and it may fail on + * some pathological inputs. + *

* For additional documentation, * see Section 4.4 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. @@ -88,7 +96,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/AssignmentProblem.java b/src/main/java/edu/princeton/cs/algs4/AssignmentProblem.java index 0cb0be67a..f1630c55d 100644 --- a/src/main/java/edu/princeton/cs/algs4/AssignmentProblem.java +++ b/src/main/java/edu/princeton/cs/algs4/AssignmentProblem.java @@ -24,6 +24,11 @@ * O(n^3 log n) to solve an n-by-n * instance. *

+ * This computes correct results if all arithmetic performed is + * without floating-point rounding error or arithmetic overflow. + * This is the case if all edge weights are integers and if none of the + * intermediate results exceeds 252. + *

* For additional documentation, see * Section 6.5 * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. @@ -32,7 +37,7 @@ * @author Kevin Wayne */ public class AssignmentProblem { - private static final double FLOATING_POINT_EPSILON = 1E-14; + private static final double FLOATING_POINT_EPSILON = 1.0E-14; private static final int UNMATCHED = -1; private int n; // number of rows and columns @@ -49,7 +54,7 @@ public class AssignmentProblem { * @param weight the n-by-n matrix of weights * @throws IllegalArgumentException unless all weights are nonnegative * @throws IllegalArgumentException if {@code weight} is {@code null} - */ + */ public AssignmentProblem(double[][] weight) { if (weight == null) throw new IllegalArgumentException("constructor argument is null"); @@ -85,7 +90,7 @@ public AssignmentProblem(double[][] weight) { assert certifySolution(); } - // find shortest augmenting path and upate + // find shortest augmenting path and update private void augment() { // build residual graph @@ -126,7 +131,7 @@ private void augment() { } // reduced cost of i-j - // (subtracting off minWeight reweights all weights to be non-negative) + // (subtracting off minWeight re-weights all weights to be non-negative) private double reducedCost(int i, int j) { double reducedCost = (weight[i][j] - minWeight) + px[i] - py[j]; @@ -281,7 +286,7 @@ public static void main(String[] args) { double[][] weight = new double[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { - weight[i][j] = StdRandom.uniform(900) + 100; // 3 digits + weight[i][j] = StdRandom.uniformInt(900) + 100; // 3 digits } } @@ -306,7 +311,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Average.java b/src/main/java/edu/princeton/cs/algs4/Average.java index 71c887a78..1c4706f65 100644 --- a/src/main/java/edu/princeton/cs/algs4/Average.java +++ b/src/main/java/edu/princeton/cs/algs4/Average.java @@ -2,7 +2,7 @@ * Compilation: javac Average.java * Execution: java Average < data.txt * Dependencies: StdIn.java StdOut.java - * + * * Reads in a sequence of real numbers, and computes their average. * * % java Average @@ -28,7 +28,7 @@ * @author Robert Sedgewick * @author Kevin Wayne */ -public class Average { +public class Average { // this class should not be instantiated private Average() { } @@ -39,7 +39,7 @@ private Average() { } * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { int count = 0; // number input values double sum = 0.0; // sum of input values @@ -59,7 +59,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BST.java b/src/main/java/edu/princeton/cs/algs4/BST.java index b80e927cf..1645d01d9 100644 --- a/src/main/java/edu/princeton/cs/algs4/BST.java +++ b/src/main/java/edu/princeton/cs/algs4/BST.java @@ -2,13 +2,13 @@ * Compilation: javac BST.java * Execution: java BST * Dependencies: StdIn.java StdOut.java Queue.java - * Data files: https://algs4.cs.princeton.edu/32bst/tinyST.txt + * Data files: https://algs4.cs.princeton.edu/32bst/tinyST.txt * * A symbol table implemented with a binary search tree. - * + * * % more tinyST.txt * S E A R C H E X A M P L E - * + * * % java BST < tinyST.txt * A 8 * C 4 @@ -43,22 +43,26 @@ * value associated with a key to {@code null} is equivalent to deleting the key * from the symbol table. *

- * This implementation uses an (unbalanced) binary search tree. It requires that + * It requires that * the key type implements the {@code Comparable} interface and calls the * {@code compareTo()} and method to compare two keys. It does not call either * {@code equals()} or {@code hashCode()}. + *

+ * This implementation uses an (unbalanced) binary search tree. * The put, contains, remove, minimum, * maximum, ceiling, floor, select, and - * rank operations each take - * linear time in the worst case, if the tree becomes unbalanced. - * The size, and is-empty operations take constant time. - * Construction takes constant time. + * rank operations each take Θ(n) time in the worst + * case, where n is the number of key-value pairs. + * The size and is-empty operations take Θ(1) time. + * The keys method takes Θ(n) time in the worst case. + * Construction takes Θ(1) time. *

- * For additional documentation, see Section 3.2 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. - * For other implementations, see {@link ST}, {@link BinarySearchST}, - * {@link SequentialSearchST}, {@link RedBlackBST}, + * For alternative implementations of the symbol table API, see {@link ST}, + * {@link BinarySearchST}, {@link SequentialSearchST}, {@link RedBlackBST}, * {@link SeparateChainingHashST}, and {@link LinearProbingHashST}, + * For additional documentation, see + * Section 3.2 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -142,7 +146,7 @@ private Value get(Node x, Key key) { } /** - * Inserts the specified key-value pair into the symbol table, overwriting the old + * Inserts the specified key-value pair into the symbol table, overwriting the old * value with the new value if the symbol table already contains the specified key. * Deletes the specified key (and its associated value) from this symbol table * if the specified value is {@code null}. @@ -209,8 +213,8 @@ private Node deleteMax(Node x) { } /** - * Removes the specified key and its associated value from this symbol table - * (if the key is in this symbol table). + * Removes the specified key and its associated value from this symbol table + * (if the key is in this symbol table). * * @param key the key * @throws IllegalArgumentException if {@code key} is {@code null} @@ -227,17 +231,17 @@ private Node delete(Node x, Key key) { int cmp = key.compareTo(x.key); if (cmp < 0) x.left = delete(x.left, key); else if (cmp > 0) x.right = delete(x.right, key); - else { + else { if (x.right == null) return x.left; if (x.left == null) return x.right; Node t = x; x = min(t.right); x.right = deleteMin(t.right); x.left = t.left; - } + } x.size = size(x.left) + size(x.right) + 1; return x; - } + } /** @@ -249,12 +253,12 @@ private Node delete(Node x, Key key) { public Key min() { if (isEmpty()) throw new NoSuchElementException("calls min() with empty symbol table"); return min(root).key; - } + } - private Node min(Node x) { - if (x.left == null) return x; - else return min(x.left); - } + private Node min(Node x) { + if (x.left == null) return x; + else return min(x.left); + } /** * Returns the largest key in the symbol table. @@ -265,12 +269,12 @@ private Node min(Node x) { public Key max() { if (isEmpty()) throw new NoSuchElementException("calls max() with empty symbol table"); return max(root).key; - } + } private Node max(Node x) { - if (x.right == null) return x; - else return max(x.right); - } + if (x.right == null) return x; + else return max(x.right); + } /** * Returns the largest key in the symbol table less than or equal to {@code key}. @@ -284,22 +288,25 @@ public Key floor(Key key) { if (key == null) throw new IllegalArgumentException("argument to floor() is null"); if (isEmpty()) throw new NoSuchElementException("calls floor() with empty symbol table"); Node x = floor(root, key); - if (x == null) return null; + if (x == null) throw new NoSuchElementException("argument to floor() is too small"); else return x.key; - } + } private Node floor(Node x, Key key) { if (x == null) return null; int cmp = key.compareTo(x.key); if (cmp == 0) return x; if (cmp < 0) return floor(x.left, key); - Node t = floor(x.right, key); + Node t = floor(x.right, key); if (t != null) return t; - else return x; - } + else return x; + } public Key floor2(Key key) { - return floor2(root, key, null); + Key x = floor2(root, key, null); + if (x == null) throw new NoSuchElementException("argument to floor() is too small"); + else return x; + } private Key floor2(Node x, Key key, Key best) { @@ -308,7 +315,7 @@ private Key floor2(Node x, Key key, Key best) { if (cmp < 0) return floor2(x.left, key, best); else if (cmp > 0) return floor2(x.right, key, x.key); else return x.key; - } + } /** * Returns the smallest key in the symbol table greater than or equal to {@code key}. @@ -322,7 +329,7 @@ public Key ceiling(Key key) { if (key == null) throw new IllegalArgumentException("argument to ceiling() is null"); if (isEmpty()) throw new NoSuchElementException("calls ceiling() with empty symbol table"); Node x = ceiling(root, key); - if (x == null) return null; + if (x == null) throw new NoSuchElementException("argument to ceiling() is too large"); else return x.key; } @@ -330,39 +337,41 @@ private Node ceiling(Node x, Key key) { if (x == null) return null; int cmp = key.compareTo(x.key); if (cmp == 0) return x; - if (cmp < 0) { - Node t = ceiling(x.left, key); + if (cmp < 0) { + Node t = ceiling(x.left, key); if (t != null) return t; - else return x; - } - return ceiling(x.right, key); - } + else return x; + } + return ceiling(x.right, key); + } /** - * Return the key in the symbol table whose rank is {@code k}. - * This is the (k+1)st smallest key in the symbol table. + * Return the key in the symbol table of a given {@code rank}. + * This key has the property that there are {@code rank} keys in + * the symbol table that are smaller. In other words, this key is the + * ({@code rank}+1)st smallest key in the symbol table. * - * @param k the order statistic - * @return the key in the symbol table of rank {@code k} - * @throws IllegalArgumentException unless {@code k} is between 0 and + * @param rank the order statistic + * @return the key in the symbol table of given {@code rank} + * @throws IllegalArgumentException unless {@code rank} is between 0 and * n–1 */ - public Key select(int k) { - if (k < 0 || k >= size()) { - throw new IllegalArgumentException("argument to select() is invalid: " + k); + public Key select(int rank) { + if (rank < 0 || rank >= size()) { + throw new IllegalArgumentException("argument to select() is invalid: " + rank); } - Node x = select(root, k); - return x.key; + return select(root, rank); } - // Return key of rank k. - private Node select(Node x, int k) { - if (x == null) return null; - int t = size(x.left); - if (t > k) return select(x.left, k); - else if (t < k) return select(x.right, k-t-1); - else return x; - } + // Return key in BST rooted at x of given rank. + // Precondition: rank is in legal range. + private Key select(Node x, int rank) { + if (x == null) return null; + int leftSize = size(x.left); + if (leftSize > rank) return select(x.left, rank); + else if (leftSize < rank) return select(x.right, rank - leftSize - 1); + else return x.key; + } /** * Return the number of keys in the symbol table strictly less than {@code key}. @@ -374,23 +383,24 @@ private Node select(Node x, int k) { public int rank(Key key) { if (key == null) throw new IllegalArgumentException("argument to rank() is null"); return rank(key, root); - } + } // Number of keys in the subtree less than key. private int rank(Key key, Node x) { - if (x == null) return 0; - int cmp = key.compareTo(x.key); - if (cmp < 0) return rank(key, x.left); - else if (cmp > 0) return 1 + size(x.left) + rank(key, x.right); - else return size(x.left); - } + if (x == null) return 0; + int cmp = key.compareTo(x.key); + if (cmp < 0) return rank(key, x.left); + else if (cmp > 0) return 1 + size(x.left) + rank(key, x.right); + else return size(x.left); + } /** - * Returns all keys in the symbol table as an {@code Iterable}. + * Returns all keys in the symbol table in ascending order, + * as an {@code Iterable}. * To iterate over all of the keys in the symbol table named {@code st}, * use the foreach notation: {@code for (Key key : st.keys())}. * - * @return all keys in the symbol table + * @return all keys in the symbol table in ascending order */ public Iterable keys() { if (isEmpty()) return new Queue(); @@ -398,13 +408,13 @@ public Iterable keys() { } /** - * Returns all keys in the symbol table in the given range, - * as an {@code Iterable}. + * Returns all keys in the symbol table in the given range + * in ascending order, as an {@code Iterable}. * * @param lo minimum endpoint * @param hi maximum endpoint - * @return all keys in the symbol table between {@code lo} - * (inclusive) and {@code hi} (inclusive) + * @return all keys in the symbol table between {@code lo} + * (inclusive) and {@code hi} (inclusive) in ascending order * @throws IllegalArgumentException if either {@code lo} or {@code hi} * is {@code null} */ @@ -415,23 +425,23 @@ public Iterable keys(Key lo, Key hi) { Queue queue = new Queue(); keys(root, queue, lo, hi); return queue; - } + } - private void keys(Node x, Queue queue, Key lo, Key hi) { - if (x == null) return; - int cmplo = lo.compareTo(x.key); - int cmphi = hi.compareTo(x.key); - if (cmplo < 0) keys(x.left, queue, lo, hi); - if (cmplo <= 0 && cmphi >= 0) queue.enqueue(x.key); - if (cmphi > 0) keys(x.right, queue, lo, hi); - } + private void keys(Node x, Queue queue, Key lo, Key hi) { + if (x == null) return; + int cmplo = lo.compareTo(x.key); + int cmphi = hi.compareTo(x.key); + if (cmplo < 0) keys(x.left, queue, lo, hi); + if (cmplo <= 0 && cmphi >= 0) queue.enqueue(x.key); + if (cmphi > 0) keys(x.right, queue, lo, hi); + } /** * Returns the number of keys in the symbol table in the given range. * * @param lo minimum endpoint * @param hi maximum endpoint - * @return the number of keys in the symbol table between {@code lo} + * @return the number of keys in the symbol table between {@code lo} * (inclusive) and {@code hi} (inclusive) * @throws IllegalArgumentException if either {@code lo} or {@code hi} * is {@code null} @@ -495,13 +505,13 @@ private boolean isBST() { // is the tree rooted at x a BST with all keys strictly between min and max // (if min or max is null, treat as empty constraint) - // Credit: Bob Dondero's elegant solution + // Credit: elegant solution due to Bob Dondero private boolean isBST(Node x, Key min, Key max) { if (x == null) return true; if (min != null && x.key.compareTo(min) <= 0) return false; if (max != null && x.key.compareTo(max) >= 0) return false; return isBST(x.left, min, x.key) && isBST(x.right, x.key, max); - } + } // are the size fields correct? private boolean isSizeConsistent() { return isSizeConsistent(root); } @@ -509,7 +519,7 @@ private boolean isSizeConsistent(Node x) { if (x == null) return true; if (x.size != size(x.left) + size(x.right) + 1) return false; return isSizeConsistent(x.left) && isSizeConsistent(x.right); - } + } // check that ranks are consistent private boolean isRankConsistent() { @@ -526,7 +536,7 @@ private boolean isRankConsistent() { * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { BST st = new BST(); for (int i = 0; !StdIn.isEmpty(); i++) { String key = StdIn.readString(); @@ -544,7 +554,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BTree.java b/src/main/java/edu/princeton/cs/algs4/BTree.java index b5661f558..9440bb7ca 100644 --- a/src/main/java/edu/princeton/cs/algs4/BTree.java +++ b/src/main/java/edu/princeton/cs/algs4/BTree.java @@ -67,7 +67,7 @@ private Node(int k) { // external nodes: only use key and value private static class Entry { private Comparable key; - private final Object val; + private Object val; private Node next; // helper field to iterate over array entries public Entry(Comparable key, Object val, Node next) { this.key = key; @@ -82,7 +82,7 @@ public Entry(Comparable key, Object val, Node next) { public BTree() { root = new Node(0); } - + /** * Returns true if this symbol table is empty. * @return {@code true} if this symbol table is empty; {@code false} otherwise @@ -154,7 +154,7 @@ private Value search(Node x, Key key, int ht) { */ public void put(Key key, Value val) { if (key == null) throw new IllegalArgumentException("argument key to put() is null"); - Node u = insert(root, key, val, height); + Node u = insert(root, key, val, height); n++; if (u == null) return; @@ -184,6 +184,7 @@ private Node insert(Node h, Key key, Value val, int ht) { Node u = insert(h.children[j++].next, key, val, ht-1); if (u == null) return null; t.key = u.children[0].key; + t.val = null; t.next = u; break; } @@ -203,8 +204,8 @@ private Node split(Node h) { Node t = new Node(M/2); h.m = M/2; for (int j = 0; j < M/2; j++) - t.children[j] = h.children[M/2+j]; - return t; + t.children[j] = h.children[M/2+j]; + return t; } /** @@ -289,7 +290,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Bag.java b/src/main/java/edu/princeton/cs/algs4/Bag.java index a3d06edc8..6b3488e5c 100644 --- a/src/main/java/edu/princeton/cs/algs4/Bag.java +++ b/src/main/java/edu/princeton/cs/algs4/Bag.java @@ -5,7 +5,7 @@ * * A generic bag or multiset, implemented using a singly linked list. * - * % more tobe.txt + * % more tobe.txt * to be or not to - be - - that - - - is * * % java Bag < tobe.txt @@ -33,8 +33,8 @@ import java.util.NoSuchElementException; /** - * The {@code Bag} class represents a bag (or multiset) of - * generic items. It supports insertion and iterating over the + * The {@code Bag} class represents a bag (or multiset) of + * generic items. It supports insertion and iterating over the * items in arbitrary order. *

* This implementation uses a singly linked list with a static nested class Node. @@ -50,7 +50,7 @@ * @author Robert Sedgewick * @author Kevin Wayne * - * @param the generic type of an item in this bag + * @param the generic type of each item in this bag */ public class Bag implements Iterable { private Node first; // beginning of bag @@ -109,24 +109,24 @@ public void add(Item item) { * @return an iterator that iterates over the items in this bag in arbitrary order */ public Iterator iterator() { - return new ListIterator(first); + return new LinkedIterator(first); } - // an iterator, doesn't implement remove() since it's optional - private class ListIterator implements Iterator { + private class LinkedIterator implements Iterator { private Node current; - public ListIterator(Node first) { + public LinkedIterator(Node first) { current = first; } - public boolean hasNext() { return current != null; } - public void remove() { throw new UnsupportedOperationException(); } + public boolean hasNext() { + return current != null; + } public Item next() { if (!hasNext()) throw new NoSuchElementException(); Item item = current.item; - current = current.next; + current = current.next; return item; } } @@ -152,7 +152,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BellmanFordSP.java b/src/main/java/edu/princeton/cs/algs4/BellmanFordSP.java index 24ce4704c..8a8133b0d 100644 --- a/src/main/java/edu/princeton/cs/algs4/BellmanFordSP.java +++ b/src/main/java/edu/princeton/cs/algs4/BellmanFordSP.java @@ -4,21 +4,23 @@ * Dependencies: EdgeWeightedDigraph.java DirectedEdge.java Queue.java * EdgeWeightedDirectedCycle.java * Data files: https://algs4.cs.princeton.edu/44sp/tinyEWDn.txt - * https://algs4.cs.princeton.edu/44sp/mediumEWDnc.txt + * https://algs4.cs.princeton.edu/44sp/tinyEWDnc.txt + * https://algs4.cs.princeton.edu/44sp/mediumEWD.txt + * https://algs4.cs.princeton.edu/44sp/largeEWD.txt * * Bellman-Ford shortest path algorithm. Computes the shortest path tree in * edge-weighted digraph G from vertex s, or finds a negative cost cycle * reachable from s. * * % java BellmanFordSP tinyEWDn.txt 0 - * 0 to 0 ( 0.00) + * 0 to 0 ( 0.00) * 0 to 1 ( 0.93) 0->2 0.26 2->7 0.34 7->3 0.39 3->6 0.52 6->4 -1.25 4->5 0.35 5->1 0.32 - * 0 to 2 ( 0.26) 0->2 0.26 - * 0 to 3 ( 0.99) 0->2 0.26 2->7 0.34 7->3 0.39 - * 0 to 4 ( 0.26) 0->2 0.26 2->7 0.34 7->3 0.39 3->6 0.52 6->4 -1.25 + * 0 to 2 ( 0.26) 0->2 0.26 + * 0 to 3 ( 0.99) 0->2 0.26 2->7 0.34 7->3 0.39 + * 0 to 4 ( 0.26) 0->2 0.26 2->7 0.34 7->3 0.39 3->6 0.52 6->4 -1.25 * 0 to 5 ( 0.61) 0->2 0.26 2->7 0.34 7->3 0.39 3->6 0.52 6->4 -1.25 4->5 0.35 - * 0 to 6 ( 1.51) 0->2 0.26 2->7 0.34 7->3 0.39 3->6 0.52 - * 0 to 7 ( 0.60) 0->2 0.26 2->7 0.34 + * 0 to 6 ( 1.51) 0->2 0.26 2->7 0.34 7->3 0.39 3->6 0.52 + * 0 to 7 ( 0.60) 0->2 0.26 2->7 0.34 * * % java BellmanFordSP tinyEWDnc.txt 0 * 4->5 0.35 @@ -32,28 +34,39 @@ /** * The {@code BellmanFordSP} class represents a data type for solving the * single-source shortest paths problem in edge-weighted digraphs with - * no negative cycles. + * no negative cycles. * The edge weights can be positive, negative, or zero. * This class finds either a shortest path from the source vertex s * to every other vertex or a negative cycle reachable from the source vertex. *

- * This implementation uses the Bellman-Ford-Moore algorithm. - * The constructor takes time proportional to V (V + E) - * in the worst case, where V is the number of vertices and E - * is the number of edges. - * Each call to {@code distTo(int)} and {@code hasPathTo(int)}, - * {@code hasNegativeCycle} takes constant time; - * each call to {@code pathTo(int)} and {@code negativeCycle()} - * takes time proportional to length of the path returned. + * This implementation uses a queue-based implementation of + * the Bellman-Ford-Moore algorithm. + * The constructor takes Θ(E V) time + * in the worst case, where V is the number of vertices and + * E is the number of edges. In practice, it performs much better. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the + * edge-weighted digraph). *

- * For additional documentation, - * see Section 4.4 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * This correctly computes shortest paths if all arithmetic performed is + * without floating-point rounding error or arithmetic overflow. + * This is the case if all edge weights are integers and if none of the + * intermediate results exceeds 252. Since all intermediate + * results are sums of edge weights, they are bounded by V C, + * where V is the number of vertices and C is the maximum + * absolute value of any edge weight. + *

+ * For additional documentation, + * see Section 4.4 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public class BellmanFordSP { + // for floating-point precision issues + private static final double EPSILON = 1E-14; + private double[] distTo; // distTo[v] = distance of shortest s->v path private DirectedEdge[] edgeTo; // edgeTo[v] = last edge on shortest s->v path private boolean[] onQueue; // onQueue[v] = is v currently on the queue? @@ -93,7 +106,7 @@ public BellmanFordSP(EdgeWeightedDigraph G, int s) { private void relax(EdgeWeightedDigraph G, int v) { for (DirectedEdge e : G.adj(v)) { int w = e.to(); - if (distTo[w] > distTo[v] + e.weight()) { + if (distTo[w] > distTo[v] + e.weight() + EPSILON) { distTo[w] = distTo[v] + e.weight(); edgeTo[w] = e; if (!onQueue[w]) { @@ -101,7 +114,7 @@ private void relax(EdgeWeightedDigraph G, int v) { onQueue[w] = true; } } - if (cost++ % G.V() == 0) { + if (++cost % G.V() == 0) { findNegativeCycle(); if (hasNegativeCycle()) return; // found a negative cycle } @@ -120,7 +133,7 @@ public boolean hasNegativeCycle() { /** * Returns a negative cycle reachable from the source vertex {@code s}, or {@code null} * if there is no such cycle. - * @return a negative cycle reachable from the soruce vertex {@code s} + * @return a negative cycle reachable from the source vertex {@code s} * as an iterable of edges, and {@code null} if there is no such cycle */ public Iterable negativeCycle() { @@ -188,9 +201,9 @@ public Iterable pathTo(int v) { return path; } - // check optimality conditions: either - // (i) there exists a negative cycle reacheable from s - // or + // check optimality conditions: either + // (i) there exists a negative cycle reachable from s + // or // (ii) for all edges e = v->w: distTo[w] <= distTo[v] + e.weight() // (ii') for all edges e = v->w on the SPT: distTo[w] == distTo[v] + e.weight() private boolean check(EdgeWeightedDigraph G, int s) { @@ -298,7 +311,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BinaryDump.java b/src/main/java/edu/princeton/cs/algs4/BinaryDump.java index c5e87ff79..a05d8caa2 100644 --- a/src/main/java/edu/princeton/cs/algs4/BinaryDump.java +++ b/src/main/java/edu/princeton/cs/algs4/BinaryDump.java @@ -1,12 +1,12 @@ /****************************************************************************** * Compilation: javac BinaryDump.java - * Execution: java BinaryDump N < file + * Execution: java BinaryDump n < file * Dependencies: BinaryStdIn.java - * Data file: http://introcs.cs.princeton.edu/stdlib/abra.txt - * - * Reads in a binary file and writes out the bits, N per line. + * Data file: https://introcs.cs.princeton.edu/stdlib/abra.txt * - * % more abra.txt + * Reads in a binary file and writes out the bits, n per line. + * + * % more abra.txt * ABRACADABRA! * * % java BinaryDump 16 < abra.txt @@ -30,7 +30,7 @@ * {@code od} (octal dump) and {@code hexdump} (hexadecimal dump). *

* For additional documentation, - * see Section 5.5 of + * see Section 5.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. *

* See also {@link HexDump} and {@link PictureDump}. @@ -74,7 +74,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BinaryIn.java b/src/main/java/edu/princeton/cs/algs4/BinaryIn.java index 21aa07b0a..b8eb4e13e 100644 --- a/src/main/java/edu/princeton/cs/algs4/BinaryIn.java +++ b/src/main/java/edu/princeton/cs/algs4/BinaryIn.java @@ -1,11 +1,11 @@ /****************************************************************************** * Compilation: javac BinaryIn.java * Execution: java BinaryIn input output - * Dependencies: none - * + * Dependencies: none + * * This library is for reading binary data from an input stream. * - * % java BinaryIn https://introcs.cs.princeton.edu/cover.jpg output.jpg + * % java BinaryIn https://introcs.cs.princeton.edu/java/cover.png output.png * ******************************************************************************/ @@ -17,13 +17,17 @@ import java.io.IOException; import java.io.InputStream; import java.net.Socket; + +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; + import java.util.NoSuchElementException; /** - * Binary input. This class provides methods for reading - * in bits from a binary input stream, either + * The BinaryIn data type provides methods for reading + * in bits from a binary input stream. It can process the bits * one bit at a time (as a {@code boolean}), * 8 bits at a time (as a {@code byte} or {@code char}), * 16 bits at a time (as a {@code short}), @@ -33,7 +37,7 @@ * The binary input stream can be from standard input, a filename, * a URL name, a Socket, or an InputStream. *

- * All primitive types are assumed to be represented using their + * All primitive types are assumed to be represented using their * standard Java representations, in big-endian (most significant * byte first) order. *

@@ -80,7 +84,7 @@ public BinaryIn(Socket socket) { fillBuffer(); } catch (IOException ioe) { - System.err.println("Could not open " + socket); + System.err.println("could not read socket: " + socket); } } @@ -97,7 +101,7 @@ public BinaryIn(URL url) { fillBuffer(); } catch (IOException ioe) { - System.err.println("Could not open " + url); + System.err.println("could not open URL: '" + url + "'"); } } @@ -123,7 +127,9 @@ public BinaryIn(String name) { // or URL from web if (url == null) { - url = new URL(name); + URI uri = new URI(name); + if (uri.isAbsolute()) url = uri.toURL(); + else throw new IllegalArgumentException("could not read: '" + name+ "'"); } URLConnection site = url.openConnection(); @@ -131,8 +137,8 @@ public BinaryIn(String name) { in = new BufferedInputStream(is); fillBuffer(); } - catch (IOException ioe) { - System.err.println("Could not open " + name); + catch (IOException | URISyntaxException e) { + System.err.println("could not open: '" + name + "'"); } } @@ -213,10 +219,11 @@ public char readChar() { /** - * Reads the next r bits from this binary input stream and return as an r-bit character. + * Reads the next r bits from this binary input stream and return + * as an r-bit character. * * @param r number of bits to read - * @return the next {@code r} bits of data from this binary input streamt as a {@code char} + * @return the next {@code r} bits of data from this binary input stream as a {@code char} * @throws NoSuchElementException if there are fewer than {@code r} bits available * @throws IllegalArgumentException unless {@code 1 <= r <= 16} */ @@ -237,7 +244,7 @@ public char readChar(int r) { /** - * Reads the remaining bytes of data from this binary input stream and return as a string. + * Reads the remaining bytes of data from this binary input stream and return as a string. * * @return the remaining bytes of data from this binary input stream as a {@code String} * @throws NoSuchElementException if this binary input stream is empty or if the number of bits @@ -288,7 +295,8 @@ public int readInt() { } /** - * Reads the next r bits from this binary input stream return as an r-bit int. + * Reads the next r bits from this binary input stream return + * as an r-bit int. * * @param r number of bits to read * @return the next {@code r} bits of data from this binary input stream as a {@code int} @@ -357,7 +365,7 @@ public byte readByte() { char c = readChar(); return (byte) (c & 0xff); } - + /** * Unit tests the {@code BinaryIn} data type. * Reads the name of a file or URL (first command-line argument) @@ -379,7 +387,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BinaryInsertion.java b/src/main/java/edu/princeton/cs/algs4/BinaryInsertion.java index 72cf749af..e43a40be1 100644 --- a/src/main/java/edu/princeton/cs/algs4/BinaryInsertion.java +++ b/src/main/java/edu/princeton/cs/algs4/BinaryInsertion.java @@ -4,8 +4,8 @@ * Dependencies: StdOut.java StdIn.java * Data files: https://algs4.cs.princeton.edu/21elementary/tiny.txt * https://algs4.cs.princeton.edu/21elementary/words3.txt - * - * Sorts a sequence of strings from standard input using + * + * Sorts a sequence of strings from standard input using * binary insertion sort with half exchanges. * * % more tiny.txt @@ -28,16 +28,20 @@ * The {@code BinaryInsertion} class provides a static method for sorting an * array using an optimized binary insertion sort with half exchanges. *

- * This implementation makes ~ n lg n compares for any array of length n. - * However, in the worst case, the running time is quadratic because the - * number of array accesses can be proportional to n^2 (e.g, if the array - * is reverse sorted). As such, it is not suitable for sorting large - * arrays (unless the number of inversions is small). + * In the worst case, this implementation makes + * ~ n log2n compares to sort an array of length + * n. However, in the worst case, the running time is + * Θ(n2) because the number of array accesses + * can be quadratic. + * As such, it is not suitable for sorting large arrays + * (unless the number of inversions is small). *

- * The sorting algorithm is stable and uses O(1) extra memory. + * This sorting algorithm is stable. + * It uses Θ(1) extra memory (not including the input array). *

- * For additional documentation, see Section 2.1 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 2.1 + * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Ivan Pesin * @author Robert Sedgewick @@ -60,12 +64,12 @@ public static void sort(Comparable[] a) { Comparable v = a[i]; int lo = 0, hi = i; while (lo < hi) { - int mid = lo + (hi - lo) / 2; + int mid = lo + (hi - lo) / 2; if (less(v, a[mid])) hi = mid; else lo = mid + 1; } - // insetion sort with "half exchanges" + // insertion sort with "half exchanges" // (insert a[i] at index j and shift a[j], ..., a[i-1] to right) for (int j = i; j > lo; --j) a[j] = a[j-1]; @@ -79,7 +83,7 @@ public static void sort(Comparable[] a) { /*************************************************************************** * Helper sorting function. ***************************************************************************/ - + // is v < w ? private static boolean less(Comparable v, Comparable w) { return v.compareTo(w) < 0; @@ -120,7 +124,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BinaryOut.java b/src/main/java/edu/princeton/cs/algs4/BinaryOut.java index 8dc1dccb1..8db5d3b3f 100644 --- a/src/main/java/edu/princeton/cs/algs4/BinaryOut.java +++ b/src/main/java/edu/princeton/cs/algs4/BinaryOut.java @@ -21,9 +21,9 @@ import java.net.Socket; /** - * Binary output. This class provides methods for converting - * primtive type variables ({@code boolean}, {@code byte}, {@code char}, - * {@code int}, {@code long}, {@code float}, and {@code double}) + * The BinaryOut data type provides a basic capability for + * converting primitive type variables ({@code boolean}, {@code byte}, + * {@code char}, {@code int}, {@code long}, {@code float}, and {@code double}) * to sequences of bits and writing them to an output stream. * The output stream can be standard output, a file, an OutputStream or a Socket. * Uses big-endian (most-significant byte first). @@ -60,29 +60,46 @@ public BinaryOut(OutputStream os) { /** * Initializes a binary output stream from a file. - * @param filename the name of the file + * @param filename the name of the file + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if {@code filename} is the empty string + * @throws IllegalArgumentException if cannot write the file {@code filename} */ public BinaryOut(String filename) { + if (filename == null) { + throw new IllegalArgumentException("filename argument is null"); + } + + if (filename.length() == 0) { + throw new IllegalArgumentException("filename argument is the empty string"); + } + try { OutputStream os = new FileOutputStream(filename); out = new BufferedOutputStream(os); } catch (IOException e) { - e.printStackTrace(); + throw new IllegalArgumentException("could not create file '" + filename + "' for writing", e); } } /** * Initializes a binary output stream from a socket. * @param socket the socket + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if cannot create output stream from socket */ public BinaryOut(Socket socket) { + if (socket == null) { + throw new IllegalArgumentException("socket argument is null"); + } + try { OutputStream os = socket.getOutputStream(); out = new BufferedOutputStream(os); } catch (IOException e) { - e.printStackTrace(); + throw new IllegalArgumentException("could not create output stream from socket", e); } } @@ -99,7 +116,7 @@ private void writeBit(boolean x) { // if buffer is full (8 bits), write out as a single byte n++; if (n == 8) clearBuffer(); - } + } /** * Writes the 8-bit byte to the binary output stream. @@ -175,7 +192,7 @@ public void close() { */ public void write(boolean x) { writeBit(x); - } + } /** * Writes the 8-bit byte to the binary output stream. @@ -197,7 +214,7 @@ public void write(int x) { } /** - * Writes the r-bit int to the binary output stream. + * Writes the r-bit int to the binary output stream. * * @param x the {@code int} to write * @param r the number of relevant bits in the char @@ -262,15 +279,15 @@ public void write(short x) { * Writes the 8-bit char to the binary output stream. * * @param x the {@code char} to write - * @throws IllegalArgumentException unless {@code x} is betwen 0 and 255 + * @throws IllegalArgumentException unless {@code x} is between 0 and 255 */ public void write(char x) { - if (x < 0 || x >= 256) throw new IllegalArgumentException("Illegal 8-bit char = " + x); + if (x >= 256) throw new IllegalArgumentException("Illegal 8-bit char = " + x); writeByte(x); } /** - * Writes the r-bit char to the binary output stream. + * Writes the r-bit char to the binary output stream. * * @param x the {@code char} to write * @param r the number of relevant bits in the char @@ -304,9 +321,9 @@ public void write(String s) { /** - * Writes the string of r-bit characters to the binary output stream. + * Writes the string of r-bit characters to the binary output stream. * @param s the {@code String} to write - * @param r the number of relevants bits in each character + * @param r the number of relevant bits in each character * @throws IllegalArgumentException unless r is between 1 and 16 * @throws IllegalArgumentException if any character in the string is not * between 0 and 2r - 1 @@ -341,7 +358,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BinarySearch.java b/src/main/java/edu/princeton/cs/algs4/BinarySearch.java index 86532460d..d3d4a7441 100644 --- a/src/main/java/edu/princeton/cs/algs4/BinarySearch.java +++ b/src/main/java/edu/princeton/cs/algs4/BinarySearch.java @@ -1,18 +1,18 @@ /****************************************************************************** * Compilation: javac BinarySearch.java - * Execution: java BinarySearch whitelist.txt < input.txt + * Execution: java BinarySearch allowlist.txt < input.txt * Dependencies: In.java StdIn.java StdOut.java - * Data files: https://algs4.cs.princeton.edu/11model/tinyW.txt - * https://algs4.cs.princeton.edu/11model/tinyT.txt - * https://algs4.cs.princeton.edu/11model/largeW.txt - * https://algs4.cs.princeton.edu/11model/largeT.txt + * Data files: https://algs4.cs.princeton.edu/11model/tinyAllowlist.txt + * https://algs4.cs.princeton.edu/11model/tinyText.txt + * https://algs4.cs.princeton.edu/11model/largeAllowlist.txt + * https://algs4.cs.princeton.edu/11model/largeText.txt * - * % java BinarySearch tinyW.txt < tinyT.txt + * % java BinarySearch tinyAllowlist.txt < tinyText.txt * 50 * 99 * 13 * - * % java BinarySearch largeW.txt < largeT.txt | more + * % java BinarySearch largeAllowlist.txt < largeText.txt | more * 499569 * 984875 * 295754 @@ -20,7 +20,7 @@ * 140925 * 161828 * [367,966 total values] - * + * ******************************************************************************/ package edu.princeton.cs.algs4; @@ -82,7 +82,7 @@ public static int rank(int key, int[] a) { } /** - * Reads in a sequence of integers from the whitelist file, specified as + * Reads in a sequence of integers from the allowlist file, specified as * a command-line argument; reads in integers from standard input; * prints to standard output those integers that do not appear in the file. * @@ -92,22 +92,22 @@ public static void main(String[] args) { // read the integers from a file In in = new In(args[0]); - int[] whitelist = in.readAllInts(); + int[] allowlist = in.readAllInts(); // sort the array - Arrays.sort(whitelist); + Arrays.sort(allowlist); - // read integer key from standard input; print if not in whitelist + // read integer key from standard input; print if not in allowlist while (!StdIn.isEmpty()) { int key = StdIn.readInt(); - if (BinarySearch.indexOf(whitelist, key) == -1) + if (BinarySearch.indexOf(allowlist, key) == -1) StdOut.println(key); } } } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BinarySearchST.java b/src/main/java/edu/princeton/cs/algs4/BinarySearchST.java index a26fe53a1..3429a3165 100644 --- a/src/main/java/edu/princeton/cs/algs4/BinarySearchST.java +++ b/src/main/java/edu/princeton/cs/algs4/BinarySearchST.java @@ -2,13 +2,13 @@ * Compilation: javac BinarySearchST.java * Execution: java BinarySearchST * Dependencies: StdIn.java StdOut.java - * Data files: https://algs4.cs.princeton.edu/31elementary/tinyST.txt - * + * Data files: https://algs4.cs.princeton.edu/31elementary/tinyST.txt + * * Symbol table implementation with binary search in an ordered array. * * % more tinyST.txt * S E A R C H E X A M P L E - * + * * % java BinarySearchST < tinyST.txt * A 8 * C 4 @@ -43,21 +43,26 @@ * value associated with a key to {@code null} is equivalent to deleting the key * from the symbol table. *

- * This implementation uses a sorted array. It requires that + * It requires that * the key type implements the {@code Comparable} interface and calls the * {@code compareTo()} and method to compare two keys. It does not call either * {@code equals()} or {@code hashCode()}. - * The put and remove operations each take linear time in - * the worst case; the contains, ceiling, floor, - * and rank operations take logarithmic time; the size, - * is-empty, minimum, maximum, and select - * operations take constant time. Construction takes constant time. *

- * For additional documentation, see Section 3.1 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. - * For other implementations, see {@link ST}, {@link BST}, - * {@link SequentialSearchST}, {@link RedBlackBST}, + * This implementation uses a sorted array. + * The put and remove operations take Θ(n) + * time in the worst case. + * The contains, ceiling, floor, + * and rank operations take Θ(log n) time in the worst + * case. + * The size, is-empty, minimum, maximum, + * and select operations take Θ(1) time. + * Construction takes Θ(1) time. + *

+ * For alternative implementations of the symbol table API, + * see {@link ST}, {@link BST}, {@link SequentialSearchST}, {@link RedBlackBST}, * {@link SeparateChainingHashST}, and {@link LinearProbingHashST}, + * For additional documentation, + * see Section 3.1 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. */ public class BinarySearchST, Value> { @@ -77,10 +82,10 @@ public BinarySearchST() { * Initializes an empty symbol table with the specified initial capacity. * @param capacity the maximum capacity */ - public BinarySearchST(int capacity) { - keys = (Key[]) new Comparable[capacity]; - vals = (Value[]) new Object[capacity]; - } + public BinarySearchST(int capacity) { + keys = (Key[]) new Comparable[capacity]; + vals = (Value[]) new Object[capacity]; + } // resize the underlying arrays private void resize(int capacity) { @@ -137,12 +142,12 @@ public boolean contains(Key key) { * @throws IllegalArgumentException if {@code key} is {@code null} */ public Value get(Key key) { - if (key == null) throw new IllegalArgumentException("argument to get() is null"); + if (key == null) throw new IllegalArgumentException("argument to get() is null"); if (isEmpty()) return null; - int i = rank(key); + int i = rank(key); if (i < n && keys[i].compareTo(key) == 0) return vals[i]; return null; - } + } /** * Returns the number of keys in this symbol table strictly less than {@code key}. @@ -152,23 +157,23 @@ public Value get(Key key) { * @throws IllegalArgumentException if {@code key} is {@code null} */ public int rank(Key key) { - if (key == null) throw new IllegalArgumentException("argument to rank() is null"); + if (key == null) throw new IllegalArgumentException("argument to rank() is null"); - int lo = 0, hi = n-1; - while (lo <= hi) { - int mid = lo + (hi - lo) / 2; + int lo = 0, hi = n-1; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; int cmp = key.compareTo(keys[mid]); - if (cmp < 0) hi = mid - 1; - else if (cmp > 0) lo = mid + 1; - else return mid; - } + if (cmp < 0) hi = mid - 1; + else if (cmp > 0) lo = mid + 1; + else return mid; + } return lo; - } + } /** - * Inserts the specified key-value pair into the symbol table, overwriting the old + * Inserts the specified key-value pair into the symbol table, overwriting the old * value with the new value if the symbol table already contains the specified key. * Deletes the specified key (and its associated value) from this symbol table * if the specified value is {@code null}. @@ -178,7 +183,7 @@ public int rank(Key key) { * @throws IllegalArgumentException if {@code key} is {@code null} */ public void put(Key key, Value val) { - if (key == null) throw new IllegalArgumentException("first argument to put() is null"); + if (key == null) throw new IllegalArgumentException("first argument to put() is null"); if (val == null) { delete(key); @@ -205,7 +210,7 @@ public void put(Key key, Value val) { n++; assert check(); - } + } /** * Removes the specified key and associated value from this symbol table @@ -215,7 +220,7 @@ public void put(Key key, Value val) { * @throws IllegalArgumentException if {@code key} is {@code null} */ public void delete(Key key) { - if (key == null) throw new IllegalArgumentException("argument to delete() is null"); + if (key == null) throw new IllegalArgumentException("argument to delete() is null"); if (isEmpty()) return; // compute rank @@ -239,7 +244,7 @@ public void delete(Key key) { if (n > 0 && n == keys.length/4) resize(keys.length/2); assert check(); - } + } /** * Removes the smallest key and associated value from this symbol table. @@ -274,7 +279,7 @@ public void deleteMax() { */ public Key min() { if (isEmpty()) throw new NoSuchElementException("called min() with empty symbol table"); - return keys[0]; + return keys[0]; } /** @@ -312,10 +317,10 @@ public Key select(int k) { * @throws IllegalArgumentException if {@code key} is {@code null} */ public Key floor(Key key) { - if (key == null) throw new IllegalArgumentException("argument to floor() is null"); + if (key == null) throw new IllegalArgumentException("argument to floor() is null"); int i = rank(key); if (i < n && key.compareTo(keys[i]) == 0) return keys[i]; - if (i == 0) return null; + if (i == 0) throw new NoSuchElementException("argument to floor() is too small"); else return keys[i-1]; } @@ -328,9 +333,9 @@ public Key floor(Key key) { * @throws IllegalArgumentException if {@code key} is {@code null} */ public Key ceiling(Key key) { - if (key == null) throw new IllegalArgumentException("argument to ceiling() is null"); + if (key == null) throw new IllegalArgumentException("argument to ceiling() is null"); int i = rank(key); - if (i == n) return null; + if (i == n) throw new NoSuchElementException("argument to ceiling() is too large"); else return keys[i]; } @@ -339,14 +344,14 @@ public Key ceiling(Key key) { * * @param lo minimum endpoint * @param hi maximum endpoint - * @return the number of keys in this symbol table between {@code lo} + * @return the number of keys in this symbol table between {@code lo} * (inclusive) and {@code hi} (inclusive) * @throws IllegalArgumentException if either {@code lo} or {@code hi} * is {@code null} */ public int size(Key lo, Key hi) { - if (lo == null) throw new IllegalArgumentException("first argument to size() is null"); - if (hi == null) throw new IllegalArgumentException("second argument to size() is null"); + if (lo == null) throw new IllegalArgumentException("first argument to size() is null"); + if (hi == null) throw new IllegalArgumentException("second argument to size() is null"); if (lo.compareTo(hi) > 0) return 0; if (contains(hi)) return rank(hi) - rank(lo) + 1; @@ -370,21 +375,21 @@ public Iterable keys() { * * @param lo minimum endpoint * @param hi maximum endpoint - * @return all keys in this symbol table between {@code lo} + * @return all keys in this symbol table between {@code lo} * (inclusive) and {@code hi} (inclusive) * @throws IllegalArgumentException if either {@code lo} or {@code hi} * is {@code null} */ public Iterable keys(Key lo, Key hi) { - if (lo == null) throw new IllegalArgumentException("first argument to keys() is null"); - if (hi == null) throw new IllegalArgumentException("second argument to keys() is null"); + if (lo == null) throw new IllegalArgumentException("first argument to keys() is null"); + if (hi == null) throw new IllegalArgumentException("second argument to keys() is null"); - Queue queue = new Queue(); + Queue queue = new Queue(); if (lo.compareTo(hi) > 0) return queue; - for (int i = rank(lo); i < rank(hi); i++) + for (int i = rank(lo); i < rank(hi); i++) queue.enqueue(keys[i]); if (contains(hi)) queue.enqueue(keys[rank(hi)]); - return queue; + return queue; } @@ -418,7 +423,7 @@ private boolean rankCheck() { * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { BinarySearchST st = new BinarySearchST(); for (int i = 0; !StdIn.isEmpty(); i++) { String key = StdIn.readString(); @@ -430,7 +435,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BinaryStdIn.java b/src/main/java/edu/princeton/cs/algs4/BinaryStdIn.java index 46bc2f796..5127e02f1 100644 --- a/src/main/java/edu/princeton/cs/algs4/BinaryStdIn.java +++ b/src/main/java/edu/princeton/cs/algs4/BinaryStdIn.java @@ -1,8 +1,8 @@ /****************************************************************************** * Compilation: javac BinaryStdIn.java * Execution: java BinaryStdIn < input > output - * Dependencies: none - * + * Dependencies: none + * * Supports reading binary data from standard input. * * % java BinaryStdIn < input.jpg > output.jpg @@ -17,14 +17,15 @@ import java.util.NoSuchElementException; /** - * Binary standard input. This class provides methods for reading - * in bits from standard input, either one bit at a time (as a {@code boolean}), + * The BinaryStdIn class provides static methods for reading + * in bits from standard input. It can process the bits + * one bit at a time (as a {@code boolean}), * 8 bits at a time (as a {@code byte} or {@code char}), * 16 bits at a time (as a {@code short}), 32 bits at a time * (as an {@code int} or {@code float}), or 64 bits at a time (as a * {@code double} or {@code long}). *

- * All primitive types are assumed to be represented using their + * All primitive types are assumed to be represented using their * standard Java representations, in big-endian (most significant * byte first) order. *

@@ -136,7 +137,7 @@ public static char readChar() { } /** - * Reads the next r bits from standard input and return as an r-bit character. + * Reads the next r bits from standard input and return as an r-bit character. * * @param r number of bits to read. * @return the next r bits of data from standard input as a {@code char} @@ -159,7 +160,7 @@ public static char readChar(int r) { } /** - * Reads the remaining bytes of data from standard input and return as a string. + * Reads the remaining bytes of data from standard input and return as a string. * * @return the remaining bytes of data from standard input as a {@code String} * @throws NoSuchElementException if standard input is empty or if the number of bits @@ -210,7 +211,7 @@ public static int readInt() { } /** - * Reads the next r bits from standard input and return as an r-bit int. + * Reads the next r bits from standard input and return as an r-bit int. * * @param r number of bits to read. * @return the next r bits of data from standard input as a {@code int} @@ -280,7 +281,7 @@ public static byte readByte() { char c = readChar(); return (byte) (c & 0xff); } - + /** * Test client. Reads in a binary input file from standard input and writes * it to standard output. @@ -299,7 +300,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BinaryStdOut.java b/src/main/java/edu/princeton/cs/algs4/BinaryStdOut.java index 058087d64..943e11db3 100644 --- a/src/main/java/edu/princeton/cs/algs4/BinaryStdOut.java +++ b/src/main/java/edu/princeton/cs/algs4/BinaryStdOut.java @@ -17,8 +17,8 @@ import java.io.IOException; /** - * Binary standard output. This class provides methods for converting - * primtive type variables ({@code boolean}, {@code byte}, {@code char}, + * The BinaryStdOut class provides static methods for converting + * primitive type variables ({@code boolean}, {@code byte}, {@code char}, * {@code int}, {@code long}, {@code float}, and {@code double}) * to sequences of bits and writing them to standard output. * Uses big-endian (most-significant byte first). @@ -26,7 +26,7 @@ * The client must {@code flush()} the output stream when finished writing bits. *

* The client should not intermix calls to {@code BinaryStdOut} with calls - * to {@code StdOut} or {@code System.out}; otherwise unexpected behavior + * to {@code StdOut} or {@code System.out}; otherwise unexpected behavior * will result. * * @author Robert Sedgewick @@ -62,7 +62,7 @@ private static void writeBit(boolean bit) { // if buffer is full (8 bits), write out as a single byte n++; if (n == 8) clearBuffer(); - } + } /** * Writes the 8-bit byte to standard output. @@ -142,7 +142,7 @@ public static void close() { */ public static void write(boolean x) { writeBit(x); - } + } /** * Writes the 8-bit byte to standard output. @@ -164,7 +164,7 @@ public static void write(int x) { } /** - * Writes the r-bit int to standard output. + * Writes the r-bit int to standard output. * @param x the {@code int} to write. * @param r the number of relevant bits in the char. * @throws IllegalArgumentException if {@code r} is not between 1 and 32. @@ -230,7 +230,7 @@ public static void write(short x) { /** * Writes the 8-bit char to standard output. * @param x the {@code char} to write. - * @throws IllegalArgumentException if {@code x} is not betwen 0 and 255. + * @throws IllegalArgumentException if {@code x} is not between 0 and 255. */ public static void write(char x) { if (x < 0 || x >= 256) throw new IllegalArgumentException("Illegal 8-bit char = " + x); @@ -238,7 +238,7 @@ public static void write(char x) { } /** - * Writes the r-bit char to standard output. + * Writes the r-bit char to standard output. * @param x the {@code char} to write. * @param r the number of relevant bits in the char. * @throws IllegalArgumentException if {@code r} is not between 1 and 16. @@ -269,9 +269,9 @@ public static void write(String s) { } /** - * Writes the string of r-bit characters to standard output. + * Writes the string of r-bit characters to standard output. * @param s the {@code String} to write. - * @param r the number of relevants bits in each character. + * @param r the number of relevant bits in each character. * @throws IllegalArgumentException if r is not between 1 and 16. * @throws IllegalArgumentException if any character in the string is not * between 0 and 2r - 1. @@ -299,7 +299,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BinomialMinPQ.java b/src/main/java/edu/princeton/cs/algs4/BinomialMinPQ.java index 661e4a4b6..06bec8b44 100644 --- a/src/main/java/edu/princeton/cs/algs4/BinomialMinPQ.java +++ b/src/main/java/edu/princeton/cs/algs4/BinomialMinPQ.java @@ -304,7 +304,7 @@ public int compare(Key key1, Key key2) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Bipartite.java b/src/main/java/edu/princeton/cs/algs4/Bipartite.java index 88cb8684d..1644ebd4f 100644 --- a/src/main/java/edu/princeton/cs/algs4/Bipartite.java +++ b/src/main/java/edu/princeton/cs/algs4/Bipartite.java @@ -1,7 +1,7 @@ /****************************************************************************** * Compilation: javac Bipartite.java * Execution: java Bipartite V E F - * Dependencies: Graph.java + * Dependencies: Graph.java * Data files: https://algs4.cs.princeton.edu/41graph/tinyG.txt * https://algs4.cs.princeton.edu/41graph/mediumG.txt * https://algs4.cs.princeton.edu/41graph/largeG.txt @@ -15,25 +15,25 @@ /** - * The {@code Bipartite} class represents a data type for - * determining whether an undirected graph is bipartite or whether - * it has an odd-length cycle. + * The {@code Bipartite} class represents a data type for + * determining whether an undirected graph is bipartite or whether + * it has an odd-length cycle. + * A graph is bipartite if and only if it has no odd-length cycle. * The isBipartite operation determines whether the graph is * bipartite. If so, the color operation determines a * bipartition; if not, the oddCycle operation determines a * cycle with an odd number of edges. *

- * This implementation uses depth-first search. - * The constructor takes time proportional to V + E - * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * Afterwards, the isBipartite and color operations - * take constant time; the oddCycle operation takes time proportional - * to the length of the cycle. + * This implementation uses depth-first search. + * The constructor takes Θ(V + E) time in + * the worst case, where V is the number of vertices and E + * is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the graph). * See {@link BipartiteX} for a nonrecursive version that uses breadth-first * search. *

- * For additional documentation, see Section 4.1 + * For additional documentation, see Section 4.1 * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -66,7 +66,7 @@ public Bipartite(Graph G) { assert check(G); } - private void dfs(Graph G, int v) { + private void dfs(Graph G, int v) { marked[v] = true; for (int w : G.adj(v)) { @@ -78,7 +78,7 @@ private void dfs(Graph G, int v) { edgeTo[w] = v; color[w] = !color[v]; dfs(G, w); - } + } // if v-w create an odd-length cycle, find it else if (color[w] == color[v]) { @@ -101,7 +101,7 @@ else if (color[w] == color[v]) { public boolean isBipartite() { return isBipartite; } - + /** * Returns the side of the bipartite that vertex {@code v} is on. * @@ -109,7 +109,7 @@ public boolean isBipartite() { * @return the side of the bipartition that vertex {@code v} is on; two vertices * are in the same side of the bipartition if and only if they have the * same color - * @throws IllegalArgumentException unless {@code 0 <= v < V} + * @throws IllegalArgumentException unless {@code 0 <= v < V} * @throws UnsupportedOperationException if this method is called when the graph * is not bipartite */ @@ -129,7 +129,7 @@ public boolean color(int v) { * otherwise */ public Iterable oddCycle() { - return cycle; + return cycle; } private boolean check(Graph G) { @@ -184,8 +184,8 @@ public static void main(String[] args) { // V2 vertices on right side, and E edges; then add F random edges Graph G = GraphGenerator.bipartite(V1, V2, E); for (int i = 0; i < F; i++) { - int v = StdRandom.uniform(V1 + V2); - int w = StdRandom.uniform(V1 + V2); + int v = StdRandom.uniformInt(V1 + V2); + int w = StdRandom.uniformInt(V1 + V2); G.addEdge(v, w); } @@ -212,7 +212,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BipartiteMatching.java b/src/main/java/edu/princeton/cs/algs4/BipartiteMatching.java index 1bf967d0b..3b99fe0c5 100644 --- a/src/main/java/edu/princeton/cs/algs4/BipartiteMatching.java +++ b/src/main/java/edu/princeton/cs/algs4/BipartiteMatching.java @@ -23,7 +23,7 @@ * A vertex cover in a graph is a subset of its vertices such that * every edge is incident to at least one vertex. A minimum vertex cover * is a vertex cover with the minimum number of vertices. - * By Konig's theorem, in any biparite + * By Konig's theorem, in any bipartite * graph, the maximum number of edges in matching equals the minimum number * of vertices in a vertex cover. * The maximum matching problem in nonbipartite graphs is @@ -34,16 +34,17 @@ * It is equivalent to reducing to the maximum-flow problem and running * the augmenting-path algorithm on the resulting flow network, but it * does so with less overhead. - * The order of growth of the running time in the worst case is - * (E + V) V, - * where E is the number of edges and V is the number - * of vertices in the graph. It uses extra space (not including the graph) - * proportional to V. + * The constructor takes O((E + V) V) + * time, where E is the number of edges and V is the + * number of vertices in the graph. + * It uses Θ(V) extra space (not including the graph). *

- * See also {@link HopcroftKarp}, which solves the problem in O(E sqrt(V)) - * using the Hopcroft-Karp algorithm and + * See also {@link HopcroftKarp}, which solves the problem in + * O(E sqrt(V)) using the Hopcroft-Karp + * algorithm and * BipartiteMatchingToMaxflow, - * which solves the problem in O(E V) time via a reduction to maxflow. + * which solves the problem in O((E + V) V) + * time via a reduction to maxflow. *

* For additional documentation, see * Section 6.5 @@ -317,7 +318,7 @@ public static void main(String[] args) { if (G.V() < 1000) StdOut.println(G); BipartiteMatching matching = new BipartiteMatching(G); - + // print maximum matching StdOut.printf("Number of edges in max matching = %d\n", matching.size()); StdOut.printf("Number of vertices in min vertex cover = %d\n", matching.size()); @@ -345,7 +346,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BipartiteX.java b/src/main/java/edu/princeton/cs/algs4/BipartiteX.java index af467abcb..c41b5f500 100644 --- a/src/main/java/edu/princeton/cs/algs4/BipartiteX.java +++ b/src/main/java/edu/princeton/cs/algs4/BipartiteX.java @@ -1,7 +1,7 @@ /****************************************************************************** * Compilation: javac BipartiteX.java * Execution: java Bipartite V E F - * Dependencies: Graph.java + * Dependencies: Graph.java * * Given a graph, find either (i) a bipartition or (ii) an odd-length cycle. * Runs in O(E + V) time. @@ -13,25 +13,25 @@ /** - * The {@code BipartiteX} class represents a data type for - * determining whether an undirected graph is bipartite or whether - * it has an odd-length cycle. + * The {@code BipartiteX} class represents a data type for + * determining whether an undirected graph is bipartite or whether + * it has an odd-length cycle. + * A graph is bipartite if and only if it has no odd-length cycle. * The isBipartite operation determines whether the graph is * bipartite. If so, the color operation determines a * bipartition; if not, the oddCycle operation determines a * cycle with an odd number of edges. *

- * This implementation uses breadth-first search and is nonrecursive. - * The constructor takes time proportional to V + E - * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * Afterwards, the isBipartite and color operations - * take constant time; the oddCycle operation takes time proportional - * to the length of the cycle. + * This implementation uses breadth-first search and is nonrecursive. + * The constructor takes Θ(V + E) time in + * in the worst case, where V is the number of vertices + * and E is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the graph). * See {@link Bipartite} for a recursive version that uses depth-first search. *

* For additional documentation, - * see Section 4.1 + * see Section 4.1 * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -67,7 +67,7 @@ public BipartiteX(Graph G) { assert check(G); } - private void bfs(Graph G, int s) { + private void bfs(Graph G, int s) { Queue q = new Queue(); color[s] = WHITE; marked[s] = true; @@ -116,7 +116,7 @@ else if (color[w] == color[v]) { public boolean isBipartite() { return isBipartite; } - + /** * Returns the side of the bipartite that vertex {@code v} is on. * @@ -124,7 +124,7 @@ public boolean isBipartite() { * @return the side of the bipartition that vertex {@code v} is on; two vertices * are in the same side of the bipartition if and only if they have the * same color - * @throws IllegalArgumentException unless {@code 0 <= v < V} + * @throws IllegalArgumentException unless {@code 0 <= v < V} * @throws UnsupportedOperationException if this method is called when the graph * is not bipartite */ @@ -145,7 +145,7 @@ public boolean color(int v) { * otherwise */ public Iterable oddCycle() { - return cycle; + return cycle; } private boolean check(Graph G) { @@ -199,8 +199,8 @@ public static void main(String[] args) { // V2 vertices on right side, and E edges; then add F random edges Graph G = GraphGenerator.bipartite(V1, V2, E); for (int i = 0; i < F; i++) { - int v = StdRandom.uniform(V1 + V2); - int w = StdRandom.uniform(V1 + V2); + int v = StdRandom.uniformInt(V1 + V2); + int w = StdRandom.uniformInt(V1 + V2); G.addEdge(v, w); } @@ -227,7 +227,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BlackFilter.java b/src/main/java/edu/princeton/cs/algs4/BlockFilter.java similarity index 66% rename from src/main/java/edu/princeton/cs/algs4/BlackFilter.java rename to src/main/java/edu/princeton/cs/algs4/BlockFilter.java index 0d349c4b5..afbdaa4b6 100644 --- a/src/main/java/edu/princeton/cs/algs4/BlackFilter.java +++ b/src/main/java/edu/princeton/cs/algs4/BlockFilter.java @@ -1,51 +1,52 @@ /****************************************************************************** - * Compilation: javac BlackFilter.java - * Execution: java BlackFilter blacklist.txt < input.txt + * Compilation: javac BlockFilter.java + * Execution: java BlockFilter blocklist.txt < input.txt * Dependencies: SET In.java StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/35applications/tinyTale.txt - * https://algs4.cs.princeton.edu/35applications/list.txt + * https://algs4.cs.princeton.edu/35applications/blocklist.txt * - * Read in a blacklist of words from a file. Then read in a list of - * words from standard input and print out all those words that + * Reads a blocklist of words from a file. Then reads a list of + * words from standard input and print all those words that * are not in the first file. - * - * % more tinyTale.txt - * it was the best of times it was the worst of times - * it was the age of wisdom it was the age of foolishness - * it was the epoch of belief it was the epoch of incredulity - * it was the season of light it was the season of darkness + * + * % more tinyTale.txt + * it was the best of times it was the worst of times + * it was the age of wisdom it was the age of foolishness + * it was the epoch of belief it was the epoch of incredulity + * it was the season of light it was the season of darkness * it was the spring of hope it was the winter of despair * - * % more list.txt - * was it the of - * - * % java BlackFilter list.txt < tinyTale.txt - * best times worst times - * age wisdom age foolishness - * epoch belief epoch incredulity - * season light season darkness - * spring hope winter despair + * % more list.txt + * was it the of + * + * % java BlockFilter list.txt < tinyTale.txt + * best times worst times + * age wisdom age foolishness + * epoch belief epoch incredulity + * season light season darkness + * spring hope winter despair * ******************************************************************************/ package edu.princeton.cs.algs4; /** - * The {@code BlackFilter} class provides a client for reading in a blacklist - * of words from a file; then, reading in a sequence of words from standard input, - * printing out each word that does not appear in the file. - * It is useful as a test client for various symbol table implementations. + * The {@code BlockFilter} class provides a client for reading in a blocklist + * of words from a file; then, reading in a sequence of words from standard input, + * printing out each word that does not appear in the file. + * It is useful as a test client for various symbol-table implementations. *

- * For additional documentation, see Section 3.5 of + * For additional documentation, + * see Section 3.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. - * + * * @author Robert Sedgewick * @author Kevin Wayne */ -public class BlackFilter { +public class BlockFilter { // Do not instantiate. - private BlackFilter() { } + private BlockFilter() { } public static void main(String[] args) { SET set = new SET(); @@ -67,7 +68,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BoruvkaMST.java b/src/main/java/edu/princeton/cs/algs4/BoruvkaMST.java index cf78a12e7..3fd940166 100644 --- a/src/main/java/edu/princeton/cs/algs4/BoruvkaMST.java +++ b/src/main/java/edu/princeton/cs/algs4/BoruvkaMST.java @@ -9,7 +9,7 @@ * * Compute a minimum spanning forest using Boruvka's algorithm. * - * % java BoruvkaMST tinyEWG.txt + * % java BoruvkaMST tinyEWG.txt * 0-2 0.26000 * 6-2 0.40000 * 5-7 0.28000 @@ -29,17 +29,24 @@ * The edge weights can be positive, zero, or negative and need not * be distinct. If the graph is not connected, it computes a minimum * spanning forest, which is the union of minimum spanning trees - * in each connected component. The {@code weight()} method returns the + * in each connected component. The {@code weight()} method returns the * weight of a minimum spanning tree and the {@code edges()} method * returns its edges. *

* This implementation uses Boruvka's algorithm and the union-find * data type. - * The constructor takes time proportional to E log V - * and extra space (not including the graph) proportional to V, - * where V is the number of vertices and E is the number of edges. - * Afterwards, the {@code weight()} method takes constant time - * and the {@code edges()} method takes time proportional to V. + * The constructor takes Θ(E log V) time in + * the worst case, where V is the number of vertices and + * E is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the + * edge-weighted graph). + *

+ * This {@code weight()} method correctly computes the weight of the MST + * if all arithmetic performed is without floating-point rounding error + * or arithmetic overflow. + * This is the case if all edge weights are non-negative integers + * and the weight of the MST does not exceed 252. *

* For additional documentation, * see Section 4.3 of @@ -51,7 +58,7 @@ * @author Kevin Wayne */ public class BoruvkaMST { - private static final double FLOATING_POINT_EPSILON = 1E-12; + private static final double FLOATING_POINT_EPSILON = 1.0E-12; private Bag mst = new Bag(); // edges in MST private double weight; // weight of MST @@ -83,7 +90,7 @@ public BoruvkaMST(EdgeWeightedGraph G) { if (e != null) { int v = e.either(), w = e.other(v); // don't add the same edge twice - if (!uf.connected(v, w)) { + if (uf.find(v) != uf.find(w)) { mst.add(e); weight += e.weight(); uf.union(v, w); @@ -116,7 +123,7 @@ public double weight() { // is the weight of edge e strictly less than that of edge f? private static boolean less(Edge e, Edge f) { - return e.weight() < f.weight(); + return e.compareTo(f) < 0; } // check optimality conditions (takes time proportional to E V lg* V) @@ -136,7 +143,7 @@ private boolean check(EdgeWeightedGraph G) { UF uf = new UF(G.V()); for (Edge e : edges()) { int v = e.either(), w = e.other(v); - if (uf.connected(v, w)) { + if (uf.find(v) == uf.find(w)) { System.err.println("Not a forest"); return false; } @@ -146,7 +153,7 @@ private boolean check(EdgeWeightedGraph G) { // check that it is a spanning forest for (Edge e : G.edges()) { int v = e.either(), w = e.other(v); - if (!uf.connected(v, w)) { + if (uf.find(v) != uf.find(w)) { System.err.println("Not a spanning forest"); return false; } @@ -165,7 +172,7 @@ private boolean check(EdgeWeightedGraph G) { // check that e is min weight edge in crossing cut for (Edge f : G.edges()) { int x = f.either(), y = f.other(x); - if (!uf.connected(x, y)) { + if (uf.find(x) != uf.find(y)) { if (f.weight() < e.weight()) { System.err.println("Edge " + f + " violates cut optimality conditions"); return false; @@ -196,7 +203,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BoyerMoore.java b/src/main/java/edu/princeton/cs/algs4/BoyerMoore.java index 08815c2f9..4647e904f 100644 --- a/src/main/java/edu/princeton/cs/algs4/BoyerMoore.java +++ b/src/main/java/edu/princeton/cs/algs4/BoyerMoore.java @@ -9,15 +9,15 @@ * (does not implement the strong good suffix rule) * * % java BoyerMoore abracadabra abacadabrabracabracadabrabrabracad - * text: abacadabrabracabracadabrabrabracad + * text: abacadabrabracabracadabrabrabracad * pattern: abracadabra * * % java BoyerMoore rab abacadabrabracabracadabrabrabracad - * text: abacadabrabracabracadabrabrabracad + * text: abacadabrabracabracadabrabrabracad * pattern: rab * * % java BoyerMoore bcara abacadabrabracabracadabrabrabracad - * text: abacadabrabracabracadabrabrabracad + * text: abacadabrabracabracadabrabrabracad * pattern: bcara * * % java BoyerMoore rabrabracad abacadabrabracabracadabrabrabracad @@ -88,7 +88,7 @@ public BoyerMoore(char[] pattern, int R) { } /** - * Returns the index of the first occurrrence of the pattern string + * Returns the index of the first occurrence of the pattern string * in the text string. * * @param txt the text string @@ -114,7 +114,7 @@ public int search(String txt) { /** - * Returns the index of the first occurrrence of the pattern string + * Returns the index of the first occurrence of the pattern string * in the text string. * * @param text the text string @@ -174,7 +174,7 @@ public static void main(String[] args) { /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BreadthFirstDirectedPaths.java b/src/main/java/edu/princeton/cs/algs4/BreadthFirstDirectedPaths.java index 59837a77c..c0ccb06fa 100644 --- a/src/main/java/edu/princeton/cs/algs4/BreadthFirstDirectedPaths.java +++ b/src/main/java/edu/princeton/cs/algs4/BreadthFirstDirectedPaths.java @@ -29,20 +29,19 @@ package edu.princeton.cs.algs4; /** - * The {@code BreadthDirectedFirstPaths} class represents a data type for finding - * shortest paths (number of edges) from a source vertex s + * The {@code BreadthDirectedFirstPaths} class represents a data type for + * finding shortest paths (number of edges) from a source vertex s * (or set of source vertices) to every other vertex in the digraph. *

* This implementation uses breadth-first search. - * The constructor takes time proportional to V + E, - * where V is the number of vertices and E is the number of edges. - * Each call to {@link #distTo(int)} and {@link #hasPathTo(int)} takes constant time; - * each call to {@link #pathTo(int)} takes time proportional to the length - * of the path. - * It uses extra space (not including the digraph) proportional to V. + * The constructor takes Θ(V + E) time in the + * worst case, where V is the number of vertices and E is + * the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the digraph). *

- * For additional documentation, - * see Section 4.2 of + * For additional documentation, + * see Section 4.2 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -75,6 +74,8 @@ public BreadthFirstDirectedPaths(Digraph G, int s) { * to every other vertex in graph {@code G}. * @param G the digraph * @param sources the source vertices + * @throws IllegalArgumentException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code sources} contains no vertices * @throws IllegalArgumentException unless each vertex {@code v} in * {@code sources} satisfies {@code 0 <= v < V} */ @@ -143,7 +144,8 @@ public boolean hasPathTo(int v) { * Returns the number of edges in a shortest path from the source {@code s} * (or sources) to vertex {@code v}? * @param v the vertex - * @return the number of edges in a shortest path + * @return the number of edges in such a shortest path + * (or {@code Integer.MAX_VALUE} if there is no such path) * @throws IllegalArgumentException unless {@code 0 <= v < V} */ public int distTo(int v) { @@ -177,20 +179,25 @@ private void validateVertex(int v) { throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1)); } - // throw an IllegalArgumentException unless {@code 0 <= v < V} + // throw an IllegalArgumentException if vertices is null, has zero vertices, + // or has a vertex not between 0 and V-1 private void validateVertices(Iterable vertices) { if (vertices == null) { throw new IllegalArgumentException("argument is null"); } - int V = marked.length; - for (int v : vertices) { - if (v < 0 || v >= V) { - throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1)); + int vertexCount = 0; + for (Integer v : vertices) { + vertexCount++; + if (v == null) { + throw new IllegalArgumentException("vertex is null"); } + validateVertex(v); + } + if (vertexCount == 0) { + throw new IllegalArgumentException("zero vertices"); } } - /** * Unit tests the {@code BreadthFirstDirectedPaths} data type. * @@ -225,7 +232,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/BreadthFirstPaths.java b/src/main/java/edu/princeton/cs/algs4/BreadthFirstPaths.java index ca79fd5fd..ec30e8e7e 100644 --- a/src/main/java/edu/princeton/cs/algs4/BreadthFirstPaths.java +++ b/src/main/java/edu/princeton/cs/algs4/BreadthFirstPaths.java @@ -12,12 +12,12 @@ * * % java Graph tinyCG.txt * 6 8 - * 0: 2 1 5 - * 1: 0 2 - * 2: 0 1 3 4 - * 3: 5 4 2 - * 4: 3 2 - * 5: 3 0 + * 0: 2 1 5 + * 1: 0 2 + * 2: 0 1 3 4 + * 3: 5 4 2 + * 4: 3 2 + * 5: 3 0 * * % java BreadthFirstPaths tinyCG.txt 0 * 0 to 0 (0): 0 @@ -48,15 +48,14 @@ * to every other vertex in an undirected graph. *

* This implementation uses breadth-first search. - * The constructor takes time proportional to V + E, - * where V is the number of vertices and E is the number of edges. - * Each call to {@link #distTo(int)} and {@link #hasPathTo(int)} takes constant time; - * each call to {@link #pathTo(int)} takes time proportional to the length - * of the path. - * It uses extra space (not including the graph) proportional to V. + * The constructor takes Θ(V + E) time in the + * worst case, where V is the number of vertices and E + * is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the graph). *

* For additional documentation, - * see Section 4.1 + * see Section 4.1 * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -90,6 +89,8 @@ public BreadthFirstPaths(Graph G, int s) { * and every other vertex in graph {@code G}. * @param G the graph * @param sources the source vertices + * @throws IllegalArgumentException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code sources} contains no vertices * @throws IllegalArgumentException unless {@code 0 <= s < V} for each vertex * {@code s} in {@code sources} */ @@ -162,7 +163,8 @@ public boolean hasPathTo(int v) { * Returns the number of edges in a shortest path between the source vertex {@code s} * (or sources) and vertex {@code v}? * @param v the vertex - * @return the number of edges in a shortest path + * @return the number of edges in such a shortest path + * (or {@code Integer.MAX_VALUE} if there is no such path) * @throws IllegalArgumentException unless {@code 0 <= v < V} */ public int distTo(int v) { @@ -240,16 +242,22 @@ private void validateVertex(int v) { throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1)); } - // throw an IllegalArgumentException unless {@code 0 <= v < V} + // throw an IllegalArgumentException if vertices is null, has zero vertices, + // or has a vertex not between 0 and V-1 private void validateVertices(Iterable vertices) { if (vertices == null) { throw new IllegalArgumentException("argument is null"); } - int V = marked.length; - for (int v : vertices) { - if (v < 0 || v >= V) { - throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1)); + int vertexCount = 0; + for (Integer v : vertices) { + vertexCount++; + if (v == null) { + throw new IllegalArgumentException("vertex is null"); } + validateVertex(v); + } + if (vertexCount == 0) { + throw new IllegalArgumentException("zero vertices"); } } @@ -287,7 +295,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/CC.java b/src/main/java/edu/princeton/cs/algs4/CC.java index b7bae678e..3355279aa 100644 --- a/src/main/java/edu/princeton/cs/algs4/CC.java +++ b/src/main/java/edu/princeton/cs/algs4/CC.java @@ -12,19 +12,19 @@ * % java CC tinyG.txt * 3 components * 0 1 2 3 4 5 6 - * 7 8 + * 7 8 * 9 10 11 12 * - * % java CC mediumG.txt + * % java CC mediumG.txt * 1 components * 0 1 2 3 4 5 6 7 8 9 10 ... * - * % java -Xss50m CC largeG.txt + * % java -Xss50m CC largeG.txt * 1 components * 0 1 2 3 4 5 6 7 8 9 10 ... * * Note: This implementation uses a recursive DFS. To avoid needing - * a potentially very large stack size, replace with a non-recurisve + * a potentially very large stack size, replace with a nonrecursive * DFS ala NonrecursiveDFS.java. * ******************************************************************************/ @@ -32,7 +32,7 @@ package edu.princeton.cs.algs4; /** - * The {@code CC} class represents a data type for + * The {@code CC} class represents a data type for * determining the connected components in an undirected graph. * The id operation determines in which connected component * a given vertex lies; the connected operation @@ -40,21 +40,22 @@ * the count operation determines the number of connected * components; and the size operation determines the number * of vertices in the connect component containing a given vertex. - - * The component identifier of a connected component is one of the - * vertices in the connected component: two vertices have the same component - * identifier if and only if they are in the same connected component. - + *

+ * The component identifier of a vertex is an integer between + * 0 and k–1, where k is the number of connected components. + * Two vertices have the same component identifier if and only if + * they are in the same connected component. *

* This implementation uses depth-first search. - * The constructor takes time proportional to V + E - * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * Afterwards, the id, count, connected, - * and size operations take constant time. + * The constructor takes Θ(V + E) time, + * where V is the number of vertices and E is the + * number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the graph). *

- * For additional documentation, see Section 4.1 - * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, see + * Section 4.1 + * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -235,7 +236,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/CPM.java b/src/main/java/edu/princeton/cs/algs4/CPM.java index 63324a3b6..8e350f127 100644 --- a/src/main/java/edu/princeton/cs/algs4/CPM.java +++ b/src/main/java/edu/princeton/cs/algs4/CPM.java @@ -37,9 +37,9 @@ *

* This implementation uses {@link AcyclicLP} to find a longest * path in a DAG. - * The running time is proportional to V + E, - * where V is the number of jobs and E is the - * number of precedence constraints. + * The program takes Θ(V + E) time in + * the worst case, where V is the number of jobs and + * E is the number of precedence constraints. *

* For additional documentation, * see Section 4.4 of @@ -99,7 +99,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Cat.java b/src/main/java/edu/princeton/cs/algs4/Cat.java index 983553ef2..b89809d53 100644 --- a/src/main/java/edu/princeton/cs/algs4/Cat.java +++ b/src/main/java/edu/princeton/cs/algs4/Cat.java @@ -5,17 +5,17 @@ * Data files: https://algs4.cs.princeton.edu/11model/in1.txt * https://algs4.cs.princeton.edu/11model/in2.txt * - * Reads in text files specified as the first command-line + * Reads in text files specified as the first command-line * arguments, concatenates them, and writes the result to * filename specified as the last command-line arguments. * * % more in1.txt * This is * - * % more in2.txt + * % more in2.txt * a tiny * test. - * + * * % java Cat in1.txt in2.txt out.txt * * % more out.txt @@ -37,7 +37,7 @@ * @author Robert Sedgewick * @author Kevin Wayne */ -public class Cat { +public class Cat { // this class should not be instantiated private Cat() { } @@ -49,7 +49,7 @@ private Cat() { } * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { Out out = new Out(args[args.length - 1]); for (int i = 0; i < args.length - 1; i++) { In in = new In(args[i]); @@ -63,7 +63,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/ClosestPair.java b/src/main/java/edu/princeton/cs/algs4/ClosestPair.java index 533055808..c6638c41f 100644 --- a/src/main/java/edu/princeton/cs/algs4/ClosestPair.java +++ b/src/main/java/edu/princeton/cs/algs4/ClosestPair.java @@ -4,7 +4,7 @@ * Dependencies: Point2D.java * Data files: https://algs4.cs.princeton.edu/99hull/rs1423.txt * https://algs4.cs.princeton.edu/99hull/kw1260.txt - * + * * Given n points in the plane, find the closest pair in n log n time. * * Note: could speed it up by comparing square of Euclidean distances @@ -18,11 +18,11 @@ /** * The {@code ClosestPair} data type computes a closest pair of points - * in a set of n points in the plane and provides accessor methods + * in a set of n points in the plane and provides accessor methods * for getting the closest pair of points and the distance between them. * The distance between two points is their Euclidean distance. *

- * This implementation uses a divide-and-conquer algorithm. + * This implementation uses a divide-and-conquer algorithm. * It runs in O(n log n) time in the worst case and uses * O(n) extra space. *

@@ -56,10 +56,11 @@ public ClosestPair(Point2D[] points) { int n = points.length; if (n <= 1) return; - // sort by x-coordinate (breaking ties by y-coordinate) + // sort by x-coordinate (breaking ties by y-coordinate via stability) Point2D[] pointsByX = new Point2D[n]; for (int i = 0; i < n; i++) pointsByX[i] = points[i]; + Arrays.sort(pointsByX, Point2D.Y_ORDER); Arrays.sort(pointsByX, Point2D.X_ORDER); // check for coincident points @@ -72,7 +73,7 @@ public ClosestPair(Point2D[] points) { } } - // sort by y-coordinate (but not yet sorted) + // sort by y-coordinate (but not yet sorted) Point2D[] pointsByY = new Point2D[n]; for (int i = 0; i < n; i++) pointsByY[i] = pointsByX[i]; @@ -148,7 +149,7 @@ public Point2D other() { } /** - * Returns the Eucliden distance between the closest pair of points. + * Returns the Euclidean distance between the closest pair of points. * * @return the Euclidean distance between the closest pair of points * {@code Double.POSITIVE_INFINITY} if no such pair of points @@ -170,8 +171,8 @@ private static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int for (int k = lo; k <= hi; k++) { aux[k] = a[k]; } - - // merge back to a[] + + // merge back to a[] int i = lo, j = mid+1; for (int k = lo; k <= hi; k++) { if (i > mid) a[k] = aux[j++]; @@ -207,7 +208,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/CollisionSystem.java b/src/main/java/edu/princeton/cs/algs4/CollisionSystem.java index 9842541b8..df79a85f9 100644 --- a/src/main/java/edu/princeton/cs/algs4/CollisionSystem.java +++ b/src/main/java/edu/princeton/cs/algs4/CollisionSystem.java @@ -1,7 +1,7 @@ /****************************************************************************** * Compilation: javac CollisionSystem.java * Execution: java CollisionSystem n (n random particles) - * java CollisionSystem < input.txt (from a file) + * java CollisionSystem < input.txt (from a file) * Dependencies: StdDraw.java Particle.java MinPQ.java * Data files: https://algs4.cs.princeton.edu/61event/diffusion.txt * https://algs4.cs.princeton.edu/61event/diffusion2.txt @@ -10,7 +10,7 @@ * https://algs4.cs.princeton.edu/61event/brownian2.txt * https://algs4.cs.princeton.edu/61event/billiards5.txt * https://algs4.cs.princeton.edu/61event/pendulum.txt - * + * * Creates n random particles and simulates their motion according * to the laws of elastic collisions. * @@ -25,9 +25,9 @@ * moving in the unit box, according to the laws of elastic collision. * This event-based simulation relies on a priority queue. *

- * For additional documentation, - * see Section 6.1 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 6.1 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -80,14 +80,14 @@ private void redraw(double limit) { } } - + /** * Simulates the system of particles for the specified amount of time. * * @param limit the amount of time */ public void simulate(double limit) { - + // initialize PQ with collision events and redraw event pq = new MinPQ(); for (int i = 0; i < particles.length; i++) { @@ -97,7 +97,7 @@ public void simulate(double limit) { // the main event-driven simulation loop - while (!pq.isEmpty()) { + while (!pq.isEmpty()) { // get impending event, discard if invalidated Event e = pq.delMin(); @@ -138,8 +138,8 @@ private static class Event implements Comparable { private final double time; // time that event is scheduled to occur private final Particle a, b; // particles involved in event, possibly null private final int countA, countB; // collision counts at event creation - - + + // create a new event to occur at time t involving a and b public Event(double t, Particle a, Particle b) { this.time = t; @@ -155,14 +155,14 @@ public Event(double t, Particle a, Particle b) { public int compareTo(Event that) { return Double.compare(this.time, that.time); } - + // has any collision occurred between when event was created and now? public boolean isValid() { if (a != null && a.count() != countA) return false; if (b != null && b.count() != countB) return false; return true; } - + } @@ -176,11 +176,7 @@ public boolean isValid() { */ public static void main(String[] args) { - StdDraw.setCanvasSize(800, 800); - - // remove the border - // StdDraw.setXscale(1.0/22.0, 21.0/22.0); - // StdDraw.setYscale(1.0/22.0, 21.0/22.0); + StdDraw.setCanvasSize(600, 600); // enable double buffering StdDraw.enableDoubleBuffering(); @@ -219,11 +215,11 @@ public static void main(String[] args) { CollisionSystem system = new CollisionSystem(particles); system.simulate(10000); } - + } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Complex.java b/src/main/java/edu/princeton/cs/algs4/Complex.java index 1828f55a8..9d6ab6a20 100644 --- a/src/main/java/edu/princeton/cs/algs4/Complex.java +++ b/src/main/java/edu/princeton/cs/algs4/Complex.java @@ -37,6 +37,10 @@ * It includes methods for addition, subtraction, multiplication, division, * conjugation, and other common functions on complex numbers. *

+ * This computes correct results if all arithmetic performed is + * without floating-point rounding error or arithmetic overflow. + * In practice, there will be floating-point rounding error. + *

* For additional documentation, see Section 9.9 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * @@ -233,7 +237,7 @@ public Complex cos() { public Complex tan() { return sin().divides(cos()); } - + /** * Unit tests the {@code Complex} data type. @@ -262,7 +266,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Count.java b/src/main/java/edu/princeton/cs/algs4/Count.java index 1940b50b9..89e2b5523 100644 --- a/src/main/java/edu/princeton/cs/algs4/Count.java +++ b/src/main/java/edu/princeton/cs/algs4/Count.java @@ -5,12 +5,12 @@ * Data files: https://algs4.cs.princeton.edu/50strings/abra.txt * https://algs4.cs.princeton.edu/50strings/pi.txt * - * Create an alphabet specified on the command line, read in a + * Create an alphabet specified on the command line, read in a * sequence of characters over that alphabet (ignoring characters * not in the alphabet), computes the frequency of occurrence of * each character, and print out the results. * - * % java Count ABCDR < abra.txt + * % java Count ABCDR < abra.txt * A 5 * B 2 * C 1 @@ -54,7 +54,7 @@ private Count() { } /** * Reads in text from standard input; calculates the frequency of * occurrence of each character over the alphabet specified as a - * commmand-line argument; and prints the frequencies to standard + * command-line argument; and prints the frequencies to standard * output. * * @param args the command-line arguments @@ -75,7 +75,7 @@ public static void main(String[] args) { /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Counter.java b/src/main/java/edu/princeton/cs/algs4/Counter.java index 1773a3e75..922136b94 100644 --- a/src/main/java/edu/princeton/cs/algs4/Counter.java +++ b/src/main/java/edu/princeton/cs/algs4/Counter.java @@ -42,14 +42,14 @@ public class Counter implements Comparable { */ public Counter(String id) { name = id; - } + } /** * Increments the counter by 1. */ public void increment() { count++; - } + } /** * Returns the current value of this counter. @@ -58,7 +58,7 @@ public void increment() { */ public int tally() { return count; - } + } /** * Returns a string representation of this counter. @@ -67,7 +67,7 @@ public int tally() { */ public String toString() { return count + " " + name; - } + } /** * Compares this counter to the specified counter. @@ -82,9 +82,7 @@ public String toString() { */ @Override public int compareTo(Counter that) { - if (this.count < that.count) return -1; - else if (this.count > that.count) return +1; - else return 0; + return Integer.compare(this.count, that.count); } @@ -94,7 +92,7 @@ public int compareTo(Counter that) { * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { int n = Integer.parseInt(args[0]); int trials = Integer.parseInt(args[1]); @@ -106,18 +104,18 @@ public static void main(String[] args) { // increment trials counters at random for (int t = 0; t < trials; t++) { - hits[StdRandom.uniform(n)].increment(); + hits[StdRandom.uniformInt(n)].increment(); } // print results for (int i = 0; i < n; i++) { StdOut.println(hits[i]); } - } -} + } +} /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Cycle.java b/src/main/java/edu/princeton/cs/algs4/Cycle.java index a4d907f85..e91cbd3a1 100644 --- a/src/main/java/edu/princeton/cs/algs4/Cycle.java +++ b/src/main/java/edu/princeton/cs/algs4/Cycle.java @@ -4,39 +4,43 @@ * Dependencies: Graph.java Stack.java In.java StdOut.java * Data files: https://algs4.cs.princeton.edu/41graph/tinyG.txt * https://algs4.cs.princeton.edu/41graph/mediumG.txt - * https://algs4.cs.princeton.edu/41graph/largeG.txt + * https://algs4.cs.princeton.edu/41graph/largeG.txt * * Identifies a cycle. * Runs in O(E + V) time. * * % java Cycle tinyG.txt - * 3 4 5 3 - * - * % java Cycle mediumG.txt - * 15 0 225 15 - * - * % java Cycle largeG.txt - * 996673 762 840164 4619 785187 194717 996673 + * 3 4 5 3 + * + * % java Cycle mediumG.txt + * 15 0 225 15 + * + * % java Cycle largeG.txt + * 996673 762 840164 4619 785187 194717 996673 * ******************************************************************************/ package edu.princeton.cs.algs4; /** - * The {@code Cycle} class represents a data type for + * The {@code Cycle} class represents a data type for * determining whether an undirected graph has a simple cycle. * The hasCycle operation determines whether the graph has * a cycle and, if so, the cycle operation returns one. *

* This implementation uses depth-first search. - * The constructor takes time proportional to V + E - * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * Afterwards, the hasCycle operation takes constant time; - * the cycle operation takes time proportional - * to the length of the cycle. + * The constructor takes Θ(V + E) time in the + * worst case, where V is the number of vertices and + * E is the number of edges. + * (The depth-first search part takes only O(V) time; + * however, checking for self-loops and parallel edges takes + * Θ(V + E) time in the worst case. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the graph). + * *

- * For additional documentation, see Section 4.1 + * For additional documentation, see + * Section 4.1 * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -54,8 +58,12 @@ public class Cycle { * @param G the undirected graph */ public Cycle(Graph G) { - if (hasSelfLoop(G)) return; + // need special case to identify parallel edge as a cycle if (hasParallelEdges(G)) return; + + // don't need special case to identify self-loop as a cycle + // if (hasSelfLoop(G)) return; + marked = new boolean[G.V()]; edgeTo = new int[G.V()]; for (int v = 0; v < G.V(); v++) @@ -169,12 +177,10 @@ public static void main(String[] args) { } } - } - /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Date.java b/src/main/java/edu/princeton/cs/algs4/Date.java index b7213e099..49443058b 100644 --- a/src/main/java/edu/princeton/cs/algs4/Date.java +++ b/src/main/java/edu/princeton/cs/algs4/Date.java @@ -13,9 +13,9 @@ * The {@code Date} class is an immutable data type to encapsulate a * date (day, month, and year). *

- * For additional documentation, - * see Section 1.2 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 1.2 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -133,8 +133,8 @@ public boolean isBefore(Date that) { * * @return the value {@code 0} if the argument date is equal to this date; * a negative integer if this date is chronologically less than - * the argument date; and a positive ineger if this date is chronologically - * after the argument date + * the argument date; and a positive integer if this date is + * chronologically after the argument date */ @Override public int compareTo(Date that) { @@ -179,11 +179,7 @@ public boolean equals(Object other) { */ @Override public int hashCode() { - int hash = 17; - hash = 31*hash + month; - hash = 31*hash + day; - hash = 31*hash + year; - return hash; + return day + 31*month + 31*12*year; } /** @@ -215,7 +211,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DeDup.java b/src/main/java/edu/princeton/cs/algs4/DeDup.java index 1fc2baa73..23d192bc3 100644 --- a/src/main/java/edu/princeton/cs/algs4/DeDup.java +++ b/src/main/java/edu/princeton/cs/algs4/DeDup.java @@ -7,14 +7,14 @@ * Read in a list of words from standard input and print out * each word, removing any duplicates. * - * % more tinyTale.txt - * it was the best of times it was the worst of times - * it was the age of wisdom it was the age of foolishness - * it was the epoch of belief it was the epoch of incredulity - * it was the season of light it was the season of darkness + * % more tinyTale.txt + * it was the best of times it was the worst of times + * it was the age of wisdom it was the age of foolishness + * it was the epoch of belief it was the epoch of incredulity + * it was the season of light it was the season of darkness * it was the spring of hope it was the winter of despair * - * % java DeDup < tinyTale.txt + * % java DeDup < tinyTale.txt * it * was * the @@ -43,7 +43,7 @@ * @author Robert Sedgewick * @author Kevin Wayne */ -public class DeDup { +public class DeDup { // Do not instantiate. private DeDup() { } @@ -63,7 +63,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DegreesOfSeparation.java b/src/main/java/edu/princeton/cs/algs4/DegreesOfSeparation.java index 774576c5b..eaba98efa 100644 --- a/src/main/java/edu/princeton/cs/algs4/DegreesOfSeparation.java +++ b/src/main/java/edu/princeton/cs/algs4/DegreesOfSeparation.java @@ -4,8 +4,8 @@ * Dependencies: SymbolGraph.java Graph.java BreadthFirstPaths.java StdOut.java * Data files: https://algs4.cs.princeton.edu/41graph/routes.txt * https://algs4.cs.princeton.edu/41graph/movies.txt - * - * + * + * * % java DegreesOfSeparation routes.txt " " "JFK" * LAS * JFK @@ -129,7 +129,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DepthFirstDirectedPaths.java b/src/main/java/edu/princeton/cs/algs4/DepthFirstDirectedPaths.java index 8ae860aa7..eec5dd98d 100644 --- a/src/main/java/edu/princeton/cs/algs4/DepthFirstDirectedPaths.java +++ b/src/main/java/edu/princeton/cs/algs4/DepthFirstDirectedPaths.java @@ -30,21 +30,21 @@ package edu.princeton.cs.algs4; /** - * The {@code DepthFirstDirectedPaths} class represents a data type for finding - * directed paths from a source vertex s to every + * The {@code DepthFirstDirectedPaths} class represents a data type for + * finding directed paths from a source vertex s to every * other vertex in the digraph. *

* This implementation uses depth-first search. - * The constructor takes time proportional to V + E, - * where V is the number of vertices and E is the number of edges. - * Each call to {@link #hasPathTo(int)} takes constant time; - * each call to {@link #pathTo(int)} takes time proportional to the length - * of the path returned. - * It uses extra space (not including the graph) proportional to V. + * The constructor takes Θ(V + E) time in the + * worst case, where V is the number of vertices and E + * is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the digraph). *

- * For additional documentation, - * see Section 4.2 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * See {@link DepthFirstDirectedPaths} for a nonrecursive implementation. + * For additional documentation, + * see Section 4.2 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -68,7 +68,7 @@ public DepthFirstDirectedPaths(Digraph G, int s) { dfs(G, s); } - private void dfs(Digraph G, int v) { + private void dfs(Digraph G, int v) { marked[v] = true; for (int w : G.adj(v)) { if (!marked[w]) { @@ -90,7 +90,7 @@ public boolean hasPathTo(int v) { return marked[v]; } - + /** * Returns a directed path from the source vertex {@code s} to vertex {@code v}, or * {@code null} if no such path. @@ -149,7 +149,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DepthFirstOrder.java b/src/main/java/edu/princeton/cs/algs4/DepthFirstOrder.java index 4931f34f5..58c9e38ea 100644 --- a/src/main/java/edu/princeton/cs/algs4/DepthFirstOrder.java +++ b/src/main/java/edu/princeton/cs/algs4/DepthFirstOrder.java @@ -25,25 +25,25 @@ * 10 8 5 * 11 6 4 * 12 7 3 - * Preorder: 0 5 4 1 6 9 11 12 10 2 3 7 8 - * Postorder: 4 5 1 12 11 10 9 6 0 3 2 7 8 - * Reverse postorder: 8 7 2 3 0 6 9 10 11 12 1 5 4 + * Preorder: 0 5 4 1 6 9 11 12 10 2 3 7 8 + * Postorder: 4 5 1 12 11 10 9 6 0 3 2 7 8 + * Reverse postorder: 8 7 2 3 0 6 9 10 11 12 1 5 4 * ******************************************************************************/ package edu.princeton.cs.algs4; /** - * The {@code DepthFirstOrder} class represents a data type for + * The {@code DepthFirstOrder} class represents a data type for * determining depth-first search ordering of the vertices in a digraph * or edge-weighted digraph, including preorder, postorder, and reverse postorder. *

* This implementation uses depth-first search. - * The constructor takes time proportional to V + E - * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * Afterwards, the preorder, postorder, and reverse postorder - * operation takes take time proportional to V. + * Each constructor takes Θ(V + E) time, + * where V is the number of vertices and E is the + * number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the digraph). *

* For additional documentation, * see Section 4.2 of @@ -243,7 +243,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DepthFirstPaths.java b/src/main/java/edu/princeton/cs/algs4/DepthFirstPaths.java index 8d62f63fa..4b48fd09a 100644 --- a/src/main/java/edu/princeton/cs/algs4/DepthFirstPaths.java +++ b/src/main/java/edu/princeton/cs/algs4/DepthFirstPaths.java @@ -7,17 +7,16 @@ * https://algs4.cs.princeton.edu/41graph/mediumG.txt * https://algs4.cs.princeton.edu/41graph/largeG.txt * - * Run depth first search on an undirected graph. - * Runs in O(E + V) time. + * Run depth-first search on an undirected graph. * * % java Graph tinyCG.txt * 6 8 - * 0: 2 1 5 - * 1: 0 2 - * 2: 0 1 3 4 - * 3: 5 4 2 - * 4: 3 2 - * 5: 3 0 + * 0: 2 1 5 + * 1: 0 2 + * 2: 0 1 3 4 + * 3: 5 4 2 + * 4: 3 2 + * 5: 3 0 * * % java DepthFirstPaths tinyCG.txt 0 * 0 to 0: 0 @@ -37,14 +36,14 @@ * in an undirected graph. *

* This implementation uses depth-first search. - * The constructor takes time proportional to V + E, - * where V is the number of vertices and E is the number of edges. - * Each call to {@link #hasPathTo(int)} takes constant time; - * each call to {@link #pathTo(int)} takes time proportional to the length - * of the path. - * It uses extra space (not including the graph) proportional to V. + * The constructor takes Θ(V + E) time in the + * worst case, where V is the number of vertices and + * E is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the graph). *

- * For additional documentation, see Section 4.1 + * For additional documentation, see + * Section 4.1 * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -147,7 +146,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DepthFirstSearch.java b/src/main/java/edu/princeton/cs/algs4/DepthFirstSearch.java index b70568974..8a994937e 100644 --- a/src/main/java/edu/princeton/cs/algs4/DepthFirstSearch.java +++ b/src/main/java/edu/princeton/cs/algs4/DepthFirstSearch.java @@ -9,11 +9,11 @@ * Runs in O(E + V) time. * * % java DepthFirstSearch tinyG.txt 0 - * 0 1 2 3 4 5 6 + * 0 1 2 3 4 5 6 * NOT connected * * % java DepthFirstSearch tinyG.txt 9 - * 9 10 11 12 + * 9 10 11 12 * NOT connected * ******************************************************************************/ @@ -21,18 +21,21 @@ package edu.princeton.cs.algs4; /** - * The {@code DepthFirstSearch} class represents a data type for + * The {@code DepthFirstSearch} class represents a data type for * determining the vertices connected to a given source vertex s * in an undirected graph. For versions that find the paths, see * {@link DepthFirstPaths} and {@link BreadthFirstPaths}. *

* This implementation uses depth-first search. - * The constructor takes time proportional to V + E - * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * It uses extra space (not including the graph) proportional to V. + * See {@link NonrecursiveDFS} for a non-recursive version. + * The constructor takes Θ(V + E) time in the worst + * case, where V is the number of vertices and E + * is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the graph). *

- * For additional documentation, see Section 4.1 + * For additional documentation, see + * Section 4.1 * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -115,7 +118,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Digraph.java b/src/main/java/edu/princeton/cs/algs4/Digraph.java index c3bb67f5c..79271dbb0 100644 --- a/src/main/java/edu/princeton/cs/algs4/Digraph.java +++ b/src/main/java/edu/princeton/cs/algs4/Digraph.java @@ -4,27 +4,27 @@ * Dependencies: Bag.java In.java StdOut.java * Data files: https://algs4.cs.princeton.edu/42digraph/tinyDG.txt * https://algs4.cs.princeton.edu/42digraph/mediumDG.txt - * https://algs4.cs.princeton.edu/42digraph/largeDG.txt + * https://algs4.cs.princeton.edu/42digraph/largeDG.txt * * A graph, implemented using an array of lists. * Parallel edges and self-loops are permitted. * * % java Digraph tinyDG.txt * 13 vertices, 22 edges - * 0: 5 1 - * 1: - * 2: 0 3 - * 3: 5 2 - * 4: 3 2 - * 5: 4 - * 6: 9 4 8 0 + * 0: 5 1 + * 1: + * 2: 0 3 + * 3: 5 2 + * 4: 3 2 + * 5: 4 + * 6: 9 4 8 0 * 7: 6 9 - * 8: 6 - * 9: 11 10 - * 10: 12 - * 11: 4 12 - * 12: 9 - * + * 8: 6 + * 9: 11 10 + * 10: 12 + * 11: 4 12 + * 12: 9 + * ******************************************************************************/ package edu.princeton.cs.algs4; @@ -36,13 +36,23 @@ * named 0 through V - 1. * It supports the following two primary operations: add an edge to the digraph, * iterate over all of the vertices adjacent from a given vertex. + * It also provides + * methods for returning the indegree or outdegree of a vertex, + * the number of vertices V in the digraph, + * the number of edges E in the digraph, and the reverse digraph. * Parallel edges and self-loops are permitted. *

- * This implementation uses an adjacency-lists representation, which + * This implementation uses an adjacency-lists representation, which * is a vertex-indexed array of {@link Bag} objects. - * All operations take constant time (in the worst case) except - * iterating over the vertices adjacent from a given vertex, which takes - * time proportional to the number of such vertices. + * It uses Θ(E + V) space, where E is + * the number of edges and V is the number of vertices. + * The reverse() method takes Θ(E + V) time + * and space; all other instance methods take Θ(1) time. (Though, iterating over + * the vertices returned by {@link #adj(int)} takes time proportional + * to the outdegree of the vertex.) + * Constructing an empty digraph with V vertices takes + * Θ(V) time; constructing a digraph with E edges + * and V vertices takes Θ(E + V) time. *

* For additional documentation, * see Section 4.2 of @@ -59,7 +69,7 @@ public class Digraph { private int E; // number of edges in this digraph private Bag[] adj; // adj[v] = adjacency list for vertex v private int[] indegree; // indegree[v] = indegree of vertex v - + /** * Initializes an empty digraph with V vertices. * @@ -67,7 +77,7 @@ public class Digraph { * @throws IllegalArgumentException if {@code V < 0} */ public Digraph(int V) { - if (V < 0) throw new IllegalArgumentException("Number of vertices in a Digraph must be nonnegative"); + if (V < 0) throw new IllegalArgumentException("Number of vertices in a Digraph must be non-negative"); this.V = V; this.E = 0; indegree = new int[V]; @@ -77,32 +87,34 @@ public Digraph(int V) { } } - /** + /** * Initializes a digraph from the specified input stream. * The format is the number of vertices V, * followed by the number of edges E, * followed by E pairs of vertices, with each entry separated by whitespace. * * @param in the input stream + * @throws IllegalArgumentException if {@code in} is {@code null} * @throws IllegalArgumentException if the endpoints of any edge are not in prescribed range * @throws IllegalArgumentException if the number of vertices or edges is negative * @throws IllegalArgumentException if the input stream is in the wrong format */ public Digraph(In in) { + if (in == null) throw new IllegalArgumentException("argument is null"); try { this.V = in.readInt(); - if (V < 0) throw new IllegalArgumentException("number of vertices in a Digraph must be nonnegative"); + if (V < 0) throw new IllegalArgumentException("number of vertices in a Digraph must be non-negative"); indegree = new int[V]; adj = (Bag[]) new Bag[V]; for (int v = 0; v < V; v++) { adj[v] = new Bag(); } int E = in.readInt(); - if (E < 0) throw new IllegalArgumentException("number of edges in a Digraph must be nonnegative"); + if (E < 0) throw new IllegalArgumentException("number of edges in a Digraph must be non-negative"); for (int i = 0; i < E; i++) { int v = in.readInt(); int w = in.readInt(); - addEdge(v, w); + addEdge(v, w); } } catch (NoSuchElementException e) { @@ -114,12 +126,26 @@ public Digraph(In in) { * Initializes a new digraph that is a deep copy of the specified digraph. * * @param G the digraph to copy + * @throws IllegalArgumentException if {@code G} is {@code null} */ public Digraph(Digraph G) { - this(G.V()); + if (G == null) throw new IllegalArgumentException("argument is null"); + + this.V = G.V(); this.E = G.E(); + if (V < 0) throw new IllegalArgumentException("Number of vertices in a Digraph must be non-negative"); + + // update indegrees + indegree = new int[V]; for (int v = 0; v < V; v++) this.indegree[v] = G.indegree(v); + + // update adjacency lists + adj = (Bag[]) new Bag[V]; + for (int v = 0; v < V; v++) { + adj[v] = new Bag(); + } + for (int v = 0; v < G.V(); v++) { // reverse so that adjacency list is in same order as original Stack reverse = new Stack(); @@ -131,7 +157,7 @@ public Digraph(Digraph G) { } } } - + /** * Returns the number of vertices in this digraph. * @@ -189,7 +215,7 @@ public Iterable adj(int v) { * This is known as the outdegree of vertex {@code v}. * * @param v the vertex - * @return the outdegree of vertex {@code v} + * @return the outdegree of vertex {@code v} * @throws IllegalArgumentException unless {@code 0 <= v < V} */ public int outdegree(int v) { @@ -202,7 +228,7 @@ public int outdegree(int v) { * This is known as the indegree of vertex {@code v}. * * @param v the vertex - * @return the indegree of vertex {@code v} + * @return the indegree of vertex {@code v} * @throws IllegalArgumentException unless {@code 0 <= v < V} */ public int indegree(int v) { @@ -228,7 +254,7 @@ public Digraph reverse() { /** * Returns a string representation of the graph. * - * @return the number of vertices V, followed by the number of edges E, + * @return the number of vertices V, followed by the number of edges E, * followed by the V adjacency lists */ public String toString() { @@ -244,6 +270,42 @@ public String toString() { return s.toString(); } + /** + * Returns a string representation of this digraph in DOT format, + * suitable for visualization with Graphviz. + * + * To visualize the digraph, install Graphviz (e.g., "brew install graphviz"). + * Then use one of the graph visualization tools + * - dot (hierarchical or layer drawing) + * - neato (spring model) + * - fdp (force-directed placement) + * - sfdp (scalable force-directed placement) + * - twopi (radial layout) + * + * For example, the following commands will create graph drawings in SVG + * and PDF formats + * - dot input.dot -Tsvg -o output.svg + * - dot input.dot -Tpdf -o output.pdf + * + * To change the digraph attributes (e.g., vertex and edge shapes, arrows, colors) + * in the DOT format, see https://graphviz.org/doc/info/lang.html + * + * @return a string representation of this digraph in DOT format + */ + public String toDot() { + StringBuilder s = new StringBuilder(); + s.append("digraph {" + NEWLINE); + s.append("node[shape=circle, style=filled, fixedsize=true, width=0.3, fontsize=\"10pt\"]" + NEWLINE); + s.append("edge[arrowhead=normal]" + NEWLINE); + for (int v = 0; v < V; v++) { + for (int w : adj[v]) { + s.append(v + " -> " + w + NEWLINE); + } + } + s.append("}" + NEWLINE); + return s.toString(); + } + /** * Unit tests the {@code Digraph} data type. * @@ -258,7 +320,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DigraphGenerator.java b/src/main/java/edu/princeton/cs/algs4/DigraphGenerator.java index 0fcc65048..7580fb5fc 100644 --- a/src/main/java/edu/princeton/cs/algs4/DigraphGenerator.java +++ b/src/main/java/edu/princeton/cs/algs4/DigraphGenerator.java @@ -4,7 +4,7 @@ * Dependencies: Digraph.java * * A digraph generator. - * + * ******************************************************************************/ package edu.princeton.cs.algs4; @@ -57,8 +57,8 @@ public static Digraph simple(int V, int E) { Digraph G = new Digraph(V); SET set = new SET(); while (G.E() < E) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); Edge e = new Edge(v, w); if ((v != w) && !set.contains(e)) { set.add(e); @@ -69,10 +69,10 @@ public static Digraph simple(int V, int E) { } /** - * Returns a random simple digraph on {@code V} vertices, with an + * Returns a random simple digraph on {@code V} vertices, with an * edge between any two vertices with probability {@code p}. This is sometimes * referred to as the Erdos-Renyi random digraph model. - * This implementations takes time propotional to V^2 (even if {@code p} is small). + * This implementations takes time proportional to V^2 (even if {@code p} is small). * @param V the number of vertices * @param p the probability of choosing an edge * @return a random simple digraph on {@code V} vertices, with an edge between @@ -93,11 +93,17 @@ public static Digraph simple(int V, double p) { /** * Returns the complete digraph on {@code V} vertices. + * In a complete digraph, every pair of distinct vertices is connected + * by two antiparallel edges. There are {@code V*(V-1)} edges. * @param V the number of vertices * @return the complete digraph on {@code V} vertices */ public static Digraph complete(int V) { - return simple(V, V*(V-1)); + Digraph G = new Digraph(V); + for (int v = 0; v < V; v++) + for (int w = 0; w < V; w++) + if (v != w) G.addEdge(v, w); + return G; } /** @@ -119,8 +125,8 @@ public static Digraph dag(int V, int E) { vertices[i] = i; StdRandom.shuffle(vertices); while (G.E() < E) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); Edge e = new Edge(v, w); if ((v < w) && !set.contains(e)) { set.add(e); @@ -130,11 +136,10 @@ public static Digraph dag(int V, int E) { return G; } - // tournament /** * Returns a random tournament digraph on {@code V} vertices. A tournament digraph - * is a DAG in which for every two vertices, there is one directed edge. - * A tournament is an oriented complete graph. + * is a digraph in which, for every pair of vertices, there is one and only one + * directed edge connecting them. A tournament is an oriented complete graph. * @param V the number of vertices * @return a random tournament digraph on {@code V} vertices */ @@ -149,6 +154,27 @@ public static Digraph tournament(int V) { return G; } + /** + * Returns a complete rooted-in DAG on {@code V} vertices. + * A rooted in-tree is a DAG in which there is a single vertex + * reachable from every other vertex. A complete rooted in-DAG + * has V*(V-1)/2 edges. + * @param V the number of vertices + * @return a complete rooted-in DAG on {@code V} vertices + */ + public static Digraph completeRootedInDAG(int V) { + Digraph G = new Digraph(V); + int[] vertices = new int[V]; + for (int i = 0; i < V; i++) + vertices[i] = i; + StdRandom.shuffle(vertices); + for (int i = 0; i < V; i++) + for (int j = i+1; j < V; j++) + G.addEdge(vertices[i], vertices[j]); + + return G; + } + /** * Returns a random rooted-in DAG on {@code V} vertices and {@code E} edges. * A rooted in-tree is a DAG in which there is a single vertex @@ -172,15 +198,15 @@ public static Digraph rootedInDAG(int V, int E) { // one edge pointing from each vertex, other than the root = vertices[V-1] for (int v = 0; v < V-1; v++) { - int w = StdRandom.uniform(v+1, V); + int w = StdRandom.uniformInt(v+1, V); Edge e = new Edge(v, w); set.add(e); G.addEdge(vertices[v], vertices[w]); } while (G.E() < E) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); Edge e = new Edge(v, w); if ((v < w) && !set.contains(e)) { set.add(e); @@ -190,6 +216,26 @@ public static Digraph rootedInDAG(int V, int E) { return G; } + /** + * Returns a complete rooted-out DAG on {@code V} vertices. + * A rooted out-tree is a DAG in which every vertex is reachable + * from a single vertex. A complete rooted in-DAG has V*(V-1)/2 edges. + * @param V the number of vertices + * @return a complete rooted-out DAG on {@code V} vertices + */ + public static Digraph completeRootedOutDAG(int V) { + Digraph G = new Digraph(V); + int[] vertices = new int[V]; + for (int i = 0; i < V; i++) + vertices[i] = i; + StdRandom.shuffle(vertices); + for (int i = 0; i < V; i++) + for (int j = i+1; j < V; j++) + G.addEdge(vertices[j], vertices[i]); + + return G; + } + /** * Returns a random rooted-out DAG on {@code V} vertices and {@code E} edges. * A rooted out-tree is a DAG in which every vertex is reachable from a @@ -213,15 +259,15 @@ public static Digraph rootedOutDAG(int V, int E) { // one edge pointing from each vertex, other than the root = vertices[V-1] for (int v = 0; v < V-1; v++) { - int w = StdRandom.uniform(v+1, V); + int w = StdRandom.uniformInt(v+1, V); Edge e = new Edge(w, v); set.add(e); G.addEdge(vertices[w], vertices[v]); } while (G.E() < E) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); Edge e = new Edge(w, v); if ((v < w) && !set.contains(e)) { set.add(e); @@ -324,7 +370,7 @@ public static Digraph eulerianCycle(int V, int E) { Digraph G = new Digraph(V); int[] vertices = new int[E]; for (int i = 0; i < E; i++) - vertices[i] = StdRandom.uniform(V); + vertices[i] = StdRandom.uniformInt(V); for (int i = 0; i < E-1; i++) { G.addEdge(vertices[i], vertices[i+1]); } @@ -349,7 +395,7 @@ public static Digraph eulerianPath(int V, int E) { Digraph G = new Digraph(V); int[] vertices = new int[E+1]; for (int i = 0; i < E+1; i++) - vertices[i] = StdRandom.uniform(V); + vertices[i] = StdRandom.uniformInt(V); for (int i = 0; i < E; i++) { G.addEdge(vertices[i], vertices[i+1]); } @@ -359,10 +405,10 @@ public static Digraph eulerianPath(int V, int E) { /** * Returns a random simple digraph on {@code V} vertices, {@code E} * edges and (at least) {@code c} strong components. The vertices are randomly - * assigned integer labels between {@code 0} and {@code c-1} (corresponding to + * assigned integer labels between {@code 0} and {@code c-1} (corresponding to * strong components). Then, a strong component is creates among the vertices * with the same label. Next, random edges (either between two vertices with - * the same labels or from a vetex with a smaller label to a vertex with a + * the same labels or from a vertex with a smaller label to a vertex with a * larger label). The number of components will be equal to the number of * distinct labels that are assigned to vertices. * @@ -389,7 +435,7 @@ public static Digraph strong(int V, int E, int c) { int[] label = new int[V]; for (int v = 0; v < V; v++) - label[v] = StdRandom.uniform(c); + label[v] = StdRandom.uniformInt(c); // make all vertices with label c a strong component by // combining a rooted in-tree and a rooted out-tree @@ -411,7 +457,7 @@ public static Digraph strong(int V, int E, int c) { // rooted-in tree with root = vertices[count-1] for (int v = 0; v < count-1; v++) { - int w = StdRandom.uniform(v+1, count); + int w = StdRandom.uniformInt(v+1, count); Edge e = new Edge(w, v); set.add(e); G.addEdge(vertices[w], vertices[v]); @@ -419,7 +465,7 @@ public static Digraph strong(int V, int E, int c) { // rooted-out tree with root = vertices[count-1] for (int v = 0; v < count-1; v++) { - int w = StdRandom.uniform(v+1, count); + int w = StdRandom.uniformInt(v+1, count); Edge e = new Edge(v, w); set.add(e); G.addEdge(vertices[v], vertices[w]); @@ -427,8 +473,8 @@ public static Digraph strong(int V, int E, int c) { } while (G.E() < E) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); Edge e = new Edge(v, w); if (!set.contains(e) && v != w && label[v] <= label[w]) { set.add(e); @@ -503,7 +549,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DijkstraAllPairsSP.java b/src/main/java/edu/princeton/cs/algs4/DijkstraAllPairsSP.java index a6297a4e3..917f72e4b 100644 --- a/src/main/java/edu/princeton/cs/algs4/DijkstraAllPairsSP.java +++ b/src/main/java/edu/princeton/cs/algs4/DijkstraAllPairsSP.java @@ -3,9 +3,11 @@ * Execution: none * Dependencies: EdgeWeightedDigraph.java Dijkstra.java * - * Dijkstra's algorithm run from each vertex. + * Dijkstra's algorithm run from each vertex. * Takes time proportional to E V log V and space proportional to EV. * + * % java DijkstraAllPairsSP tinyEWD.txt + * ******************************************************************************/ package edu.princeton.cs.algs4; @@ -13,19 +15,19 @@ /** * The {@code DijkstraAllPairsSP} class represents a data type for solving the * all-pairs shortest paths problem in edge-weighted digraphs - * where the edge weights are nonnegative. + * where the edge weights are non-negative. *

* This implementation runs Dijkstra's algorithm from each vertex. - * The constructor takes time proportional to V (E log V) - * and uses space proprtional to V2, - * where V is the number of vertices and E is the number of edges. - * Afterwards, the {@code dist()} and {@code hasPath()} methods take - * constant time and the {@code path()} method takes time proportional to the - * number of edges in the shortest path returned. + * The constructor takes Θ(V (E log V)) time + * in the worst case, where V is the number of vertices and + * E is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V2) extra space (not including the + * edge-weighted digraph). *

- * For additional documentation, - * see Section 4.4 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 4.4 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -34,7 +36,7 @@ public class DijkstraAllPairsSP { private DijkstraSP[] all; /** - * Computes a shortest paths tree from each vertex to to every other vertex in + * Computes a shortest paths tree from each vertex to every other vertex in * the edge-weighted digraph {@code G}. * @param G the edge-weighted digraph * @throws IllegalArgumentException if an edge weight is negative @@ -65,7 +67,7 @@ public Iterable path(int s, int t) { * Is there a path from the vertex {@code s} to vertex {@code t}? * @param s the source vertex * @param t the destination vertex - * @return {@code true} if there is a path from vertex {@code s} + * @return {@code true} if there is a path from vertex {@code s} * to vertex {@code t}, and {@code false} otherwise * @throws IllegalArgumentException unless {@code 0 <= s < V} * @throws IllegalArgumentException unless {@code 0 <= t < V} @@ -98,10 +100,56 @@ private void validateVertex(int v) { throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1)); } + + /** + * Unit tests the {@code DijkstraAllPairsSP} data type. + * + * @param args the command-line arguments + */ + public static void main(String[] args) { + + // read edge-weighted digraph + In in = new In(args[0]); + EdgeWeightedDigraph G = new EdgeWeightedDigraph(in); + + // compute shortest paths between all pairs of vertices + DijkstraAllPairsSP spt = new DijkstraAllPairsSP(G); + + // print all-pairs shortest path distances + StdOut.printf(" "); + for (int v = 0; v < G.V(); v++) { + StdOut.printf("%6d ", v); + } + StdOut.println(); + for (int v = 0; v < G.V(); v++) { + StdOut.printf("%3d: ", v); + for (int w = 0; w < G.V(); w++) { + if (spt.hasPath(v, w)) StdOut.printf("%6.2f ", spt.dist(v, w)); + else StdOut.printf(" Inf "); + } + StdOut.println(); + } + StdOut.println(); + + // print all-pairs shortest paths + for (int v = 0; v < G.V(); v++) { + for (int w = 0; w < G.V(); w++) { + if (spt.hasPath(v, w)) { + StdOut.printf("%d to %d (%5.2f) ", v, w, spt.dist(v, w)); + for (DirectedEdge e : spt.path(v, w)) + StdOut.print(e + " "); + StdOut.println(); + } + else { + StdOut.printf("%d to %d no path\n", v, w); + } + } + } + } } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DijkstraSP.java b/src/main/java/edu/princeton/cs/algs4/DijkstraSP.java index d8a5cf1be..03cd20c10 100644 --- a/src/main/java/edu/princeton/cs/algs4/DijkstraSP.java +++ b/src/main/java/edu/princeton/cs/algs4/DijkstraSP.java @@ -7,24 +7,24 @@ * https://algs4.cs.princeton.edu/44sp/largeEWD.txt * * Dijkstra's algorithm. Computes the shortest path tree. - * Assumes all weights are nonnegative. + * Assumes all weights are non-negative. * * % java DijkstraSP tinyEWD.txt 0 - * 0 to 0 (0.00) - * 0 to 1 (1.05) 0->4 0.38 4->5 0.35 5->1 0.32 - * 0 to 2 (0.26) 0->2 0.26 - * 0 to 3 (0.99) 0->2 0.26 2->7 0.34 7->3 0.39 - * 0 to 4 (0.38) 0->4 0.38 - * 0 to 5 (0.73) 0->4 0.38 4->5 0.35 - * 0 to 6 (1.51) 0->2 0.26 2->7 0.34 7->3 0.39 3->6 0.52 - * 0 to 7 (0.60) 0->2 0.26 2->7 0.34 + * 0 to 0 (0.00) + * 0 to 1 (1.05) 0->4 0.38 4->5 0.35 5->1 0.32 + * 0 to 2 (0.26) 0->2 0.26 + * 0 to 3 (0.99) 0->2 0.26 2->7 0.34 7->3 0.39 + * 0 to 4 (0.38) 0->4 0.38 + * 0 to 5 (0.73) 0->4 0.38 4->5 0.35 + * 0 to 6 (1.51) 0->2 0.26 2->7 0.34 7->3 0.39 3->6 0.52 + * 0 to 7 (0.60) 0->2 0.26 2->7 0.34 * * % java DijkstraSP mediumEWD.txt 0 - * 0 to 0 (0.00) - * 0 to 1 (0.71) 0->44 0.06 44->93 0.07 ... 107->1 0.07 - * 0 to 2 (0.65) 0->44 0.06 44->231 0.10 ... 42->2 0.11 - * 0 to 3 (0.46) 0->97 0.08 97->248 0.09 ... 45->3 0.12 - * 0 to 4 (0.42) 0->44 0.06 44->93 0.07 ... 77->4 0.11 + * 0 to 0 (0.00) + * 0 to 1 (0.71) 0->44 0.06 44->93 0.07 ... 107->1 0.07 + * 0 to 2 (0.65) 0->44 0.06 44->231 0.10 ... 42->2 0.11 + * 0 to 3 (0.46) 0->97 0.08 97->248 0.09 ... 45->3 0.12 + * 0 to 4 (0.42) 0->44 0.06 44->93 0.07 ... 77->4 0.11 * ... * ******************************************************************************/ @@ -35,18 +35,27 @@ /** * The {@code DijkstraSP} class represents a data type for solving the * single-source shortest paths problem in edge-weighted digraphs - * where the edge weights are nonnegative. + * where the edge weights are non-negative. *

- * This implementation uses Dijkstra's algorithm with a binary heap. - * The constructor takes time proportional to E log V, - * where V is the number of vertices and E is the number of edges. - * Each call to {@code distTo(int)} and {@code hasPathTo(int)} takes constant time; - * each call to {@code pathTo(int)} takes time proportional to the number of - * edges in the shortest path returned. + * This implementation uses Dijkstra's algorithm with a + * binary heap. The constructor takes + * Θ(E log V) time in the worst case, + * where V is the number of vertices and E is + * the number of edges. Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the + * edge-weighted digraph). *

- * For additional documentation, - * see Section 4.4 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * This correctly computes shortest paths if all arithmetic performed is + * without floating-point rounding error or arithmetic overflow. + * This is the case if all edge weights are integers and if none of the + * intermediate results exceeds 252. Since all intermediate + * results are sums of edge weights, they are bounded by V C, + * where V is the number of vertices and C is the maximum + * weight of any edge. + *

+ * For additional documentation, + * see Section 4.4 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -153,7 +162,7 @@ public Iterable pathTo(int v) { // (ii) for all edge e on the SPT: distTo[e.to()] == distTo[e.from()] + e.weight() private boolean check(EdgeWeightedDigraph G, int s) { - // check that edge weights are nonnegative + // check that edge weights are non-negative for (DirectedEdge e : G.edges()) { if (e.weight() < 0) { System.err.println("negative edge weight detected"); @@ -238,7 +247,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DijkstraUndirectedSP.java b/src/main/java/edu/princeton/cs/algs4/DijkstraUndirectedSP.java index de71cfaf4..44d6c3ef1 100644 --- a/src/main/java/edu/princeton/cs/algs4/DijkstraUndirectedSP.java +++ b/src/main/java/edu/princeton/cs/algs4/DijkstraUndirectedSP.java @@ -7,7 +7,7 @@ * https://algs4.cs.princeton.edu/43mst/largeEWG.txt * * Dijkstra's algorithm. Computes the shortest path tree. - * Assumes all weights are nonnegative. + * Assumes all weights are non-negative. * * % java DijkstraUndirectedSP tinyEWG.txt 6 * 6 to 0 (0.58) 6-0 0.58000 @@ -27,7 +27,7 @@ * ... * * % java DijkstraUndirectedSP largeEWG.txt 0 - * 0 to 0 (0.00) + * 0 to 0 (0.00) * 0 to 1 (0.78) 0-460790 0.00190 460790-696678 0.00173 ... 1-826350 0.00191 * 0 to 2 (0.61) 0-15786 0.00130 15786-53370 0.00113 ... 2-793420 0.00040 * 0 to 3 (0.31) 0-460790 0.00190 460790-752483 0.00194 ... 3-698373 0.00172 @@ -40,20 +40,29 @@ /** * The {@code DijkstraUndirectedSP} class represents a data type for solving * the single-source shortest paths problem in edge-weighted graphs - * where the edge weights are nonnegative. + * where the edge weights are non-negative. *

* This implementation uses Dijkstra's algorithm with a binary heap. - * The constructor takes time proportional to E log V, - * where V is the number of vertices and E is the number of edges. - * Each call to {@code distTo(int)} and {@code hasPathTo(int)} takes constant time; - * each call to {@code pathTo(int)} takes time proportional to the number of - * edges in the shortest path returned. + * The constructor takes Θ(E log V) time in the + * worst case, where V is the number of vertices and + * E is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the + * edge-weighted graph). *

- * For additional documentation, - * see Section 4.4 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 4.4 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * See {@link DijkstraSP} for a version on edge-weighted digraphs. - * + *

+ * This correctly computes shortest paths if all arithmetic performed is + * without floating-point rounding error or arithmetic overflow. + * This is the case if all edge weights are integers and if none of the + * intermediate results exceeds 252. Since all intermediate + * results are sums of edge weights, they are bounded by V C, + * where V is the number of vertices and C is the maximum + * weight of any edge. + *

* @author Robert Sedgewick * @author Kevin Wayne * @author Nate Liu @@ -165,7 +174,7 @@ public Iterable pathTo(int v) { // (ii) for all edge e = v-w on the SPT: distTo[w] == distTo[v] + e.weight() private boolean check(EdgeWeightedGraph G, int s) { - // check that edge weights are nonnegative + // check that edge weights are non-negative for (Edge e : G.edges()) { if (e.weight() < 0) { System.err.println("negative edge weight detected"); @@ -250,7 +259,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DirectedCycle.java b/src/main/java/edu/princeton/cs/algs4/DirectedCycle.java index d2abaaf3f..29661d1b1 100644 --- a/src/main/java/edu/princeton/cs/algs4/DirectedCycle.java +++ b/src/main/java/edu/princeton/cs/algs4/DirectedCycle.java @@ -6,12 +6,11 @@ * https://algs4.cs.princeton.edu/42digraph/tinyDAG.txt * * Finds a directed cycle in a digraph. - * Runs in O(E + V) time. * - * % java DirectedCycle tinyDG.txt - * Directed cycle: 3 5 4 3 + * % java DirectedCycle tinyDG.txt + * Directed cycle: 3 5 4 3 * - * % java DirectedCycle tinyDAG.txt + * % java DirectedCycle tinyDAG.txt * No directed cycle * ******************************************************************************/ @@ -19,19 +18,18 @@ package edu.princeton.cs.algs4; /** - * The {@code DirectedCycle} class represents a data type for + * The {@code DirectedCycle} class represents a data type for * determining whether a digraph has a directed cycle. * The hasCycle operation determines whether the digraph has * a simple directed cycle and, if so, the cycle operation * returns one. *

* This implementation uses depth-first search. - * The constructor takes time proportional to V + E - * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * Afterwards, the hasCycle operation takes constant time; - * the cycle operation takes time proportional - * to the length of the cycle. + * The constructor takes Θ(V + E) time in the worst + * case, where V is the number of vertices and E is + * the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the digraph). *

* See {@link Topological} to compute a topological order if the * digraph is acyclic. @@ -62,7 +60,7 @@ public DirectedCycle(Digraph G) { if (!marked[v] && cycle == null) dfs(G, v); } - // check that algorithm computes either the topological order or finds a directed cycle + // run DFS and find a directed cycle (if one exists) private void dfs(Digraph G, int v) { onStack[v] = true; marked[v] = true; @@ -156,7 +154,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DirectedCycleX.java b/src/main/java/edu/princeton/cs/algs4/DirectedCycleX.java index 73a377d22..a803c131b 100644 --- a/src/main/java/edu/princeton/cs/algs4/DirectedCycleX.java +++ b/src/main/java/edu/princeton/cs/algs4/DirectedCycleX.java @@ -11,7 +11,7 @@ package edu.princeton.cs.algs4; /** - * The {@code DirectedCycleX} class represents a data type for + * The {@code DirectedCycleX} class represents a data type for * determining whether a digraph has a directed cycle. * The hasCycle operation determines whether the digraph has * a simple directed cycle and, if so, the cycle operation @@ -20,10 +20,10 @@ * This implementation uses a nonrecursive, queue-based algorithm. * The constructor takes time proportional to V + E * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * Afterwards, the hasCycle operation takes constant time; - * the cycle operation takes time proportional - * to the length of the cycle. + * where V is the number of vertices and E is the + * number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the digraph). *

* See {@link DirectedCycle} for a recursive version that uses depth-first search. * See {@link Topological} or {@link TopologicalX} to compute a topological order @@ -144,8 +144,8 @@ public static void main(String[] args) { // add F extra edges for (int i = 0; i < F; i++) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); G.addEdge(v, w); } @@ -170,7 +170,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DirectedDFS.java b/src/main/java/edu/princeton/cs/algs4/DirectedDFS.java index 7748afc06..57506db27 100644 --- a/src/main/java/edu/princeton/cs/algs4/DirectedDFS.java +++ b/src/main/java/edu/princeton/cs/algs4/DirectedDFS.java @@ -17,13 +17,13 @@ * 0 1 2 3 4 5 * * % java DirectedDFS tinyDG.txt 1 2 6 - * 0 1 2 3 4 5 6 8 9 10 11 12 + * 0 1 2 3 4 5 6 8 9 10 11 12 * ******************************************************************************/ package edu.princeton.cs.algs4; /** - * The {@code DirectedDFS} class represents a data type for + * The {@code DirectedDFS} class represents a data type for * determining the vertices reachable from a given source vertex s * (or set of source vertices) in a digraph. For versions that find the paths, * see {@link DepthFirstDirectedPaths} and {@link BreadthFirstDirectedPaths}. @@ -32,6 +32,8 @@ * The constructor takes time proportional to V + E * (in the worst case), * where V is the number of vertices and E is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the digraph). *

* For additional documentation, * see Section 4.2 of @@ -62,6 +64,8 @@ public DirectedDFS(Digraph G, int s) { * connected to any of the source vertices {@code sources}. * @param G the graph * @param sources the source vertices + * @throws IllegalArgumentException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code sources} contains no vertices * @throws IllegalArgumentException unless {@code 0 <= s < V} * for each vertex {@code s} in {@code sources} */ @@ -73,7 +77,7 @@ public DirectedDFS(Digraph G, Iterable sources) { } } - private void dfs(Digraph G, int v) { + private void dfs(Digraph G, int v) { count++; marked[v] = true; for (int w : G.adj(v)) { @@ -110,20 +114,25 @@ private void validateVertex(int v) { throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1)); } - // throw an IllegalArgumentException unless {@code 0 <= v < V} + // throw an IllegalArgumentException if vertices is null, has zero vertices, + // or has a vertex not between 0 and V-1 private void validateVertices(Iterable vertices) { if (vertices == null) { throw new IllegalArgumentException("argument is null"); } - int V = marked.length; - for (int v : vertices) { - if (v < 0 || v >= V) { - throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1)); + int vertexCount = 0; + for (Integer v : vertices) { + vertexCount++; + if (v == null) { + throw new IllegalArgumentException("vertex is null"); } + validateVertex(v); + } + if (vertexCount == 0) { + throw new IllegalArgumentException("zero vertices"); } } - /** * Unit tests the {@code DirectedDFS} data type. * @@ -155,7 +164,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DirectedEdge.java b/src/main/java/edu/princeton/cs/algs4/DirectedEdge.java index 7b420bd3a..600ccfcc2 100644 --- a/src/main/java/edu/princeton/cs/algs4/DirectedEdge.java +++ b/src/main/java/edu/princeton/cs/algs4/DirectedEdge.java @@ -9,20 +9,21 @@ package edu.princeton.cs.algs4; /** - * The {@code DirectedEdge} class represents a weighted edge in an + * The {@code DirectedEdge} class represents a weighted edge in an * {@link EdgeWeightedDigraph}. Each edge consists of two integers * (naming the two vertices) and a real-value weight. The data type * provides methods for accessing the two endpoints of the directed edge and * the weight. *

- * For additional documentation, see Section 4.4 of + * For additional documentation, + * see Section 4.4 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ -public class DirectedEdge { +public class DirectedEdge { private final int v; private final int w; private final double weight; @@ -38,8 +39,8 @@ public class DirectedEdge { * @throws IllegalArgumentException if {@code weight} is {@code NaN} */ public DirectedEdge(int v, int w, double weight) { - if (v < 0) throw new IllegalArgumentException("Vertex names must be nonnegative integers"); - if (w < 0) throw new IllegalArgumentException("Vertex names must be nonnegative integers"); + if (v < 0) throw new IllegalArgumentException("Vertex names must be non-negative integers"); + if (w < 0) throw new IllegalArgumentException("Vertex names must be non-negative integers"); if (Double.isNaN(weight)) throw new IllegalArgumentException("Weight is NaN"); this.v = v; this.w = w; @@ -90,7 +91,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DirectedEulerianCycle.java b/src/main/java/edu/princeton/cs/algs4/DirectedEulerianCycle.java index 61d3e51c9..6e873f8e0 100644 --- a/src/main/java/edu/princeton/cs/algs4/DirectedEulerianCycle.java +++ b/src/main/java/edu/princeton/cs/algs4/DirectedEulerianCycle.java @@ -20,10 +20,11 @@ * uses every edge in the digraph exactly once. *

* This implementation uses a nonrecursive depth-first search. - * The constructor runs in O(E + V) time, - * and uses O(V) extra space, where E is the - * number of edges and V the number of vertices - * All other methods take O(1) time. + * The constructor takes Θ(E + V) time in the worst + * case, where E is the number of edges and V is the + * number of vertices + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the digraph). *

* To compute Eulerian paths in digraphs, see {@link DirectedEulerianPath}. * To compute Eulerian cycles and paths in undirected graphs, see @@ -32,17 +33,17 @@ * For additional documentation, * see Section 4.2 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. - * + * * @author Robert Sedgewick * @author Kevin Wayne * @author Nate Liu */ public class DirectedEulerianCycle { - private Stack cycle = null; // Eulerian cycle; null if no such cylce + private Stack cycle = null; // Eulerian cycle; null if no such cycle /** * Computes an Eulerian cycle in the specified digraph, if one exists. - * + * * @param G the digraph */ public DirectedEulerianCycle(Digraph G) { @@ -88,7 +89,7 @@ public DirectedEulerianCycle(Digraph G) { /** * Returns the sequence of vertices on an Eulerian cycle. - * + * * @return the sequence of vertices on an Eulerian cycle; * {@code null} if no such cycle */ @@ -98,7 +99,7 @@ public Iterable cycle() { /** * Returns true if the digraph has an Eulerian cycle. - * + * * @return {@code true} if the digraph has an Eulerian cycle; * {@code false} otherwise */ @@ -142,8 +143,8 @@ private static boolean satisfiesNecessaryAndSufficientConditions(Digraph G) { for (int v = 0; v < G.V(); v++) for (int w : G.adj(v)) H.addEdge(v, w); - - // check that all non-isolated vertices are conneted + + // check that all non-isolated vertices are connected int s = nonIsolatedVertex(G); BreadthFirstPaths bfs = new BreadthFirstPaths(H, s); for (int v = 0; v < G.V(); v++) @@ -219,7 +220,7 @@ public static void main(String[] args) { // self loop Digraph G4 = new Digraph(V); - int v4 = StdRandom.uniform(V); + int v4 = StdRandom.uniformInt(V); G4.addEdge(v4, v4); unitTest(G4, "single self loop"); @@ -251,7 +252,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DirectedEulerianPath.java b/src/main/java/edu/princeton/cs/algs4/DirectedEulerianPath.java index 80d9ab667..ba2d78f48 100644 --- a/src/main/java/edu/princeton/cs/algs4/DirectedEulerianPath.java +++ b/src/main/java/edu/princeton/cs/algs4/DirectedEulerianPath.java @@ -20,9 +20,10 @@ * uses every edge in the digraph exactly once. *

* This implementation uses a nonrecursive depth-first search. - * The constructor runs in O(E + V) time, and uses O(V) extra space, - * where E is the number of edges and V the number of vertices - * All other methods take O(1) time. + * The constructor take Θ(E + V) time + * in the worst case, where E is the number of edges and + * V is the number of vertices. + * It uses Θ(V) extra space (not including the digraph). *

* To compute Eulerian cycles in digraphs, see {@link DirectedEulerianCycle}. * To compute Eulerian cycles and paths in undirected graphs, see @@ -31,7 +32,7 @@ * For additional documentation, * see Section 4.2 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. - * + * * @author Robert Sedgewick * @author Kevin Wayne * @author Nate Liu @@ -41,7 +42,7 @@ public class DirectedEulerianPath { /** * Computes an Eulerian path in the specified digraph, if one exists. - * + * * @param G the digraph */ public DirectedEulerianPath(Digraph G) { @@ -83,7 +84,7 @@ public DirectedEulerianPath(Digraph G) { // push vertex with no more available edges to path path.push(v); } - + // check if all edges have been used if (path.size() != G.E() + 1) path = null; @@ -93,7 +94,7 @@ public DirectedEulerianPath(Digraph G) { /** * Returns the sequence of vertices on an Eulerian path. - * + * * @return the sequence of vertices on an Eulerian path; * {@code null} if no such path */ @@ -103,7 +104,7 @@ public Iterable path() { /** * Returns true if the digraph has an Eulerian path. - * + * * @return {@code true} if the digraph has an Eulerian path; * {@code false} otherwise */ @@ -150,7 +151,7 @@ private static boolean satisfiesNecessaryAndSufficientConditions(Digraph G) { for (int v = 0; v < G.V(); v++) for (int w : G.adj(v)) H.addEdge(v, w); - + // check that all non-isolated vertices are connected int s = nonIsolatedVertex(G); BreadthFirstPaths bfs = new BreadthFirstPaths(H, s); @@ -223,18 +224,18 @@ public static void main(String[] args) { // add one random edge Digraph G3 = new Digraph(G2); - G3.addEdge(StdRandom.uniform(V), StdRandom.uniform(V)); + G3.addEdge(StdRandom.uniformInt(V), StdRandom.uniformInt(V)); unitTest(G3, "one random edge added to Eulerian path"); // self loop Digraph G4 = new Digraph(V); - int v4 = StdRandom.uniform(V); + int v4 = StdRandom.uniformInt(V); G4.addEdge(v4, v4); unitTest(G4, "single self loop"); // single edge Digraph G5 = new Digraph(V); - G5.addEdge(StdRandom.uniform(V), StdRandom.uniform(V)); + G5.addEdge(StdRandom.uniformInt(V), StdRandom.uniformInt(V)); unitTest(G5, "single edge"); // empty digraph @@ -253,7 +254,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DoublingRatio.java b/src/main/java/edu/princeton/cs/algs4/DoublingRatio.java index eb9034da1..c5f849fa4 100644 --- a/src/main/java/edu/princeton/cs/algs4/DoublingRatio.java +++ b/src/main/java/edu/princeton/cs/algs4/DoublingRatio.java @@ -44,10 +44,10 @@ private DoublingRatio() { } public static double timeTrial(int n) { int[] a = new int[n]; for (int i = 0; i < n; i++) { - a[i] = StdRandom.uniform(-MAXIMUM_INTEGER, MAXIMUM_INTEGER); + a[i] = StdRandom.uniformInt(-MAXIMUM_INTEGER, MAXIMUM_INTEGER); } Stopwatch timer = new Stopwatch(); - ThreeSum.count(a); + int ignore = ThreeSum.count(a); return timer.elapsedTime(); } @@ -58,19 +58,19 @@ public static double timeTrial(int n) { * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { double prev = timeTrial(125); for (int n = 250; true; n += n) { double time = timeTrial(n); StdOut.printf("%7d %7.1f %5.1f\n", n, time, time/prev); prev = time; - } - } -} + } + } +} /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DoublingTest.java b/src/main/java/edu/princeton/cs/algs4/DoublingTest.java index 2c4c3130d..9a0b5fb3d 100644 --- a/src/main/java/edu/princeton/cs/algs4/DoublingTest.java +++ b/src/main/java/edu/princeton/cs/algs4/DoublingTest.java @@ -3,7 +3,7 @@ * Execution: java DoublingTest * Dependencies: ThreeSum.java Stopwatch.java StdRandom.java StdOut.java * - * % java DoublingTest + * % java DoublingTest * 250 0.0 * 500 0.0 * 1000 0.1 @@ -42,10 +42,10 @@ private DoublingTest() { } public static double timeTrial(int n) { int[] a = new int[n]; for (int i = 0; i < n; i++) { - a[i] = StdRandom.uniform(-MAXIMUM_INTEGER, MAXIMUM_INTEGER); + a[i] = StdRandom.uniformInt(-MAXIMUM_INTEGER, MAXIMUM_INTEGER); } Stopwatch timer = new Stopwatch(); - ThreeSum.count(a); + int ignore = ThreeSum.count(a); return timer.elapsedTime(); } @@ -55,16 +55,16 @@ public static double timeTrial(int n) { * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { for (int n = 250; true; n += n) { double time = timeTrial(n); StdOut.printf("%7d %7.1f\n", n, time); - } - } + } + } } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Draw.java b/src/main/java/edu/princeton/cs/algs4/Draw.java index dfa5f4c8e..e4a916c0c 100644 --- a/src/main/java/edu/princeton/cs/algs4/Draw.java +++ b/src/main/java/edu/princeton/cs/algs4/Draw.java @@ -8,7 +8,7 @@ * allows you to create drawings consisting of points, lines, and curves * in a window on your computer and to save the drawings to a file. * This is the object-oriented version of standard draw; it supports - * multiple indepedent drawing windows. + * multiple independent drawing windows. * * Todo * ---- @@ -27,11 +27,14 @@ import java.awt.BasicStroke; import java.awt.Color; +import java.awt.Component; import java.awt.FileDialog; import java.awt.Font; import java.awt.FontMetrics; +import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; +import java.awt.MediaTracker; import java.awt.RenderingHints; import java.awt.Toolkit; @@ -43,6 +46,9 @@ import java.awt.event.KeyEvent; import java.awt.event.KeyListener; +import java.util.Timer; +import java.util.TimerTask; + import java.awt.geom.Arc2D; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; @@ -50,13 +56,14 @@ import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; -import java.awt.image.DirectColorModel; -import java.awt.image.WritableRaster; import java.io.File; import java.io.IOException; +import java.net.MalformedURLException; import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.LinkedList; @@ -73,14 +80,15 @@ import javax.swing.KeyStroke; /** - * Draw. This class provides a basic capability for + * The Draw data type provides a basic capability for * creating drawings with your programs. It uses a simple graphics model that * allows you to create drawings consisting of points, lines, and curves * in a window on your computer and to save the drawings to a file. * This is the object-oriented version of standard draw; it supports - * multiple indepedent drawing windows. + * multiple independent drawing windows. *

- * For additional documentation, see Section 3.1 of + * For additional documentation, see + * Section 3.1 of * Computer Science: An Interdisciplinary Approach by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -89,97 +97,148 @@ public final class Draw implements ActionListener, MouseListener, MouseMotionListener, KeyListener { /** - * The color black. + * The color aqua (0, 255, 255). + */ + public static final Color AQUA = new Color(0, 255, 255); + + /** + * The color black (0, 0, 0). */ public static final Color BLACK = Color.BLACK; /** - * The color blue. + * The color blue (0, 0, 255). */ public static final Color BLUE = Color.BLUE; /** - * The color cyan. + * The color cyan (0, 255, 255). */ public static final Color CYAN = Color.CYAN; /** - * The color dark gray. + * The color fuscia (255, 0, 255). + */ + public static final Color FUSCIA = new Color(255, 0, 255); + + /** + * The color dark gray (64, 64, 64). */ public static final Color DARK_GRAY = Color.DARK_GRAY; /** - * The color gray. + * The color gray (128, 128, 128). */ public static final Color GRAY = Color.GRAY; /** - * The color green. + * The color green (0, 128, 0). */ - public static final Color GREEN = Color.GREEN; + public static final Color GREEN = new Color(0, 128, 0); /** - * The color light gray. + * The color light gray (192, 192, 192). */ public static final Color LIGHT_GRAY = Color.LIGHT_GRAY; /** - * The color magenta. + * The color lime (0, 255, 0). + */ + public static final Color LIME = new Color(0, 255, 0); + + /** + * The color magenta (255, 0, 255). */ public static final Color MAGENTA = Color.MAGENTA; /** - * The color orange. + * The color maroon (128, 0, 0). + */ + public static final Color MAROON = new Color(128, 0, 0); + + /** + * The color navy (0, 0, 128). + */ + public static final Color NAVY = new Color(0, 0, 128); + + /** + * The color olive (128, 128, 0). + */ + public static final Color OLIVE = new Color(128, 128, 0); + + /** + * The color orange (255, 200, 0). */ public static final Color ORANGE = Color.ORANGE; /** - * The color pink. + * The color pink (255, 175, 175). */ public static final Color PINK = Color.PINK; /** - * The color red. + * The color purple (128, 0, 128). + */ + public static final Color PURPLE = new Color(128, 0, 128); + + /** + * The color red (255, 0, 0). */ public static final Color RED = Color.RED; /** - * The color white. + * The color silver (192, 192, 192). + */ + public static final Color SILVER = new Color(192, 192, 192); + + /** + * The color teal (0, 128, 128). + */ + public static final Color TEAL = new Color(0, 128, 128); + + /** + * The color white (255, 255, 255). */ public static final Color WHITE = Color.WHITE; /** - * The color yellow. + * The color yellow (255, 255, 0). */ public static final Color YELLOW = Color.YELLOW; /** - * Shade of blue used in Introduction to Programming in Java. + * A 100% transparent color, for a transparent background. + */ + public static final Color TRANSPARENT = new Color(0, 0, 0, 0); + + /** + * The shade of blue used in Introduction to Programming in Java. * It is Pantone 300U. The RGB values are approximately (9, 90, 166). */ public static final Color BOOK_BLUE = new Color(9, 90, 166); /** - * Shade of light blue used in Introduction to Programming in Java. + * The shade of light blue used in Introduction to Programming in Java. * The RGB values are approximately (103, 198, 243). */ public static final Color BOOK_LIGHT_BLUE = new Color(103, 198, 243); - + /** - * Shade of red used in Algorithms, 4th edition. + * The shade of red used in Algorithms, 4th edition. * It is Pantone 1805U. The RGB values are approximately (150, 35, 31). */ public static final Color BOOK_RED = new Color(150, 35, 31); /** - * Shade of orange used in Princeton's identity. + * The shade of orange used in Princeton University's identity. * It is PMS 158. The RGB values are approximately (245, 128, 37). */ public static final Color PRINCETON_ORANGE = new Color(245, 128, 37); // default colors - private static final Color DEFAULT_PEN_COLOR = BLACK; - private static final Color DEFAULT_CLEAR_COLOR = WHITE; + private static final Color DEFAULT_PEN_COLOR = BLACK; + private static final Color DEFAULT_BACKGROUND_COLOR = WHITE; + // boundary of drawing canvas, 0% border private static final double BORDER = 0.0; @@ -197,30 +256,39 @@ public final class Draw implements ActionListener, MouseListener, MouseMotionLis // default font private static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 16); + // default title of drawing window + private static final String DEFAULT_WINDOW_TITLE = "Draw"; + // current pen color - private Color penColor; + private Color penColor = DEFAULT_PEN_COLOR; + + // background color + private Color backgroundColor = DEFAULT_BACKGROUND_COLOR; + + // current title of drawing window + private String windowTitle = DEFAULT_WINDOW_TITLE; // canvas size private int width = DEFAULT_SIZE; private int height = DEFAULT_SIZE; // current pen radius - private double penRadius; + private double penRadius = DEFAULT_PEN_RADIUS; // show we draw immediately or wait until next show? private boolean defer = false; - private double xmin, ymin, xmax, ymax; - - // name of window - private String name = "Draw"; + private double xmin = DEFAULT_XMIN; + private double xmax = DEFAULT_XMAX; + private double ymin = DEFAULT_YMIN; + private double ymax = DEFAULT_YMAX; // for synchronization private final Object mouseLock = new Object(); private final Object keyLock = new Object(); // current font - private Font font; + private Font font = DEFAULT_FONT; // the JLabel for drawing private JLabel draw; @@ -230,7 +298,10 @@ public final class Draw implements ActionListener, MouseListener, MouseMotionLis private Graphics2D offscreen, onscreen; // the frame for drawing to the screen - private JFrame frame = new JFrame(); + private JFrame frame; + + // is the JFrame visible (upon calling draw())? + private static boolean isJFrameVisible = true; // mouse state private boolean isMousePressed = false; @@ -244,68 +315,86 @@ public final class Draw implements ActionListener, MouseListener, MouseMotionLis // event-based listeners private final ArrayList listeners = new ArrayList(); - - /** - * Initializes an empty drawing object with the given name. - * - * @param name the title of the drawing window. - */ - public Draw(String name) { - this.name = name; - init(); - } + // timer + private Timer timer; /** * Initializes an empty drawing object. */ public Draw() { - init(); + initCanvas(); + initGUI(); } - private void init() { - if (frame != null) frame.setVisible(false); - frame = new JFrame(); - offscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - onscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + // initialize the drawing canvas + private void initCanvas() { + + // BufferedImage stuff + offscreenImage = new BufferedImage(2*width, 2*height, BufferedImage.TYPE_INT_ARGB); + onscreenImage = new BufferedImage(2*width, 2*height, BufferedImage.TYPE_INT_ARGB); offscreen = offscreenImage.createGraphics(); onscreen = onscreenImage.createGraphics(); - setXscale(); - setYscale(); - offscreen.setColor(DEFAULT_CLEAR_COLOR); - offscreen.fillRect(0, 0, width, height); - setPenColor(); - setPenRadius(); - setFont(); - clear(); + offscreen.scale(2.0, 2.0); // since we made it 2x as big + + // initialize drawing window + offscreen.setBackground(DEFAULT_BACKGROUND_COLOR); + offscreen.clearRect(0, 0, width, height); + onscreen.setBackground(DEFAULT_BACKGROUND_COLOR); + onscreen.clearRect(0, 0, 2*width, 2*height); + + // set the pen color + offscreen.setColor(penColor); // add antialiasing - RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); + RenderingHints hints = new RenderingHints(null); + hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); offscreen.addRenderingHints(hints); + } - // frame stuff - ImageIcon icon = new ImageIcon(onscreenImage); - draw = new JLabel(icon); + // initialize the GUI + private void initGUI() { + + // create the JFrame (if necessary) + if (frame == null) { + frame = new JFrame(); + frame.addKeyListener(this); // JLabel cannot get keyboard focus + frame.setFocusTraversalKeysEnabled(false); // allow VK_TAB with isKeyPressed() + frame.setResizable(false); + // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closes all windows + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // closes only current window + frame.setTitle(windowTitle); + frame.setJMenuBar(createMenuBar()); + } + // create the ImageIcon + RetinaImageIcon icon = new RetinaImageIcon(onscreenImage); + draw = new JLabel(icon); draw.addMouseListener(this); draw.addMouseMotionListener(this); + // finish up the JFrame frame.setContentPane(draw); - frame.addKeyListener(this); // JLabel cannot get keyboard focus - frame.setResizable(false); - // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closes all windows - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // closes only current window - frame.setTitle(name); - frame.setJMenuBar(createMenuBar()); frame.pack(); frame.requestFocusInWindow(); - frame.setVisible(true); + frame.setVisible(false); } /** - * Sets the upper-left hand corner of the drawing window to be (x, y), where (0, 0) is upper left. + * Makes the drawing window visible or invisible. + * + * @param isVisible if {@code true}, makes the drawing window visible, + * otherwise hides the drawing window. + */ + public void setVisible(boolean isVisible) { + isJFrameVisible = isVisible; + frame.setVisible(isVisible); + } + + /** + * Sets the upper-left hand corner of the drawing window to be (x, y), + * where (0, 0) is upper left. * * @param x the number of pixels from the left * @param y the number of pixels from the top @@ -326,13 +415,20 @@ public void setLocationOnScreen(int x, int y) { public void setDefaultCloseOperation(int value) { frame.setDefaultCloseOperation(value); } - + + /** + * Sets the canvas (drawing area) to be 512-by-512 pixels. + * This also clears the current drawing using the default background color (white). + * Ordinarily, this method is called once, at the very beginning of a program. + */ + public void setCanvasSize() { + setCanvasSize(DEFAULT_SIZE, DEFAULT_SIZE); + } /** * Sets the canvas (drawing area) to be width-by-height pixels. - * This also erases the current drawing and resets the coordinate system, pen radius, - * pen color, and font back to their default values. - * Ordinarly, this method is called once, at the very beginning of a program. + * This also clears the current drawing using the default background color (white). + * Ordinarily, this method is called once, at the very beginning of a program. * * @param canvasWidth the width as a number of pixels * @param canvasHeight the height as a number of pixels @@ -345,66 +441,146 @@ public void setCanvasSize(int canvasWidth, int canvasHeight) { } width = canvasWidth; height = canvasHeight; - init(); + initCanvas(); + initGUI(); } - // create the menu bar (changed to private) + // create the menu bar private JMenuBar createMenuBar() { JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu("File"); menuBar.add(menu); JMenuItem menuItem1 = new JMenuItem(" Save... "); menuItem1.addActionListener(this); + // Java 11: use getMenuShortcutKeyMaskEx() + // Java 8: use getMenuShortcutKeyMask() menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); menu.add(menuItem1); return menuBar; } + /** + * Closes the drawing window. + * This allows the client program to terminate instead of requiring + * the user to close the drawing window manually. + * Drawing after calling this method will restore the previous window state. + */ + public void close() { + frame.dispose(); + } + + /*************************************************************************** + * Input validation helper methods. + ***************************************************************************/ + + // throw an IllegalArgumentException if x is NaN or infinite + private static void validate(double x, String name) { + if (Double.isNaN(x)) throw new IllegalArgumentException(name + " is NaN"); + if (Double.isInfinite(x)) throw new IllegalArgumentException(name + " is infinite"); + } + + // throw an IllegalArgumentException if s is null + private static void validateNonnegative(double x, String name) { + if (x < 0) throw new IllegalArgumentException(name + " negative"); + } + + // throw an IllegalArgumentException if s is null + private static void validateNotNull(Object x, String name) { + if (x == null) throw new IllegalArgumentException(name + " is null"); + } + + + /*************************************************************************** + * Set the title of the drawing window. + ***************************************************************************/ + + /** + * Sets the title of the drawing window to the specified string. + * + * @param windowTitle the title of the window + * @throws IllegalArgumentException if {@code title} is {@code null} + */ + public void setTitle(String windowTitle) { + validateNotNull(windowTitle, "title"); + this.windowTitle = windowTitle; + frame.setTitle(windowTitle); + } /*************************************************************************** * User and screen coordinate systems. ***************************************************************************/ /** - * Sets the x-scale to be the default (between 0.0 and 1.0). + * Sets the x-scale to the default range (between 0.0 and 1.0). */ public void setXscale() { setXscale(DEFAULT_XMIN, DEFAULT_XMAX); } /** - * Sets the y-scale to be the default (between 0.0 and 1.0). + * Sets the y-scale to the default range (between 0.0 and 1.0). */ public void setYscale() { setYscale(DEFAULT_YMIN, DEFAULT_YMAX); } /** - * Sets the x-scale. + * Sets the x-scale to the specified range. * * @param min the minimum value of the x-scale * @param max the maximum value of the x-scale + * @throws IllegalArgumentException if {@code (max == min)} + * @throws IllegalArgumentException if either {@code min} or {@code max} is either NaN or infinite */ public void setXscale(double min, double max) { + validate(min, "min"); + validate(max, "max"); double size = max - min; + if (size == 0.0) throw new IllegalArgumentException("the min and max are the same"); xmin = min - BORDER * size; xmax = max + BORDER * size; } /** - * Sets the y-scale. + * Sets the y-scale to the specified range. * * @param min the minimum value of the y-scale * @param max the maximum value of the y-scale + * @throws IllegalArgumentException if {@code (max == min)} + * @throws IllegalArgumentException if either {@code min} or {@code max} is either NaN or infinite */ public void setYscale(double min, double max) { + validate(min, "min"); + validate(max, "max"); double size = max - min; + if (size == 0.0) throw new IllegalArgumentException("the min and max are the same"); ymin = min - BORDER * size; ymax = max + BORDER * size; } + /** + * Sets both the x-scale and y-scale to the default range (between 0.0 and 1.0). + */ + public void setScale() { + setXscale(); + setYscale(); + } + + /** + * Sets both the x-scale and y-scale to the (same) specified range. + * @param min the minimum value of the y-scale + * @param max the maximum value of the y-scale + * @throws IllegalArgumentException if {@code (max == min)} + * @throws IllegalArgumentException if either {@code min} or {@code max} is either NaN or infinite + */ + public void setScale(double min, double max) { + setXscale(min, max); + setYscale(min, max); + } + + // helper functions that scale from user coordinates to screen coordinates and back private double scaleX(double x) { return width * (x - xmin) / (xmax - xmin); } private double scaleY(double y) { return height * (ymax - y) / (ymax - ymin); } @@ -415,26 +591,31 @@ public void setYscale(double min, double max) { /** - * Clears the screen to the default color (white). + * Clears the screen using the default background color (white). */ public void clear() { - clear(DEFAULT_CLEAR_COLOR); + clear(DEFAULT_BACKGROUND_COLOR); } /** - * Clears the screen to the given color. + * Clears the screen using the specified background color. + * To make the background transparent, use {@code Draw.TRANSPARENT}. * * @param color the color to make the background + * @throws IllegalArgumentException if {@code color} is {@code null} */ public void clear(Color color) { - offscreen.setColor(color); - offscreen.fillRect(0, 0, width, height); - offscreen.setColor(penColor); + validateNotNull(color, "color"); + + backgroundColor = color; + offscreen.setBackground(backgroundColor); + offscreen.clearRect(0, 0, width, height); + draw(); } /** - * Gets the current pen radius. + * Returns the current pen radius. * * @return the current pen radius */ @@ -443,7 +624,7 @@ public double getPenRadius() { } /** - * Sets the pen size to the default (.002). + * Sets the pen radius to the default (0.002). */ public void setPenRadius() { setPenRadius(DEFAULT_PEN_RADIUS); @@ -452,19 +633,21 @@ public void setPenRadius() { /** * Sets the radius of the pen to the given size. * - * @param r the radius of the pen - * @throws IllegalArgumentException if r is negative + * @param radius the radius of the pen + * @throws IllegalArgumentException if {@code radius} is negative, NaN, or infinite */ - public void setPenRadius(double r) { - if (r < 0) throw new IllegalArgumentException("pen radius must be positive"); - penRadius = r * DEFAULT_SIZE; + public void setPenRadius(double radius) { + validate(radius, "pen radius"); + validateNonnegative(radius, "pen radius"); + + penRadius = radius * DEFAULT_SIZE; BasicStroke stroke = new BasicStroke((float) penRadius, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); // BasicStroke stroke = new BasicStroke((float) penRadius); offscreen.setStroke(stroke); } /** - * Gets the current pen color. + * Returns the current pen color. * * @return the current pen color */ @@ -472,6 +655,15 @@ public Color getPenColor() { return penColor; } + /** + * Returns the current background color. + * + * @return the current background color + */ + public Color getBackgroundColor() { + return backgroundColor; + } + /** * Sets the pen color to the default color (black). */ @@ -483,8 +675,10 @@ public void setPenColor() { * Sets the pen color to the given color. * * @param color the color to make the pen + * @throws IllegalArgumentException if {@code color} is {@code null} */ public void setPenColor(Color color) { + validateNotNull(color, "color"); penColor = color; offscreen.setColor(penColor); } @@ -495,12 +689,13 @@ public void setPenColor(Color color) { * @param red the amount of red (between 0 and 255) * @param green the amount of green (between 0 and 255) * @param blue the amount of blue (between 0 and 255) - * @throws IllegalArgumentException if the amount of red, green, or blue are outside prescribed range + * @throws IllegalArgumentException if {@code red}, {@code green}, + * or {@code blue} is outside its prescribed range */ public void setPenColor(int red, int green, int blue) { - if (red < 0 || red >= 256) throw new IllegalArgumentException("amount of red must be between 0 and 255"); - if (green < 0 || green >= 256) throw new IllegalArgumentException("amount of red must be between 0 and 255"); - if (blue < 0 || blue >= 256) throw new IllegalArgumentException("amount of red must be between 0 and 255"); + if (red < 0 || red >= 256) throw new IllegalArgumentException("red must be between 0 and 255"); + if (green < 0 || green >= 256) throw new IllegalArgumentException("green must be between 0 and 255"); + if (blue < 0 || blue >= 256) throw new IllegalArgumentException("blue must be between 0 and 255"); setPenColor(new Color(red, green, blue)); } @@ -509,7 +704,7 @@ public void setPenColor(int red, int green, int blue) { * Turns on xor mode. */ public void xorOn() { - offscreen.setXORMode(DEFAULT_CLEAR_COLOR); + offscreen.setXORMode(backgroundColor); } /** @@ -520,7 +715,7 @@ public void xorOff() { } /** - * Gets the current {@code JLabel} for use in some other GUI. + * Returns the current {@code JLabel} for use in some other GUI. * * @return the current {@code JLabel} */ @@ -529,7 +724,7 @@ public JLabel getJLabel() { } /** - * Gets the current font. + * Returns the current font. * * @return the current font */ @@ -548,8 +743,10 @@ public void setFont() { * Sets the font to the given value. * * @param font the font + * @throws IllegalArgumentException if {@code font} is {@code null} */ public void setFont(Font font) { + validateNotNull(font, "font"); this.font = font; } @@ -565,8 +762,13 @@ public void setFont(Font font) { * @param y0 the y-coordinate of the starting point * @param x1 the x-coordinate of the destination point * @param y1 the y-coordinate of the destination point + * @throws IllegalArgumentException if any coordinate is either NaN or infinite */ public void line(double x0, double y0, double x1, double y1) { + validate(x0, "x0"); + validate(y0, "y0"); + validate(x1, "x1"); + validate(y1, "y1"); offscreen.draw(new Line2D.Double(scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1))); draw(); } @@ -576,8 +778,11 @@ public void line(double x0, double y0, double x1, double y1) { * * @param x the x-coordinate of the pixel * @param y the y-coordinate of the pixel + * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite */ private void pixel(double x, double y) { + validate(x, "x"); + validate(y, "y"); offscreen.fillRect((int) Math.round(scaleX(x)), (int) Math.round(scaleY(y)), 1, 1); } @@ -586,8 +791,12 @@ private void pixel(double x, double y) { * * @param x the x-coordinate of the point * @param y the y-coordinate of the point + * @throws IllegalArgumentException if either {@code x} or {@code y} is either NaN or infinite */ public void point(double x, double y) { + validate(x, "x"); + validate(y, "y"); + double xs = scaleX(x); double ys = scaleY(y); double r = penRadius; @@ -600,38 +809,48 @@ public void point(double x, double y) { } /** - * Draws a circle of radius r, centered on (x, y). + * Draws a circle of the specified radius, centered at (x, y). * * @param x the x-coordinate of the center of the circle * @param y the y-coordinate of the center of the circle - * @param r the radius of the circle - * @throws IllegalArgumentException if the radius of the circle is negative + * @param radius the radius of the circle + * @throws IllegalArgumentException if {@code radius} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ - public void circle(double x, double y, double r) { - if (r < 0) throw new IllegalArgumentException("circle radius can't be negative"); + public void circle(double x, double y, double radius) { + validate(x, "x"); + validate(y, "y"); + validate(radius, "radius"); + validateNonnegative(radius, "radius"); + double xs = scaleX(x); double ys = scaleY(y); - double ws = factorX(2*r); - double hs = factorY(2*r); + double ws = factorX(2*radius); + double hs = factorY(2*radius); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** - * Draws a filled circle of radius r, centered on (x, y). + * Draws a filled circle of the specified radius, centered at (x, y). * * @param x the x-coordinate of the center of the circle * @param y the y-coordinate of the center of the circle - * @param r the radius of the circle - * @throws IllegalArgumentException if the radius of the circle is negative + * @param radius the radius of the circle + * @throws IllegalArgumentException if {@code radius} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ - public void filledCircle(double x, double y, double r) { - if (r < 0) throw new IllegalArgumentException("circle radius can't be negative"); + public void filledCircle(double x, double y, double radius) { + validate(x, "x"); + validate(y, "y"); + validate(radius, "radius"); + validateNonnegative(radius, "radius"); + double xs = scaleX(x); double ys = scaleY(y); - double ws = factorX(2*r); - double hs = factorY(2*r); + double ws = factorX(2*radius); + double hs = factorY(2*radius); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); @@ -639,17 +858,25 @@ public void filledCircle(double x, double y, double r) { /** - * Draws an ellipse with given semimajor and semiminor axes, centered on (x, y). + * Draws an ellipse with the specified semimajor and semiminor axes, + * centered at (x, y). * - * @param x the x-coordinate of the center of the ellipse - * @param y the y-coordinate of the center of the ellipse + * @param x the x-coordinate of the center of the ellipse + * @param y the y-coordinate of the center of the ellipse * @param semiMajorAxis is the semimajor axis of the ellipse * @param semiMinorAxis is the semiminor axis of the ellipse - * @throws IllegalArgumentException if either of the axes are negative + * @throws IllegalArgumentException if either {@code semiMajorAxis} + * or {@code semiMinorAxis} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ public void ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis) { - if (semiMajorAxis < 0) throw new IllegalArgumentException("ellipse semimajor axis can't be negative"); - if (semiMinorAxis < 0) throw new IllegalArgumentException("ellipse semiminor axis can't be negative"); + validate(x, "x"); + validate(y, "y"); + validate(semiMajorAxis, "semimajor axis"); + validate(semiMinorAxis, "semiminor axis"); + validateNonnegative(semiMajorAxis, "semimajor axis"); + validateNonnegative(semiMinorAxis, "semiminor axis"); + double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*semiMajorAxis); @@ -660,16 +887,25 @@ public void ellipse(double x, double y, double semiMajorAxis, double semiMinorAx } /** - * Draws an ellipse with given semimajor and semiminor axes, centered on (x, y). - * @param x the x-coordinate of the center of the ellipse - * @param y the y-coordinate of the center of the ellipse + * Draws a filled ellipse with the specified semimajor and semiminor axes, + * centered at (x, y). + * + * @param x the x-coordinate of the center of the ellipse + * @param y the y-coordinate of the center of the ellipse * @param semiMajorAxis is the semimajor axis of the ellipse * @param semiMinorAxis is the semiminor axis of the ellipse - * @throws IllegalArgumentException if either of the axes are negative + * @throws IllegalArgumentException if either {@code semiMajorAxis} + * or {@code semiMinorAxis} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ public void filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis) { - if (semiMajorAxis < 0) throw new IllegalArgumentException("ellipse semimajor axis can't be negative"); - if (semiMinorAxis < 0) throw new IllegalArgumentException("ellipse semiminor axis can't be negative"); + validate(x, "x"); + validate(y, "y"); + validate(semiMajorAxis, "semimajor axis"); + validate(semiMinorAxis, "semiminor axis"); + validateNonnegative(semiMajorAxis, "semimajor axis"); + validateNonnegative(semiMinorAxis, "semiminor axis"); + double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*semiMajorAxis); @@ -680,61 +916,79 @@ public void filledEllipse(double x, double y, double semiMajorAxis, double semiM } /** - * Draws an arc of radius r, centered on (x, y), from angle1 to angle2 (in degrees). + * Draws a circular arc of the specified radius, + * centered at (x, y), from angle1 to angle2 (in degrees). * - * @param x the x-coordinate of the center of the circle - * @param y the y-coordinate of the center of the circle - * @param r the radius of the circle + * @param x the x-coordinate of the center of the circle + * @param y the y-coordinate of the center of the circle + * @param radius the radius of the circle * @param angle1 the starting angle. 0 would mean an arc beginning at 3 o'clock. * @param angle2 the angle at the end of the arc. For example, if * you want a 90 degree arc, then angle2 should be angle1 + 90. - * @throws IllegalArgumentException if the radius of the circle is negative + * @throws IllegalArgumentException if {@code radius} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ - public void arc(double x, double y, double r, double angle1, double angle2) { - if (r < 0) throw new IllegalArgumentException("arc radius can't be negative"); + public void arc(double x, double y, double radius, double angle1, double angle2) { + validate(x, "x"); + validate(y, "y"); + validate(radius, "arc radius"); + validate(angle1, "angle1"); + validate(angle2, "angle2"); + validateNonnegative(radius, "arc radius"); + while (angle2 < angle1) angle2 += 360; double xs = scaleX(x); double ys = scaleY(y); - double ws = factorX(2*r); - double hs = factorY(2*r); + double ws = factorX(2*radius); + double hs = factorY(2*radius); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.draw(new Arc2D.Double(xs - ws/2, ys - hs/2, ws, hs, angle1, angle2 - angle1, Arc2D.OPEN)); draw(); } /** - * Draws a square of side length 2r, centered on (x, y). + * Draws a square of the specified size, centered at (x, y). * - * @param x the x-coordinate of the center of the square - * @param y the y-coordinate of the center of the square - * @param r radius is half the length of any side of the square - * @throws IllegalArgumentException if r is negative - */ - public void square(double x, double y, double r) { - if (r < 0) throw new IllegalArgumentException("square side length can't be negative"); + * @param x the x-coordinate of the center of the square + * @param y the y-coordinate of the center of the square + * @param halfLength one half the length of any side of the square + * @throws IllegalArgumentException if {@code halfLength} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite + */ + public void square(double x, double y, double halfLength) { + validate(x, "x"); + validate(y, "y"); + validate(halfLength, "halfLength"); + validateNonnegative(halfLength, "half length"); + double xs = scaleX(x); double ys = scaleY(y); - double ws = factorX(2*r); - double hs = factorY(2*r); + double ws = factorX(2*halfLength); + double hs = factorY(2*halfLength); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** - * Draws a filled square of side length 2r, centered on (x, y). + * Draws a square of the specified size, centered at (x, y). * - * @param x the x-coordinate of the center of the square - * @param y the y-coordinate of the center of the square - * @param r radius is half the length of any side of the square - * @throws IllegalArgumentException if r is negative - */ - public void filledSquare(double x, double y, double r) { - if (r < 0) throw new IllegalArgumentException("square side length can't be negative"); + * @param x the x-coordinate of the center of the square + * @param y the y-coordinate of the center of the square + * @param halfLength one half the length of any side of the square + * @throws IllegalArgumentException if {@code halfLength} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite + */ + public void filledSquare(double x, double y, double halfLength) { + validate(x, "x"); + validate(y, "y"); + validate(halfLength, "halfLength"); + validateNonnegative(halfLength, "half length"); + double xs = scaleX(x); double ys = scaleY(y); - double ws = factorX(2*r); - double hs = factorY(2*r); + double ws = factorX(2*halfLength); + double hs = factorY(2*halfLength); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); @@ -742,17 +996,23 @@ public void filledSquare(double x, double y, double r) { /** - * Draws a rectangle of given half width and half height, centered on (x, y). + * Draws a rectangle of the specified size, centered at (x, y). * - * @param x the x-coordinate of the center of the rectangle - * @param y the y-coordinate of the center of the rectangle - * @param halfWidth is half the width of the rectangle - * @param halfHeight is half the height of the rectangle - * @throws IllegalArgumentException if halfWidth or halfHeight is negative + * @param x the x-coordinate of the center of the rectangle + * @param y the y-coordinate of the center of the rectangle + * @param halfWidth one half the width of the rectangle + * @param halfHeight one half the height of the rectangle + * @throws IllegalArgumentException if either {@code halfWidth} or {@code halfHeight} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ public void rectangle(double x, double y, double halfWidth, double halfHeight) { - if (halfWidth < 0) throw new IllegalArgumentException("half width can't be negative"); - if (halfHeight < 0) throw new IllegalArgumentException("half height can't be negative"); + validate(x, "x"); + validate(y, "y"); + validate(halfWidth, "halfWidth"); + validate(halfHeight, "halfHeight"); + validateNonnegative(halfWidth, "half width"); + validateNonnegative(halfHeight, "half height"); + double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*halfWidth); @@ -763,17 +1023,23 @@ public void rectangle(double x, double y, double halfWidth, double halfHeight) { } /** - * Draws a filled rectangle of given half width and half height, centered on (x, y). + * Draws a filled rectangle of the specified size, centered at (x, y). * - * @param x the x-coordinate of the center of the rectangle - * @param y the y-coordinate of the center of the rectangle - * @param halfWidth is half the width of the rectangle - * @param halfHeight is half the height of the rectangle - * @throws IllegalArgumentException if halfWidth or halfHeight is negative + * @param x the x-coordinate of the center of the rectangle + * @param y the y-coordinate of the center of the rectangle + * @param halfWidth one half the width of the rectangle + * @param halfHeight one half the height of the rectangle + * @throws IllegalArgumentException if either {@code halfWidth} or {@code halfHeight} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ public void filledRectangle(double x, double y, double halfWidth, double halfHeight) { - if (halfWidth < 0) throw new IllegalArgumentException("half width can't be negative"); - if (halfHeight < 0) throw new IllegalArgumentException("half height can't be negative"); + validate(x, "x"); + validate(y, "y"); + validate(halfWidth, "halfWidth"); + validate(halfHeight, "halfHeight"); + validateNonnegative(halfWidth, "half width"); + validateNonnegative(halfHeight, "half height"); + double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*halfWidth); @@ -784,13 +1050,30 @@ public void filledRectangle(double x, double y, double halfWidth, double halfHei } /** - * Draws a polygon with the given (x[i], y[i]) coordinates. + * Draws a polygon with the vertices + * (x0, y0), + * (x1, y1), ..., + * (xn–1, yn–1). * - * @param x an array of all the x-coordindates of the polygon - * @param y an array of all the y-coordindates of the polygon + * @param x an array of all the x-coordinates of the polygon + * @param y an array of all the y-coordinates of the polygon + * @throws IllegalArgumentException unless {@code x[]} and {@code y[]} + * are of the same length + * @throws IllegalArgumentException if any coordinate is either NaN or infinite + * @throws IllegalArgumentException if either {@code x[]} or {@code y[]} is {@code null} */ public void polygon(double[] x, double[] y) { - int n = x.length; + validateNotNull(x, "x-coordinate array"); + validateNotNull(y, "y-coordinate array"); + for (int i = 0; i < x.length; i++) validate(x[i], "x[" + i + "]"); + for (int i = 0; i < y.length; i++) validate(y[i], "y[" + i + "]"); + + int n1 = x.length; + int n2 = y.length; + if (n1 != n2) throw new IllegalArgumentException("arrays must be of the same length"); + int n = n1; + if (n == 0) return; + GeneralPath path = new GeneralPath(); path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0])); for (int i = 0; i < n; i++) @@ -801,13 +1084,30 @@ public void polygon(double[] x, double[] y) { } /** - * Draws a filled polygon with the given (x[i], y[i]) coordinates. + * Draws a filled polygon with the vertices + * (x0, y0), + * (x1, y1), ..., + * (xn–1, yn–1). * - * @param x an array of all the x-coordindates of the polygon - * @param y an array of all the y-coordindates of the polygon + * @param x an array of all the x-coordinates of the polygon + * @param y an array of all the y-coordinates of the polygon + * @throws IllegalArgumentException unless {@code x[]} and {@code y[]} + * are of the same length + * @throws IllegalArgumentException if any coordinate is either NaN or infinite + * @throws IllegalArgumentException if either {@code x[]} or {@code y[]} is {@code null} */ public void filledPolygon(double[] x, double[] y) { - int n = x.length; + validateNotNull(x, "x-coordinate array"); + validateNotNull(y, "y-coordinate array"); + for (int i = 0; i < x.length; i++) validate(x[i], "x[" + i + "]"); + for (int i = 0; i < y.length; i++) validate(y[i], "y[" + i + "]"); + + int n1 = x.length; + int n2 = y.length; + if (n1 != n2) throw new IllegalArgumentException("arrays must be of the same length"); + int n = n1; + if (n == 0) return; + GeneralPath path = new GeneralPath(); path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0])); for (int i = 0; i < n; i++) @@ -823,53 +1123,68 @@ public void filledPolygon(double[] x, double[] y) { * Drawing images. ***************************************************************************/ - private static BufferedImage getImage(String filename) { + // get an image from the given filename + private static Image getImage(String filename) { + if (filename == null) throw new IllegalArgumentException(); - // from a file or URL - try { - URL url = new URL(filename); - return ImageIO.read(url); - } - catch (IOException e) { - // ignore + // to read from file + ImageIcon icon = new ImageIcon(filename); + + // try to read from URL + if (icon.getImageLoadStatus() != MediaTracker.COMPLETE) { + try { + URI uri = new URI(filename); + if (uri.isAbsolute()) { + URL url = uri.toURL(); + icon = new ImageIcon(url); + } + } + catch (MalformedURLException | URISyntaxException e) { + /* not a url */ + } } - // in case file is inside a .jar (classpath relative to StdDraw) - try { - URL url = StdDraw.class.getResource(filename); - return ImageIO.read(url); - } - catch (IOException e) { - // ignore + // in case file is inside a .jar (classpath relative to Draw) + if (icon.getImageLoadStatus() != MediaTracker.COMPLETE) { + URL url = Draw.class.getResource(filename); + if (url != null) + icon = new ImageIcon(url); } // in case file is inside a .jar (classpath relative to root of jar) - try { - URL url = StdDraw.class.getResource("/" + filename); - return ImageIO.read(url); - } - catch (IOException e) { - // ignore + if (icon.getImageLoadStatus() != MediaTracker.COMPLETE) { + URL url = Draw.class.getResource("/" + filename); + if (url == null) throw new IllegalArgumentException("could not read image: '" + filename + "'"); + icon = new ImageIcon(url); } - throw new IllegalArgumentException("image " + filename + " not found"); + + return icon.getImage(); } /** - * Draws picture (gif, jpg, or png) centered on (x, y). + * Draws the specified image centered at (x, y). + * The supported image formats are typically JPEG, PNG, GIF, TIFF, and BMP. + * As an optimization, the picture is cached, so there is no performance + * penalty for redrawing the same image multiple times (e.g., in an animation). + * However, if you change the picture file after drawing it, subsequent + * calls will draw the original picture. * - * @param x the center x-coordinate of the image - * @param y the center y-coordinate of the image + * @param x the center x-coordinate of the image + * @param y the center y-coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" - * @throws IllegalArgumentException if the image is corrupt - * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if the image filename is invalid + * @throws IllegalArgumentException if either {@code x} or {@code y} is either NaN or infinite */ public void picture(double x, double y, String filename) { - if (filename == null) throw new IllegalArgumentException("filename argument is null"); - BufferedImage image = getImage(filename); + validate(x, "x"); + validate(y, "y"); + validateNotNull(filename, "filename"); + + Image image = getImage(filename); double xs = scaleX(x); double ys = scaleY(y); - int ws = image.getWidth(); - int hs = image.getHeight(); + int ws = image.getWidth(null); + int hs = image.getHeight(null); if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt"); offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null); @@ -877,23 +1192,29 @@ public void picture(double x, double y, String filename) { } /** - * Draws picture (gif, jpg, or png) centered on (x, y), + * Draws the specified image centered at (x, y), * rotated given number of degrees. + * The supported image formats are typically JPEG, PNG, GIF, TIFF, and BMP. * - * @param x the center x-coordinate of the image - * @param y the center y-coordinate of the image + * @param x the center x-coordinate of the image + * @param y the center y-coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" * @param degrees is the number of degrees to rotate counterclockwise - * @throws IllegalArgumentException if the image is corrupt + * @throws IllegalArgumentException if the image filename is invalid + * @throws IllegalArgumentException if {@code x}, {@code y}, {@code degrees} is NaN or infinite * @throws IllegalArgumentException if {@code filename} is {@code null} */ public void picture(double x, double y, String filename, double degrees) { - if (filename == null) throw new IllegalArgumentException("filename argument is null"); - BufferedImage image = getImage(filename); + validate(x, "x"); + validate(y, "y"); + validate(degrees, "degrees"); + validateNotNull(filename, "filename"); + + Image image = getImage(filename); double xs = scaleX(x); double ys = scaleY(y); - int ws = image.getWidth(); - int hs = image.getHeight(); + int ws = image.getWidth(null); + int hs = image.getHeight(null); if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt"); offscreen.rotate(Math.toRadians(-degrees), xs, ys); @@ -904,23 +1225,35 @@ public void picture(double x, double y, String filename, double degrees) { } /** - * Draws picture (gif, jpg, or png) centered on (x, y), rescaled to w-by-h. + * Draws the specified image centered at (x, y), + * rescaled to the specified bounding box. + * The supported image formats are typically JPEG, PNG, GIF, TIFF, and BMP. * - * @param x the center x coordinate of the image - * @param y the center y coordinate of the image + * @param x the center x-coordinate of the image + * @param y the center y-coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" - * @param w the width of the image - * @param h the height of the image - * @throws IllegalArgumentException if the image is corrupt + * @param scaledWidth the width of the scaled image (in screen coordinates) + * @param scaledHeight the height of the scaled image (in screen coordinates) + * @throws IllegalArgumentException if either {@code scaledWidth} + * or {@code scaledHeight} is negative + * @throws IllegalArgumentException if the image filename is invalid + * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite * @throws IllegalArgumentException if {@code filename} is {@code null} */ - public void picture(double x, double y, String filename, double w, double h) { - if (filename == null) throw new IllegalArgumentException("filename argument is null"); + public void picture(double x, double y, String filename, double scaledWidth, double scaledHeight) { + validate(x, "x"); + validate(y, "y"); + validate(scaledWidth, "scaled width"); + validate(scaledHeight, "scaled height"); + validateNotNull(filename, "filename"); + validateNonnegative(scaledWidth, "scaled width"); + validateNonnegative(scaledHeight, "scaled height"); + Image image = getImage(filename); double xs = scaleX(x); double ys = scaleY(y); - double ws = factorX(w); - double hs = factorY(h); + double ws = factorX(scaledWidth); + double hs = factorY(scaledHeight); if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt"); if (ws <= 1 && hs <= 1) pixel(x, y); else { @@ -934,25 +1267,35 @@ public void picture(double x, double y, String filename, double w, double h) { /** - * Draws picture (gif, jpg, or png) centered on (x, y), rotated - * given number of degrees, rescaled to w-by-h. + * Draws the specified image centered at (x, y), rotated + * given number of degrees, and rescaled to the specified bounding box. + * The supported image formats are typically JPEG, PNG, GIF, TIFF, and BMP. * - * @param x the center x-coordinate of the image - * @param y the center y-coordinate of the image + * @param x the center x-coordinate of the image + * @param y the center y-coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" - * @param w the width of the image - * @param h the height of the image + * @param scaledWidth the width of the scaled image (in screen coordinates) + * @param scaledHeight the height of the scaled image (in screen coordinates) * @param degrees is the number of degrees to rotate counterclockwise - * @throws IllegalArgumentException if the image is corrupt - * @throws IllegalArgumentException if {@code filename} is {@code null} - */ - public void picture(double x, double y, String filename, double w, double h, double degrees) { - if (filename == null) throw new IllegalArgumentException("filename argument is null"); + * @throws IllegalArgumentException if either {@code scaledWidth} + * or {@code scaledHeight} is negative + * @throws IllegalArgumentException if the image filename is invalid + */ + public void picture(double x, double y, String filename, double scaledWidth, double scaledHeight, double degrees) { + validate(x, "x"); + validate(y, "y"); + validate(scaledWidth, "scaled width"); + validate(scaledHeight, "scaled height"); + validate(degrees, "degrees"); + validateNotNull(filename, "filename"); + validateNonnegative(scaledWidth, "scaled width"); + validateNonnegative(scaledHeight, "scaled height"); + Image image = getImage(filename); double xs = scaleX(x); double ys = scaleY(y); - double ws = factorX(w); - double hs = factorY(h); + double ws = factorX(scaledWidth); + double hs = factorY(scaledHeight); if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt"); if (ws <= 1 && hs <= 1) pixel(x, y); @@ -972,63 +1315,104 @@ public void picture(double x, double y, String filename, double w, double h, dou ***************************************************************************/ /** - * Writes the given text string in the current font, centered on (x, y). + * Writes the given text string in the current font, centered at (x, y). * - * @param x the center x-coordinate of the text - * @param y the center y-coordinate of the text - * @param s the text + * @param x the center x-coordinate of the text + * @param y the center y-coordinate of the text + * @param text the text to write + * @throws IllegalArgumentException if {@code text} is {@code null} + * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite */ - public void text(double x, double y, String s) { + public void text(double x, double y, String text) { + validate(x, "x"); + validate(y, "y"); + validateNotNull(text, "text"); + offscreen.setFont(font); FontMetrics metrics = offscreen.getFontMetrics(); double xs = scaleX(x); double ys = scaleY(y); - int ws = metrics.stringWidth(s); + int ws = metrics.stringWidth(text); int hs = metrics.getDescent(); - offscreen.drawString(s, (float) (xs - ws/2.0), (float) (ys + hs)); + offscreen.drawString(text, (float) (xs - ws/2.0), (float) (ys + hs)); draw(); } /** - * Writes the given text string in the current font, centered on (x, y) and + * Writes the given text string in the current font, centered at (x, y) and * rotated by the specified number of degrees. - * - * @param x the center x-coordinate of the text - * @param y the center y-coordinate of the text - * @param s the text - * @param degrees is the number of degrees to rotate counterclockwise + * @param x the center x-coordinate of the text + * @param y the center y-coordinate of the text + * @param text the text to write + * @param degrees is the number of degrees to rotate counterclockwise + * @throws IllegalArgumentException if {@code text} is {@code null} + * @throws IllegalArgumentException if {@code x}, {@code y}, or {@code degrees} is either NaN or infinite */ - public void text(double x, double y, String s, double degrees) { + public void text(double x, double y, String text, double degrees) { + validate(x, "x"); + validate(y, "y"); + validate(degrees, "degrees"); + validateNotNull(text, "text"); + double xs = scaleX(x); double ys = scaleY(y); offscreen.rotate(Math.toRadians(-degrees), xs, ys); - text(x, y, s); + text(x, y, text); offscreen.rotate(Math.toRadians(+degrees), xs, ys); } /** - * Writes the given text string in the current font, left-aligned at (x, y). - * - * @param x the x-coordinate of the text - * @param y the y-coordinate of the text - * @param s the text + * Writes the given text string in the current font, left-aligned at (x, y). + * @param x the x-coordinate of the text + * @param y the y-coordinate of the text + * @param text the text + * @throws IllegalArgumentException if {@code text} is {@code null} + * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite */ - public void textLeft(double x, double y, String s) { + public void textLeft(double x, double y, String text) { + validate(x, "x"); + validate(y, "y"); + validateNotNull(text, "text"); + offscreen.setFont(font); FontMetrics metrics = offscreen.getFontMetrics(); double xs = scaleX(x); double ys = scaleY(y); - // int ws = metrics.stringWidth(s); + // int ws = metrics.stringWidth(text); int hs = metrics.getDescent(); - offscreen.drawString(s, (float) xs, (float) (ys + hs)); + offscreen.drawString(text, (float) xs, (float) (ys + hs)); draw(); } + /** + * Writes the given text string in the current font, right-aligned at (x, y). + * + * @param x the x-coordinate of the text + * @param y the y-coordinate of the text + * @param text the text to write + * @throws IllegalArgumentException if {@code text} is {@code null} + * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite + */ + public void textRight(double x, double y, String text) { + validate(x, "x"); + validate(y, "y"); + validateNotNull(text, "text"); + + offscreen.setFont(font); + FontMetrics metrics = offscreen.getFontMetrics(); + double xs = scaleX(x); + double ys = scaleY(y); + int ws = metrics.stringWidth(text); + int hs = metrics.getDescent(); + offscreen.drawString(text, (float) (xs - ws), (float) (ys + hs)); + draw(); + } /** * Copies the offscreen buffer to the onscreen buffer, pauses for t milliseconds * and enables double buffering. * @param t number of milliseconds + * @throws IllegalArgumentException if {@code t} is negative * @deprecated replaced by {@link #enableDoubleBuffering()}, {@link #show()}, and {@link #pause(int t)} */ @Deprecated @@ -1041,6 +1425,7 @@ public void show(int t) { /** * Pause for t milliseconds. This method is intended to support computer animations. * @param t number of milliseconds + * @throws IllegalArgumentException if {@code t} is negative */ public void pause(int t) { try { @@ -1056,7 +1441,15 @@ public void pause(int t) { * this method unless double buffering is enabled. */ public void show() { + onscreen.setBackground(backgroundColor); + onscreen.clearRect(0, 0, 2*width, 2*height); onscreen.drawImage(offscreenImage, 0, 0, null); + + // make frame visible upon first call to show() + if (frame.isVisible() != isJFrameVisible) { + frame.setVisible(isJFrameVisible); + } + frame.repaint(); } @@ -1066,9 +1459,9 @@ private void draw() { } /** - * Enable double buffering. All subsequent calls to + * Enable double buffering. All subsequent calls to * drawing methods such as {@code line()}, {@code circle()}, - * and {@code square()} will be deffered until the next call + * and {@code square()} will be deferred until the next call * to show(). Useful for animations. */ public void enableDoubleBuffering() { @@ -1076,7 +1469,7 @@ public void enableDoubleBuffering() { } /** - * Disable double buffering. All subsequent calls to + * Disable double buffering. All subsequent calls to * drawing methods such as {@code line()}, {@code circle()}, * and {@code square()} will be displayed on screen when called. * This is the default. @@ -1086,60 +1479,62 @@ public void disableDoubleBuffering() { } /** - * Saves this drawing to a file. + * Saves the drawing to a file in a supported file format + * (typically JPEG, PNG, GIF, TIFF, and BMP). + * The filetype extension must be {@code .jpg}, {@code .png}, {@code .gif}, + * {@code .bmp}, or {@code .tif}. * - * @param filename the name of the file (with suffix png, jpg, or gif) + * @param filename the name of the file + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if {@code filename} is the empty string + * @throws IllegalArgumentException if {@code filename} has invalid filetype extension + * @throws IllegalArgumentException if cannot write the file {@code filename} */ public void save(String filename) { + validateNotNull(filename, "filename"); + if (filename.length() == 0) { + throw new IllegalArgumentException("argument to save() is the empty string"); + } + File file = new File(filename); String suffix = filename.substring(filename.lastIndexOf('.') + 1); - - // png files - if ("png".equalsIgnoreCase(suffix)) { - try { - ImageIO.write(offscreenImage, suffix, file); - } - catch (IOException e) { - e.printStackTrace(); - } + if (!filename.contains(".") || suffix.length() == 0) { + throw new IllegalArgumentException("the filename '" + filename + "' has no file extension, such as .jpg or .png"); } - // need to change from ARGB to RGB for jpeg - // reference: http://archives.java.sun.com/cgi-bin/wa?A2=ind0404&L=java2d-interest&D=0&P=2727 - else if ("jpg".equalsIgnoreCase(suffix)) { - WritableRaster raster = offscreenImage.getRaster(); - WritableRaster newRaster; - newRaster = raster.createWritableChild(0, 0, width, height, 0, 0, new int[] {0, 1, 2}); - DirectColorModel cm = (DirectColorModel) offscreenImage.getColorModel(); - DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), - cm.getRedMask(), - cm.getGreenMask(), - cm.getBlueMask()); - BufferedImage rgbBuffer = new BufferedImage(newCM, newRaster, false, null); - try { - ImageIO.write(rgbBuffer, suffix, file); - } - catch (IOException e) { - e.printStackTrace(); - } - } + try { + // if the file format supports transparency (such as PNG or GIF) + if (ImageIO.write(onscreenImage, suffix, file)) return; - else { - System.out.println("Invalid image file type: " + suffix); + // if the file format does not support transparency (such as JPEG or BMP) + BufferedImage saveImage = new BufferedImage(2*width, 2*height, BufferedImage.TYPE_INT_RGB); + saveImage.createGraphics().drawImage(onscreenImage, 0, 0, Color.WHITE, null); + if (ImageIO.write(saveImage, suffix, file)) return; + + // failed to save the file; probably wrong format + throw new IllegalArgumentException("the filetype '" + suffix + "' is not supported"); + } + catch (IOException e) { + throw new IllegalArgumentException("could not write the file + " + filename, e); } } - /** * This method cannot be called directly. */ @Override - public void actionPerformed(ActionEvent e) { + public void actionPerformed(ActionEvent event) { FileDialog chooser = new FileDialog(frame, "Use a .png or .jpg extension", FileDialog.SAVE); chooser.setVisible(true); - String filename = chooser.getFile(); - if (filename != null) { - save(chooser.getDirectory() + File.separator + chooser.getFile()); + String selectedDirectory = chooser.getDirectory(); + String selectedFilename = chooser.getFile(); + if (selectedDirectory != null && selectedFilename != null) { + try { + save(selectedDirectory + selectedFilename); + } + catch (IllegalArgumentException e) { + System.err.println(e.getMessage()); + } } } @@ -1155,13 +1550,9 @@ public void actionPerformed(ActionEvent e) { * @param listener the {\tt DrawListener} argument */ public void addListener(DrawListener listener) { - // ensure there is a window for listenting to events + // ensure there is a window for listening to events show(); listeners.add(listener); - frame.addKeyListener(this); - frame.addMouseListener(this); - frame.addMouseMotionListener(this); - frame.setFocusable(true); } @@ -1224,7 +1615,7 @@ public double mouseY() { * This method cannot be called directly. */ @Override - public void mouseClicked(MouseEvent e) { + public void mouseEntered(MouseEvent event) { // this body is intentionally left empty } @@ -1232,7 +1623,7 @@ public void mouseClicked(MouseEvent e) { * This method cannot be called directly. */ @Override - public void mouseEntered(MouseEvent e) { + public void mouseExited(MouseEvent event) { // this body is intentionally left empty } @@ -1240,63 +1631,67 @@ public void mouseEntered(MouseEvent e) { * This method cannot be called directly. */ @Override - public void mouseExited(MouseEvent e) { - // this body is intentionally left empty + public void mousePressed(MouseEvent event) { + synchronized (mouseLock) { + mouseX = userX(event.getX()); + mouseY = userY(event.getY()); + isMousePressed = true; + } + if (event.getButton() == MouseEvent.BUTTON1) { + for (DrawListener listener : listeners) + listener.mousePressed(userX(event.getX()), userY(event.getY())); + } + } /** * This method cannot be called directly. */ @Override - public void mousePressed(MouseEvent e) { + public void mouseReleased(MouseEvent event) { synchronized (mouseLock) { - mouseX = userX(e.getX()); - mouseY = userY(e.getY()); - isMousePressed = true; + isMousePressed = false; } - if (e.getButton() == MouseEvent.BUTTON1) { + if (event.getButton() == MouseEvent.BUTTON1) { for (DrawListener listener : listeners) - listener.mousePressed(userX(e.getX()), userY(e.getY())); + listener.mouseReleased(userX(event.getX()), userY(event.getY())); } - } /** * This method cannot be called directly. */ @Override - public void mouseReleased(MouseEvent e) { - synchronized (mouseLock) { - isMousePressed = false; - } - if (e.getButton() == MouseEvent.BUTTON1) { + public void mouseClicked(MouseEvent event) { + if (event.getButton() == MouseEvent.BUTTON1) { for (DrawListener listener : listeners) - listener.mouseReleased(userX(e.getX()), userY(e.getY())); + listener.mouseClicked(userX(event.getX()), userY(event.getY())); } } + /** * This method cannot be called directly. */ @Override - public void mouseDragged(MouseEvent e) { + public void mouseDragged(MouseEvent event) { synchronized (mouseLock) { - mouseX = userX(e.getX()); - mouseY = userY(e.getY()); + mouseX = userX(event.getX()); + mouseY = userY(event.getY()); } // doesn't seem to work if a button is specified for (DrawListener listener : listeners) - listener.mouseDragged(userX(e.getX()), userY(e.getY())); + listener.mouseDragged(userX(event.getX()), userY(event.getY())); } /** * This method cannot be called directly. */ @Override - public void mouseMoved(MouseEvent e) { + public void mouseMoved(MouseEvent event) { synchronized (mouseLock) { - mouseX = userX(e.getX()); - mouseY = userY(e.getY()); + mouseX = userX(event.getX()); + mouseY = userY(event.getY()); } } @@ -1349,46 +1744,104 @@ public boolean isKeyPressed(int keycode) { * This method cannot be called directly. */ @Override - public void keyTyped(KeyEvent e) { + public void keyTyped(KeyEvent event) { synchronized (keyLock) { - keysTyped.addFirst(e.getKeyChar()); + keysTyped.addFirst(event.getKeyChar()); } // notify all listeners for (DrawListener listener : listeners) - listener.keyTyped(e.getKeyChar()); + listener.keyTyped(event.getKeyChar()); } /** * This method cannot be called directly. */ @Override - public void keyPressed(KeyEvent e) { + public void keyPressed(KeyEvent event) { synchronized (keyLock) { - keysDown.add(e.getKeyCode()); + keysDown.add(event.getKeyCode()); } // notify all listeners for (DrawListener listener : listeners) - listener.keyPressed(e.getKeyCode()); + listener.keyPressed(event.getKeyCode()); } /** * This method cannot be called directly. */ @Override - public void keyReleased(KeyEvent e) { + public void keyReleased(KeyEvent event) { synchronized (keyLock) { - keysDown.remove(e.getKeyCode()); + keysDown.remove(event.getKeyCode()); } // notify all listeners for (DrawListener listener : listeners) - listener.keyPressed(e.getKeyCode()); + listener.keyReleased(event.getKeyCode()); } + /*************************************************************************** + * Timer events. + ***************************************************************************/ + + /** + * Sets a timer that calls update() method a specified number of times + * per second. + *

+ * @param callsPerSecond calls per second + */ + public void enableTimer(int callsPerSecond) { + disableTimer(); + timer = new Timer(); + timer.schedule(new MyTimerTask(), 0, (int) Math.round(1000.0 / callsPerSecond)); + } + public void disableTimer() { + if (timer != null) timer.cancel(); + } + private class MyTimerTask extends TimerTask { + public void run() { + for (DrawListener listener : listeners) + listener.update(); + } + } + + /*************************************************************************** + * For improved resolution on Mac Retina displays. + ***************************************************************************/ + + private static class RetinaImageIcon extends ImageIcon { + + public RetinaImageIcon(Image image) { + super(image); + } + + public int getIconWidth() { + return super.getIconWidth() / 2; + } + + /** + * Returns the height of the icon. + * + * @return the height in pixels of this icon + */ + public int getIconHeight() { + return super.getIconHeight() / 2; + } + + public synchronized void paintIcon(Component c, Graphics g, int x, int y) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2.scale(0.5, 0.5); + super.paintIcon(c, g2, x * 2, y * 2); + g2.dispose(); + } + } /** * Test client. @@ -1398,36 +1851,38 @@ public void keyReleased(KeyEvent e) { public static void main(String[] args) { // create one drawing window - Draw draw1 = new Draw("Test client 1"); - draw1.square(.2, .8, .1); - draw1.filledSquare(.8, .8, .2); - draw1.circle(.8, .2, .2); + Draw draw1 = new Draw(); + draw1.setTitle("Test client 1"); + draw1.square(0.2, 0.8, 0.1); + draw1.filledSquare(0.8, 0.8, 0.2); + draw1.circle(0.8, 0.2, 0.2); draw1.setPenColor(Draw.MAGENTA); - draw1.setPenRadius(.02); - draw1.arc(.8, .2, .1, 200, 45); + draw1.setPenRadius(0.02); + draw1.arc(0.8, 0.2, 0.1, 200, 45); // create another one - Draw draw2 = new Draw("Test client 2"); + Draw draw2 = new Draw(); draw2.setCanvasSize(900, 200); + draw2.setTitle("Test client 2"); // draw a blue diamond draw2.setPenRadius(); draw2.setPenColor(Draw.BLUE); - double[] x = { .1, .2, .3, .2 }; - double[] y = { .2, .3, .2, .1 }; + double[] x = { 0.1, 0.2, 0.3, 0.2 }; + double[] y = { 0.2, 0.3, 0.2, 0.1 }; draw2.filledPolygon(x, y); // text draw2.setPenColor(Draw.BLACK); - draw2.text(0.2, 0.5, "bdfdfdfdlack text"); + draw2.text(0.2, 0.5, "black text"); draw2.setPenColor(Draw.WHITE); - draw2.text(0.8, 0.8, "white text"); + draw2.text(0.2, 0.2, "white text"); } } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/DrawListener.java b/src/main/java/edu/princeton/cs/algs4/DrawListener.java index 3cfc4f4d8..fdd2468b2 100644 --- a/src/main/java/edu/princeton/cs/algs4/DrawListener.java +++ b/src/main/java/edu/princeton/cs/algs4/DrawListener.java @@ -8,6 +8,22 @@ package edu.princeton.cs.algs4; +/** + * The DrawListener interface provides a basic capability for + * responding to keyboard in mouse events from {@link Draw} via callbacks. + * You can see some examples in + * Section 3.6. + * + *

+ * For additional documentation, see + * Section 3.1 of + * Computer Science: An Interdisciplinary Approach + * by Robert Sedgewick and Kevin Wayne. + * + * @author Robert Sedgewick + * @author Kevin Wayne + */ + public interface DrawListener { /** @@ -16,7 +32,9 @@ public interface DrawListener { * @param x the x-coordinate of the mouse * @param y the y-coordinate of the mouse */ - void mousePressed(double x, double y); + default void mousePressed(double x, double y) { + // does nothing by default + } /** * Invoked when the mouse has been dragged. @@ -24,7 +42,9 @@ public interface DrawListener { * @param x the x-coordinate of the mouse * @param y the y-coordinate of the mouse */ - void mouseDragged(double x, double y); + default void mouseDragged(double x, double y) { + // does nothing by default + } /** * Invoked when the mouse has been released. @@ -32,32 +52,63 @@ public interface DrawListener { * @param x the x-coordinate of the mouse * @param y the y-coordinate of the mouse */ - void mouseReleased(double x, double y); + default void mouseReleased(double x, double y) { + // does nothing by default + } + + /** + * Invoked when the mouse has been clicked (pressed and released). + * A mouse click is triggered only if the user presses a mouse button + * and then releases it quickly, without moving the mouse. + * It does not work with touch events. + * The {@link mousePressed} method is generally preferred for + * detecting mouse clicks. + * + * @param x the x-coordinate of the mouse + * @param y the y-coordinate of the mouse + */ + default void mouseClicked(double x, double y) { + // does nothing by default + } /** * Invoked when a key has been typed. * * @param c the character typed */ - void keyTyped(char c); + default void keyTyped(char c) { + // does nothing by default + } /** * Invoked when a key has been pressed. * * @param keycode the key combination pressed */ - void keyPressed(int keycode); + default void keyPressed(int keycode) { + // does nothing by default + } /** * Invoked when a key has been released. * * @param keycode the key combination released */ - void keyReleased(int keycode); + default void keyReleased(int keycode) { + // does nothing by default + } + + /** + * Gets called at regular time intervals. + */ + default void update() { + // does nothing by default + } + } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Edge.java b/src/main/java/edu/princeton/cs/algs4/Edge.java index 984f3b87b..3983530c9 100644 --- a/src/main/java/edu/princeton/cs/algs4/Edge.java +++ b/src/main/java/edu/princeton/cs/algs4/Edge.java @@ -10,7 +10,7 @@ package edu.princeton.cs.algs4; /** - * The {@code Edge} class represents a weighted edge in an + * The {@code Edge} class represents a weighted edge in an * {@link EdgeWeightedGraph}. Each edge consists of two integers * (naming the two vertices) and a real-value weight. The data type * provides methods for accessing the two endpoints of the edge and @@ -23,7 +23,7 @@ * @author Robert Sedgewick * @author Kevin Wayne */ -public class Edge implements Comparable { +public class Edge implements Comparable { private final int v; private final int w; @@ -36,13 +36,13 @@ public class Edge implements Comparable { * @param v one vertex * @param w the other vertex * @param weight the weight of this edge - * @throws IllegalArgumentException if either {@code v} or {@code w} + * @throws IllegalArgumentException if either {@code v} or {@code w} * is a negative integer * @throws IllegalArgumentException if {@code weight} is {@code NaN} */ public Edge(int v, int w, double weight) { - if (v < 0) throw new IllegalArgumentException("vertex index must be a nonnegative integer"); - if (w < 0) throw new IllegalArgumentException("vertex index must be a nonnegative integer"); + if (v < 0) throw new IllegalArgumentException("vertex index must be a non-negative integer"); + if (w < 0) throw new IllegalArgumentException("vertex index must be a non-negative integer"); if (Double.isNaN(weight)) throw new IllegalArgumentException("Weight is NaN"); this.v = v; this.w = w; @@ -117,7 +117,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/EdgeWeightedDigraph.java b/src/main/java/edu/princeton/cs/algs4/EdgeWeightedDigraph.java index 7cdff8069..b5885746a 100644 --- a/src/main/java/edu/princeton/cs/algs4/EdgeWeightedDigraph.java +++ b/src/main/java/edu/princeton/cs/algs4/EdgeWeightedDigraph.java @@ -2,9 +2,9 @@ * Compilation: javac EdgeWeightedDigraph.java * Execution: java EdgeWeightedDigraph digraph.txt * Dependencies: Bag.java DirectedEdge.java - * Data files: https://algs4.cs.princeton.edu/44st/tinyEWD.txt - * https://algs4.cs.princeton.edu/44st/mediumEWD.txt - * https://algs4.cs.princeton.edu/44st/largeEWD.txt + * Data files: https://algs4.cs.princeton.edu/44sp/tinyEWD.txt + * https://algs4.cs.princeton.edu/44sp/mediumEWD.txt + * https://algs4.cs.princeton.edu/44sp/largeEWD.txt * * An edge-weighted digraph, implemented using adjacency lists. * @@ -12,21 +12,30 @@ package edu.princeton.cs.algs4; +import java.util.NoSuchElementException; + /** - * The {@code EdgeWeightedDigraph} class represents a edge-weighted + * The {@code EdgeWeightedDigraph} class represents an edge-weighted * digraph of vertices named 0 through V - 1, where each * directed edge is of type {@link DirectedEdge} and has a real-valued weight. * It supports the following two primary operations: add a directed edge - * to the digraph and iterate over all of edges incident from a given vertex. - * It also provides - * methods for returning the number of vertices V and the number - * of edges E. Parallel edges and self-loops are permitted. + * to the digraph and iterate over all edges incident from a given vertex. + * It also provides methods for returning the indegree or outdegree of a + * vertex, the number of vertices V in the digraph, and + * the number of edges E in the digraph. + * Parallel edges and self-loops are permitted. *

- * This implementation uses an adjacency-lists representation, which + * This implementation uses an adjacency-lists representation, which * is a vertex-indexed array of {@link Bag} objects. - * All operations take constant time (in the worst case) except - * iterating over the edges incident from a given vertex, which takes - * time proportional to the number of such edges. + * It uses Θ(E + V) space, where E is + * the number of edges and V is the number of vertices. + * All instance methods take Θ(1) time. (Though, iterating over + * the edges returned by {@link #adj(int)} takes time proportional + * to the outdegree of the vertex.) + * Constructing an empty edge-weighted digraph with V vertices + * takes Θ(V) time; constructing an edge-weighted digraph + * with E edges and V vertices takes + * Θ(E + V) time. *

* For additional documentation, * see Section 4.4 of @@ -42,7 +51,7 @@ public class EdgeWeightedDigraph { private int E; // number of edges in this digraph private Bag[] adj; // adj[v] = adjacency list for vertex v private int[] indegree; // indegree[v] = indegree of vertex v - + /** * Initializes an empty edge-weighted digraph with {@code V} vertices and 0 edges. * @@ -50,7 +59,7 @@ public class EdgeWeightedDigraph { * @throws IllegalArgumentException if {@code V < 0} */ public EdgeWeightedDigraph(int V) { - if (V < 0) throw new IllegalArgumentException("Number of vertices in a Digraph must be nonnegative"); + if (V < 0) throw new IllegalArgumentException("Number of vertices in a Digraph must be non-negative"); this.V = V; this.E = 0; this.indegree = new int[V]; @@ -69,17 +78,17 @@ public EdgeWeightedDigraph(int V) { */ public EdgeWeightedDigraph(int V, int E) { this(V); - if (E < 0) throw new IllegalArgumentException("Number of edges in a Digraph must be nonnegative"); + if (E < 0) throw new IllegalArgumentException("Number of edges in a Digraph must be non-negative"); for (int i = 0; i < E; i++) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); - double weight = 0.01 * StdRandom.uniform(100); + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); + double weight = 0.01 * StdRandom.uniformInt(100); DirectedEdge e = new DirectedEdge(v, w, weight); addEdge(e); } } - /** + /** * Initializes an edge-weighted digraph from the specified input stream. * The format is the number of vertices V, * followed by the number of edges E, @@ -87,20 +96,34 @@ public EdgeWeightedDigraph(int V, int E) { * with each entry separated by whitespace. * * @param in the input stream + * @throws IllegalArgumentException if {@code in} is {@code null} * @throws IllegalArgumentException if the endpoints of any edge are not in prescribed range * @throws IllegalArgumentException if the number of vertices or edges is negative */ public EdgeWeightedDigraph(In in) { - this(in.readInt()); - int E = in.readInt(); - if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative"); - for (int i = 0; i < E; i++) { - int v = in.readInt(); - int w = in.readInt(); - validateVertex(v); - validateVertex(w); - double weight = in.readDouble(); - addEdge(new DirectedEdge(v, w, weight)); + if (in == null) throw new IllegalArgumentException("argument is null"); + try { + this.V = in.readInt(); + if (V < 0) throw new IllegalArgumentException("number of vertices in a Digraph must be non-negative"); + indegree = new int[V]; + adj = (Bag[]) new Bag[V]; + for (int v = 0; v < V; v++) { + adj[v] = new Bag(); + } + + int E = in.readInt(); + if (E < 0) throw new IllegalArgumentException("Number of edges must be non-negative"); + for (int i = 0; i < E; i++) { + int v = in.readInt(); + int w = in.readInt(); + validateVertex(v); + validateVertex(w); + double weight = in.readDouble(); + addEdge(new DirectedEdge(v, w, weight)); + } + } + catch (NoSuchElementException e) { + throw new IllegalArgumentException("invalid input format in EdgeWeightedDigraph constructor", e); } } @@ -221,7 +244,7 @@ public Iterable edges() { } } return list; - } + } /** * Returns a string representation of this edge-weighted digraph. @@ -242,6 +265,43 @@ public String toString() { return s.toString(); } + /** + * Returns a string representation of this edge-weighted digraph in DOT format, + * suitable for visualization with Graphviz. + * + * To visualize the graph, install Graphviz (e.g., "brew install graphviz"). + * Then use one of the graph visualization tools + * - dot (hierarchical or layer drawing) + * - neato (spring model) + * - fdp (force-directed placement) + * - sfdp (scalable force-directed placement) + * - twopi (radial layout) + * + * For example, the following commands will create graph drawings in SVG + * and PDF formats + * - dot input.dot -Tsvg -o output.svg + * - dot input.dot -Tpdf -o output.pdf + * + * To change the digraph attributes (e.g., vertex and edge shapes, arrows, colors) + * in the DOT format, see https://graphviz.org/doc/info/lang.html + * + * @return a string representation of this edge-weighted digraph in DOT format + */ + public String toDot() { + StringBuilder s = new StringBuilder(); + s.append("digraph {" + NEWLINE); + s.append("node[shape=circle, style=filled, fixedsize=true, width=0.3, fontsize=\"10pt\"]" + NEWLINE); + s.append("edge[arrowhead=normal, fontsize=\"9pt\"]" + NEWLINE); + for (int v = 0; v < V; v++) { + for (DirectedEdge e : adj[v]) { + int w = e.to(); + s.append(v + " -> " + w + " [label=\"" + e.weight() + "\"]" + NEWLINE); + } + } + s.append("}" + NEWLINE); + return s.toString(); + } + /** * Unit tests the {@code EdgeWeightedDigraph} data type. * @@ -256,7 +316,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/EdgeWeightedDirectedCycle.java b/src/main/java/edu/princeton/cs/algs4/EdgeWeightedDirectedCycle.java index 1d2797690..85b2c88f0 100644 --- a/src/main/java/edu/princeton/cs/algs4/EdgeWeightedDirectedCycle.java +++ b/src/main/java/edu/princeton/cs/algs4/EdgeWeightedDirectedCycle.java @@ -12,26 +12,26 @@ package edu.princeton.cs.algs4; /** - * The {@code EdgeWeightedDirectedCycle} class represents a data type for + * The {@code EdgeWeightedDirectedCycle} class represents a data type for * determining whether an edge-weighted digraph has a directed cycle. * The hasCycle operation determines whether the edge-weighted * digraph has a directed cycle and, if so, the cycle operation * returns one. *

- * This implementation uses depth-first search. - * The constructor takes time proportional to V + E - * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * Afterwards, the hasCycle operation takes constant time; - * the cycle operation takes time proportional - * to the length of the cycle. + * This implementation uses depth-first search. + * The constructor takes Θ(V + E) time in the + * worst case, where V is the number of vertices and + * E is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the + * edge-weighted digraph). *

- * See {@link Topological} to compute a topological order if the edge-weighted - * digraph is acyclic. + * See {@link Topological} to compute a topological order if the + * edge-weighted digraph is acyclic. *

- * For additional documentation, - * see Section 4.4 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 4.4 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -130,6 +130,13 @@ private boolean check() { last = e; } + // cycle() contains no edges + if (first == null || last == null) { + System.err.printf("cycle contains no edges\n"); + return false; + } + + // first and last edges in cycle are not incident if (last.to() != first.from()) { System.err.printf("cycle edges %s and %s not incident\n", last, first); return false; @@ -159,18 +166,18 @@ public static void main(String[] args) { for (int i = 0; i < E; i++) { int v, w; do { - v = StdRandom.uniform(V); - w = StdRandom.uniform(V); + v = StdRandom.uniformInt(V); + w = StdRandom.uniformInt(V); } while (v >= w); - double weight = StdRandom.uniform(); + double weight = StdRandom.uniformDouble(0.0, 1.0); G.addEdge(new DirectedEdge(v, w, weight)); } // add F extra edges for (int i = 0; i < F; i++) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); - double weight = StdRandom.uniform(0.0, 1.0); + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); + double weight = StdRandom.uniformDouble(0.0, 1.0); G.addEdge(new DirectedEdge(v, w, weight)); } @@ -186,7 +193,7 @@ public static void main(String[] args) { StdOut.println(); } - // or give topologial sort + // or give topological sort else { StdOut.println("No directed cycle"); } @@ -195,7 +202,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/EdgeWeightedGraph.java b/src/main/java/edu/princeton/cs/algs4/EdgeWeightedGraph.java index 99de0e21b..5c74ee718 100644 --- a/src/main/java/edu/princeton/cs/algs4/EdgeWeightedGraph.java +++ b/src/main/java/edu/princeton/cs/algs4/EdgeWeightedGraph.java @@ -9,14 +9,14 @@ * An edge-weighted undirected graph, implemented using adjacency lists. * Parallel edges and self-loops are permitted. * - * % java EdgeWeightedGraph tinyEWG.txt + * % java EdgeWeightedGraph tinyEWG.txt * 8 16 - * 0: 6-0 0.58000 0-2 0.26000 0-4 0.38000 0-7 0.16000 - * 1: 1-3 0.29000 1-2 0.36000 1-7 0.19000 1-5 0.32000 - * 2: 6-2 0.40000 2-7 0.34000 1-2 0.36000 0-2 0.26000 2-3 0.17000 - * 3: 3-6 0.52000 1-3 0.29000 2-3 0.17000 - * 4: 6-4 0.93000 0-4 0.38000 4-7 0.37000 4-5 0.35000 - * 5: 1-5 0.32000 5-7 0.28000 4-5 0.35000 + * 0: 6-0 0.58000 0-2 0.26000 0-4 0.38000 0-7 0.16000 + * 1: 1-3 0.29000 1-2 0.36000 1-7 0.19000 1-5 0.32000 + * 2: 6-2 0.40000 2-7 0.34000 1-2 0.36000 0-2 0.26000 2-3 0.17000 + * 3: 3-6 0.52000 1-3 0.29000 2-3 0.17000 + * 4: 6-4 0.93000 0-4 0.38000 4-7 0.37000 4-5 0.35000 + * 5: 1-5 0.32000 5-7 0.28000 4-5 0.35000 * 6: 6-4 0.93000 6-0 0.58000 3-6 0.52000 6-2 0.40000 * 7: 2-7 0.34000 1-7 0.19000 0-7 0.16000 5-7 0.28000 4-7 0.37000 * @@ -24,23 +24,32 @@ package edu.princeton.cs.algs4; +import java.util.NoSuchElementException; + /** * The {@code EdgeWeightedGraph} class represents an edge-weighted * graph of vertices named 0 through V – 1, where each * undirected edge is of type {@link Edge} and has a real-valued weight. * It supports the following two primary operations: add an edge to the graph, * iterate over all of the edges incident to a vertex. It also provides - * methods for returning the number of vertices V and the number - * of edges E. Parallel edges and self-loops are permitted. + * methods for returning the degree of a vertex, the number of vertices + * V in the graph, and the number of edges E in the graph. + * Parallel edges and self-loops are permitted. * By convention, a self-loop v-v appears in the * adjacency list of v twice and contributes two to the degree * of v. *

- * This implementation uses an adjacency-lists representation, which + * This implementation uses an adjacency-lists representation, which * is a vertex-indexed array of {@link Bag} objects. - * All operations take constant time (in the worst case) except - * iterating over the edges incident to a given vertex, which takes - * time proportional to the number of such edges. + * It uses Θ(E + V) space, where E is + * the number of edges and V is the number of vertices. + * All instance methods take Θ(1) time. (Though, iterating over + * the edges returned by {@link #adj(int)} takes time proportional + * to the degree of the vertex.) + * Constructing an empty edge-weighted graph with V vertices takes + * Θ(V) time; constructing an edge-weighted graph with + * E edges and V vertices takes + * Θ(E + V) time. *

* For additional documentation, * see Section 4.3 of @@ -55,7 +64,7 @@ public class EdgeWeightedGraph { private final int V; private int E; private Bag[] adj; - + /** * Initializes an empty edge-weighted graph with {@code V} vertices and 0 edges. * @@ -63,7 +72,7 @@ public class EdgeWeightedGraph { * @throws IllegalArgumentException if {@code V < 0} */ public EdgeWeightedGraph(int V) { - if (V < 0) throw new IllegalArgumentException("Number of vertices must be nonnegative"); + if (V < 0) throw new IllegalArgumentException("Number of vertices must be non-negative"); this.V = V; this.E = 0; adj = (Bag[]) new Bag[V]; @@ -82,17 +91,17 @@ public EdgeWeightedGraph(int V) { */ public EdgeWeightedGraph(int V, int E) { this(V); - if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative"); + if (E < 0) throw new IllegalArgumentException("Number of edges must be non-negative"); for (int i = 0; i < E; i++) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); - double weight = Math.round(100 * StdRandom.uniform()) / 100.0; + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); + double weight = 0.01 * StdRandom.uniformInt(0, 100); Edge e = new Edge(v, w, weight); addEdge(e); } } - /** + /** * Initializes an edge-weighted graph from an input stream. * The format is the number of vertices V, * followed by the number of edges E, @@ -100,22 +109,36 @@ public EdgeWeightedGraph(int V, int E) { * with each entry separated by whitespace. * * @param in the input stream + * @throws IllegalArgumentException if {@code in} is {@code null} * @throws IllegalArgumentException if the endpoints of any edge are not in prescribed range * @throws IllegalArgumentException if the number of vertices or edges is negative */ public EdgeWeightedGraph(In in) { - this(in.readInt()); - int E = in.readInt(); - if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative"); - for (int i = 0; i < E; i++) { - int v = in.readInt(); - int w = in.readInt(); - validateVertex(v); - validateVertex(w); - double weight = in.readDouble(); - Edge e = new Edge(v, w, weight); - addEdge(e); + if (in == null) throw new IllegalArgumentException("argument is null"); + + try { + V = in.readInt(); + adj = (Bag[]) new Bag[V]; + for (int v = 0; v < V; v++) { + adj[v] = new Bag(); + } + + int E = in.readInt(); + if (E < 0) throw new IllegalArgumentException("Number of edges must be non-negative"); + for (int i = 0; i < E; i++) { + int v = in.readInt(); + int w = in.readInt(); + validateVertex(v); + validateVertex(w); + double weight = in.readDouble(); + Edge e = new Edge(v, w, weight); + addEdge(e); + } + } + catch (NoSuchElementException e) { + throw new IllegalArgumentException("invalid input format in EdgeWeightedGraph constructor", e); } + } /** @@ -195,7 +218,7 @@ public Iterable adj(int v) { * Returns the degree of vertex {@code v}. * * @param v the vertex - * @return the degree of vertex {@code v} + * @return the degree of vertex {@code v} * @throws IllegalArgumentException unless {@code 0 <= v < V} */ public int degree(int v) { @@ -248,6 +271,42 @@ public String toString() { return s.toString(); } + /** + * Returns a string representation of this edge-weighted graph in DOT format, + * suitable for visualization with Graphviz. + * + * To visualize the graph, install Graphviz (e.g., "brew install graphviz"). + * Then use one of the graph visualization tools + * - dot (hierarchical or layer drawing) + * - neato (spring model) + * - fdp (force-directed placement) + * - sfdp (scalable force-directed placement) + * - twopi (radial layout) + * + * For example, the following commands will create graph drawings in SVG + * and PDF formats + * - dot input.dot -Tsvg -o output.svg + * - dot input.dot -Tpdf -o output.pdf + * + * To change the graph attributes (e.g., vertex and edge shapes, arrows, colors) + * in the DOT format, see https://graphviz.org/doc/info/lang.html + * + * @return a string representation of this edge-weighted graph in DOT format + */ + public String toDot() { + StringBuilder s = new StringBuilder(); + s.append("graph {" + NEWLINE); + s.append("node[shape=circle, style=filled, fixedsize=true, width=0.3, fontsize=\"10pt\"]" + NEWLINE); + s.append("edge[arrowhead=normal, fontsize=\"9pt\"]" + NEWLINE); + for (Edge e : edges()) { + int v = e.either(); + int w = e.other(v); + s.append(v + " --" + w + " [label=\"" + e.weight() + "\"]" + NEWLINE); + } + s.append("}" + NEWLINE); + return s.toString(); + } + /** * Unit tests the {@code EdgeWeightedGraph} data type. * @@ -262,7 +321,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/EulerianCycle.java b/src/main/java/edu/princeton/cs/algs4/EulerianCycle.java index 7dc49b947..eabaaad89 100644 --- a/src/main/java/edu/princeton/cs/algs4/EulerianCycle.java +++ b/src/main/java/edu/princeton/cs/algs4/EulerianCycle.java @@ -7,9 +7,9 @@ * * Runs in O(E + V) time. * - * This implementation is tricker than the one for digraphs because + * This implementation is trickier than the one for digraphs because * when we use edge v-w from v's adjacency list, we must be careful - * not to use the second copy of the edge from w's adjaceny list. + * not to use the second copy of the edge from w's adjacency list. * ******************************************************************************/ @@ -22,10 +22,12 @@ * uses every edge in the graph exactly once. *

* This implementation uses a nonrecursive depth-first search. - * The constructor runs in O(E + V) time, - * and uses O(E + V) extra space, where E is the - * number of edges and V the number of vertices - * All other methods take O(1) time. + * The constructor takes Θ(E + V) time in the worst + * case, where E is the number of edges and V is the + * number of vertices + * Each instance method takes Θ(1) time. + * It uses Θ(E + V) extra space in the worst case + * (not including the graph). *

* To compute Eulerian paths in graphs, see {@link EulerianPath}. * To compute Eulerian cycles and paths in digraphs, see @@ -34,7 +36,7 @@ * For additional documentation, * see Section 4.1 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. - * + * * @author Robert Sedgewick * @author Kevin Wayne * @author Nate Liu @@ -64,7 +66,7 @@ public int other(int vertex) { /** * Computes an Eulerian cycle in the specified graph, if one exists. - * + * * @param G the graph */ public EulerianCycle(Graph G) { @@ -73,8 +75,8 @@ public EulerianCycle(Graph G) { if (G.E() == 0) return; // necessary condition: all vertices have even degree - // (this test is needed or it might find an Eulerian path instead of cycle) - for (int v = 0; v < G.V(); v++) + // (this test is needed, or it might find an Eulerian path instead of cycle) + for (int v = 0; v < G.V(); v++) if (G.degree(v) % 2 != 0) return; @@ -133,7 +135,7 @@ else if (v < w) { /** * Returns the sequence of vertices on an Eulerian cycle. - * + * * @return the sequence of vertices on an Eulerian cycle; * {@code null} if no such cycle */ @@ -143,7 +145,7 @@ public Iterable cycle() { /** * Returns true if the graph has an Eulerian cycle. - * + * * @return {@code true} if the graph has an Eulerian cycle; * {@code false} otherwise */ @@ -263,7 +265,7 @@ public static void main(String[] args) { // self loop Graph G4 = new Graph(V); - int v4 = StdRandom.uniform(V); + int v4 = StdRandom.uniformInt(V); G4.addEdge(v4, v4); unitTest(G4, "single self loop"); @@ -290,7 +292,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/EulerianPath.java b/src/main/java/edu/princeton/cs/algs4/EulerianPath.java index eede09691..46bf3eaff 100644 --- a/src/main/java/edu/princeton/cs/algs4/EulerianPath.java +++ b/src/main/java/edu/princeton/cs/algs4/EulerianPath.java @@ -16,10 +16,12 @@ * uses every edge in the graph exactly once. *

* This implementation uses a nonrecursive depth-first search. - * The constructor runs in O(E + V) time, - * and uses O(E + V) extra space, - * where E is the number of edges and V the number of vertices - * All other methods take O(1) time. + * The constructor takes Θ(E + V) time in the worst + * case, where E is the number of edges and V is + * the number of vertices. + * Each instance method takes Θ(1) time. + * It uses Θ(E + V) extra space in the worst case + * (not including the digraph). *

* To compute Eulerian cycles in graphs, see {@link EulerianCycle}. * To compute Eulerian cycles and paths in digraphs, see @@ -28,7 +30,7 @@ * For additional documentation, * see Section 4.1 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. - * + * * @author Robert Sedgewick * @author Kevin Wayne * @author Nate Liu @@ -58,7 +60,7 @@ public int other(int vertex) { /** * Computes an Eulerian path in the specified graph, if one exists. - * + * * @param G the graph */ public EulerianPath(Graph G) { @@ -136,7 +138,7 @@ else if (v < w) { /** * Returns the sequence of vertices on an Eulerian path. - * + * * @return the sequence of vertices on an Eulerian path; * {@code null} if no such path */ @@ -146,7 +148,7 @@ public Iterable path() { /** * Returns true if the graph has an Eulerian path. - * + * * @return {@code true} if the graph has an Eulerian path; * {@code false} otherwise */ @@ -258,18 +260,18 @@ public static void main(String[] args) { // add one random edge Graph G3 = new Graph(G2); - G3.addEdge(StdRandom.uniform(V), StdRandom.uniform(V)); + G3.addEdge(StdRandom.uniformInt(V), StdRandom.uniformInt(V)); unitTest(G3, "one random edge added to Eulerian path"); // self loop Graph G4 = new Graph(V); - int v4 = StdRandom.uniform(V); + int v4 = StdRandom.uniformInt(V); G4.addEdge(v4, v4); unitTest(G4, "single self loop"); // single edge Graph G5 = new Graph(V); - G5.addEdge(StdRandom.uniform(V), StdRandom.uniform(V)); + G5.addEdge(StdRandom.uniformInt(V), StdRandom.uniformInt(V)); unitTest(G5, "single edge"); // empty graph @@ -283,7 +285,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/FFT.java b/src/main/java/edu/princeton/cs/algs4/FFT.java index d58ca3ce5..37eeecdaf 100644 --- a/src/main/java/edu/princeton/cs/algs4/FFT.java +++ b/src/main/java/edu/princeton/cs/algs4/FFT.java @@ -4,7 +4,7 @@ * Dependencies: Complex.java * * Compute the FFT and inverse FFT of a length n complex sequence. - * Bare bones implementation that runs in O(n log n) time. Our goal + * Bare-bones implementation that runs in O(n log n) time. Our goal * is to optimize the clarity of the code, rather than performance. * * Limitations @@ -15,7 +15,7 @@ * an object type for representing complex numbers and because * it re-allocates memory for the subarray, instead of doing * in-place or reusing a single temporary array) - * + * * * % java FFT 4 * x @@ -62,7 +62,7 @@ package edu.princeton.cs.algs4; /** - * The {@code FFT} class provides methods for computing the + * The {@code FFT} class provides methods for computing the * FFT (Fast-Fourier Transform), inverse FFT, linear convolution, * and circular convolution of a complex array. *

@@ -71,11 +71,16 @@ * n must be a power of 2. * Our goal is to optimize the clarity of the code, rather than performance. * It is not the most memory efficient implementation because it uses - * objects to represents complex numbers and it it re-allocates memory - * for the subarray, instead of doing in-place or reusing a single temporary array. - * + * objects to represent complex numbers and it re-allocates memory + * for the subarray, instead of doing in-place or reusing a single + * temporary array. + *

+ * This computes correct results if all arithmetic performed is + * without floating-point rounding error or arithmetic overflow. + * In practice, there will be floating-point rounding error. *

- * For additional documentation, see Section 9.9 of + * For additional documentation, + * see Section 9.9 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -116,7 +121,7 @@ public static Complex[] fft(Complex[] x) { Complex[] q = fft(even); // fft of odd terms - Complex[] odd = even; // reuse the array + Complex[] odd = even; // reuse the array for (int k = 0; k < n/2; k++) { odd[k] = x[2*k + 1]; } @@ -245,14 +250,14 @@ private static void show(Complex[] x, String title) { * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { int n = Integer.parseInt(args[0]); Complex[] x = new Complex[n]; // original data for (int i = 0; i < n; i++) { x[i] = new Complex(i, 0); - x[i] = new Complex(StdRandom.uniform(-1.0, 1.0), 0); + x[i] = new Complex(StdRandom.uniformDouble(-1.0, 1.0), 0); } show(x, "x"); @@ -276,7 +281,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/FarthestPair.java b/src/main/java/edu/princeton/cs/algs4/FarthestPair.java index b6b45e8c5..e5f21c2b1 100644 --- a/src/main/java/edu/princeton/cs/algs4/FarthestPair.java +++ b/src/main/java/edu/princeton/cs/algs4/FarthestPair.java @@ -4,7 +4,7 @@ * Dependencies: GrahamScan.java Point2D.java * Data files: https://algs4.cs.princeton.edu/99hull/rs1423.txt * https://algs4.cs.princeton.edu/99hull/kw1260.txt - * + * * Given a set of n points in the plane, find the farthest pair * (equivalently, compute the diameter of the set of points). * @@ -134,7 +134,7 @@ public Point2D other() { } /** - * Returns the Eucliden distance between the farthest pair of points. + * Returns the Euclidean distance between the farthest pair of points. * This quantity is also known as the diameter of the set of points. * * @return the Euclidean distance between the farthest pair of points @@ -169,7 +169,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/FenwickTree.java b/src/main/java/edu/princeton/cs/algs4/FenwickTree.java index ae7f92877..abf5f3fc5 100644 --- a/src/main/java/edu/princeton/cs/algs4/FenwickTree.java +++ b/src/main/java/edu/princeton/cs/algs4/FenwickTree.java @@ -188,7 +188,7 @@ else if (line[0].equals("rsq")) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/FibonacciMinPQ.java b/src/main/java/edu/princeton/cs/algs4/FibonacciMinPQ.java index a846194ea..85b22ff12 100644 --- a/src/main/java/edu/princeton/cs/algs4/FibonacciMinPQ.java +++ b/src/main/java/edu/princeton/cs/algs4/FibonacciMinPQ.java @@ -27,6 +27,8 @@ * The delete-the-minimum operation takes amortized logarithmic time. * The insert, min-key, is-empty, size, union and constructor take constant time. * + * WARNING: THIS VERSION HAS AT LEAST ONE BUG. + * * @author Tristan Claverie */ public class FibonacciMinPQ implements Iterable { @@ -328,7 +330,7 @@ public int compare(Key key1, Key key2) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/FileIndex.java b/src/main/java/edu/princeton/cs/algs4/FileIndex.java index 83780ba82..890e9132f 100644 --- a/src/main/java/edu/princeton/cs/algs4/FileIndex.java +++ b/src/main/java/edu/princeton/cs/algs4/FileIndex.java @@ -10,14 +10,14 @@ * % java FileIndex ex*.txt * age * ex3.txt - * ex4.txt + * ex4.txt * best - * ex1.txt + * ex1.txt * was * ex1.txt * ex2.txt * ex3.txt - * ex4.txt + * ex4.txt * * % java FileIndex *.txt * @@ -36,11 +36,11 @@ *

* For additional documentation, see Section 3.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. - * + * * @author Robert Sedgewick * @author Kevin Wayne */ -public class FileIndex { +public class FileIndex { // Do not instantiate. private FileIndex() { } @@ -81,7 +81,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/FlowEdge.java b/src/main/java/edu/princeton/cs/algs4/FlowEdge.java index 9d325f9f8..8624cf22c 100644 --- a/src/main/java/edu/princeton/cs/algs4/FlowEdge.java +++ b/src/main/java/edu/princeton/cs/algs4/FlowEdge.java @@ -10,7 +10,7 @@ package edu.princeton.cs.algs4; /** - * The {@code FlowEdge} class represents a capacitated edge with a + * The {@code FlowEdge} class represents a capacitated edge with a * flow in a {@link FlowNetwork}. Each edge consists of two integers * (naming the two vertices), a real-valued capacity, and a real-valued * flow. The data type provides methods for accessing the two endpoints @@ -26,10 +26,10 @@ */ public class FlowEdge { // to deal with floating-point roundoff errors - private static final double FLOATING_POINT_EPSILON = 1E-10; + private static final double FLOATING_POINT_EPSILON = 1.0E-10; private final int v; // from - private final int w; // to + private final int w; // to private final double capacity; // capacity private double flow; // flow @@ -48,7 +48,7 @@ public FlowEdge(int v, int w, double capacity) { if (w < 0) throw new IllegalArgumentException("vertex index must be a non-negative integer"); if (!(capacity >= 0.0)) throw new IllegalArgumentException("Edge capacity must be non-negative"); this.v = v; - this.w = w; + this.w = w; this.capacity = capacity; this.flow = 0.0; } @@ -63,7 +63,7 @@ public FlowEdge(int v, int w, double capacity) { * @throws IllegalArgumentException if either {@code v} or {@code w} * is a negative integer * @throws IllegalArgumentException if {@code capacity} is negative - * @throws IllegalArgumentException unless {@code flow} is between + * @throws IllegalArgumentException unless {@code flow} is between * {@code 0.0} and {@code capacity}. */ public FlowEdge(int v, int w, double capacity, double flow) { @@ -73,7 +73,7 @@ public FlowEdge(int v, int w, double capacity, double flow) { if (!(flow <= capacity)) throw new IllegalArgumentException("flow exceeds capacity"); if (!(flow >= 0.0)) throw new IllegalArgumentException("flow must be non-negative"); this.v = v; - this.w = w; + this.w = w; this.capacity = capacity; this.flow = flow; } @@ -95,7 +95,7 @@ public FlowEdge(FlowEdge e) { */ public int from() { return v; - } + } /** * Returns the head vertex of the edge. @@ -103,7 +103,7 @@ public int from() { */ public int to() { return w; - } + } /** * Returns the capacity of the edge. @@ -154,18 +154,18 @@ public double residualCapacityTo(int vertex) { /** * Increases the flow on the edge in the direction to the given vertex. - * If {@code vertex} is the tail vertex, this increases the flow on the edge by {@code delta}; - * if {@code vertex} is the head vertex, this decreases the flow on the edge by {@code delta}. + * If {@code vertex} is the tail vertex, this increases the flow on the edge by {@code delta}; + * if {@code vertex} is the head vertex, this decreases the flow on the edge by {@code delta}. * @param vertex one endpoint of the edge * @param delta amount by which to increase flow * @throws IllegalArgumentException if {@code vertex} is not one of the endpoints - * of the edge - * @throws IllegalArgumentException if {@code delta} makes the flow on - * on the edge either negative or larger than its capacity + * of the edge + * @throws IllegalArgumentException if {@code delta} makes the flow + * on the edge either negative or larger than its capacity * @throws IllegalArgumentException if {@code delta} is {@code NaN} */ public void addResidualFlowTo(int vertex, double delta) { - if (!(delta >= 0.0)) throw new IllegalArgumentException("Delta must be nonnegative"); + if (!(delta >= 0.0)) throw new IllegalArgumentException("Delta must be non-negative"); if (vertex == v) flow -= delta; // backward edge else if (vertex == w) flow += delta; // forward edge @@ -204,7 +204,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/FlowNetwork.java b/src/main/java/edu/princeton/cs/algs4/FlowNetwork.java index b4d767c27..bc7f1378f 100644 --- a/src/main/java/edu/princeton/cs/algs4/FlowNetwork.java +++ b/src/main/java/edu/princeton/cs/algs4/FlowNetwork.java @@ -20,7 +20,7 @@ * methods for returning the number of vertices V and the number * of edges E. Parallel edges and self-loops are permitted. *

- * This implementation uses an adjacency-lists representation, which + * This implementation uses an adjacency-lists representation, which * is a vertex-indexed array of {@link Bag} objects. * All operations take constant time (in the worst case) except * iterating over the edges incident to a given vertex, which takes @@ -39,14 +39,14 @@ public class FlowNetwork { private final int V; private int E; private Bag[] adj; - + /** * Initializes an empty flow network with {@code V} vertices and 0 edges. * @param V the number of vertices * @throws IllegalArgumentException if {@code V < 0} */ public FlowNetwork(int V) { - if (V < 0) throw new IllegalArgumentException("Number of vertices in a Graph must be nonnegative"); + if (V < 0) throw new IllegalArgumentException("Number of vertices in a Graph must be non-negative"); this.V = V; this.E = 0; adj = (Bag[]) new Bag[V]; @@ -64,16 +64,16 @@ public FlowNetwork(int V) { */ public FlowNetwork(int V, int E) { this(V); - if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative"); + if (E < 0) throw new IllegalArgumentException("Number of edges must be non-negative"); for (int i = 0; i < E; i++) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); - double capacity = StdRandom.uniform(100); + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); + double capacity = StdRandom.uniformInt(100); addEdge(new FlowEdge(v, w, capacity)); } } - /** + /** * Initializes a flow network from an input stream. * The format is the number of vertices V, * followed by the number of edges E, @@ -86,7 +86,7 @@ public FlowNetwork(int V, int E) { public FlowNetwork(In in) { this(in.readInt()); int E = in.readInt(); - if (E < 0) throw new IllegalArgumentException("number of edges must be nonnegative"); + if (E < 0) throw new IllegalArgumentException("number of edges must be non-negative"); for (int i = 0; i < E; i++) { int v = in.readInt(); int w = in.readInt(); @@ -163,7 +163,7 @@ public Iterable edges() { /** * Returns a string representation of the flow network. * This method takes time proportional to E + V. - * @return the number of vertices V, followed by the number of edges E, + * @return the number of vertices V, followed by the number of edges E, * followed by the V adjacency lists */ public String toString() { @@ -193,7 +193,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/FloydWarshall.java b/src/main/java/edu/princeton/cs/algs4/FloydWarshall.java index 1a3669438..6d1978e13 100644 --- a/src/main/java/edu/princeton/cs/algs4/FloydWarshall.java +++ b/src/main/java/edu/princeton/cs/algs4/FloydWarshall.java @@ -26,15 +26,23 @@ * or a negative cycle. *

* This implementation uses the Floyd-Warshall algorithm. - * The constructor takes time proportional to V3 in the - * worst case, where V is the number of vertices. - * Afterwards, the {@code dist()}, {@code hasPath()}, and {@code hasNegativeCycle()} - * methods take constant time; the {@code path()} and {@code negativeCycle()} - * method takes time proportional to the number of edges returned. + * The constructor takes Θ(V3) time, + * where V is the number of vertices. + * Each instance method takes Θ(1) time. + * It uses Θ(V2) extra space + * (not including the edge-weighted digraph). *

- * For additional documentation, - * see Section 4.4 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * This correctly computes shortest paths if all arithmetic performed is + * without floating-point rounding error or arithmetic overflow. + * This is the case if all edge weights are integers and if none of the + * intermediate results exceeds 252. Since all intermediate + * results are sums of edge weights, they are bounded by V C, + * where V is the number of vertices and C is the maximum + * absolute value of any edge weight. + *

+ * For additional documentation, + * see Section 4.4 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -45,7 +53,7 @@ public class FloydWarshall { private DirectedEdge[][] edgeTo; // edgeTo[v][w] = last edge on shortest v->w path /** - * Computes a shortest paths tree from each vertex to to every other vertex in + * Computes a shortest paths tree from each vertex to every other vertex in * the edge-weighted digraph {@code G}. If no such shortest path exists for * some pair of vertices, it computes a negative cycle. * @param G the edge-weighted digraph @@ -219,9 +227,9 @@ public static void main(String[] args) { int E = Integer.parseInt(args[1]); AdjMatrixEdgeWeightedDigraph G = new AdjMatrixEdgeWeightedDigraph(V); for (int i = 0; i < E; i++) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); - double weight = Math.round(100 * (StdRandom.uniform() - 0.15)) / 100.0; + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); + double weight = 0.01 * StdRandom.uniformInt(-15, 100); if (v == w) G.addEdge(new DirectedEdge(v, w, Math.abs(weight))); else G.addEdge(new DirectedEdge(v, w, weight)); } @@ -276,7 +284,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/FordFulkerson.java b/src/main/java/edu/princeton/cs/algs4/FordFulkerson.java index 0d9287d70..304aed9f1 100644 --- a/src/main/java/edu/princeton/cs/algs4/FordFulkerson.java +++ b/src/main/java/edu/princeton/cs/algs4/FordFulkerson.java @@ -4,7 +4,7 @@ * Dependencies: FlowNetwork.java FlowEdge.java Queue.java * Data files: https://algs4.cs.princeton.edu/65maxflow/tinyFN.txt * - * Ford-Fulkerson algorithm for computing a max flow and + * Ford-Fulkerson algorithm for computing a max flow and * a min cut using shortest augmenting path rule. * ******************************************************************************/ @@ -18,34 +18,33 @@ *

* This implementation uses the Ford-Fulkerson algorithm with * the shortest augmenting path heuristic. - * The constructor takes time proportional to E V (E + V) - * in the worst case and extra space (not including the network) - * proportional to V, where V is the number of vertices - * and E is the number of edges. In practice, the algorithm will - * run much faster. - * Afterwards, the {@code inCut()} and {@code value()} methods take - * constant time. + * The constructor takes O(E V (E + V)) + * time, where V is the number of vertices and E is + * the number of edges. In practice, the algorithm will run much faster. + * The {@code inCut()} and {@code value()} methods take Θ(1) time. + * It uses Θ(V) extra space (not including the network). *

- * If the capacities and initial flow values are all integers, then this - * implementation guarantees to compute an integer-valued maximum flow. - * If the capacities and floating-point numbers, then floating-point - * roundoff error can accumulate. + * This correctly computes the maxflow and mincut if all arithmetic + * performed is without floating-point rounding error or arithmetic + * overflow. This is guaranteed to be the case if all edge capacities + * and initial flow values are integers and the value of the maxflow + * does not exceed 252. *

- * For additional documentation, - * see Section 6.4 of + * For additional documentation, see + * Section 6.4 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public class FordFulkerson { - private static final double FLOATING_POINT_EPSILON = 1E-11; + private static final double FLOATING_POINT_EPSILON = 1.0E-11; private final int V; // number of vertices private boolean[] marked; // marked[v] = true iff s->v path in residual graph private FlowEdge[] edgeTo; // edgeTo[v] = last edge on shortest residual s->v path private double value; // current value of max flow - + /** * Compute a maximum flow and minimum cut in the network {@code G} * from vertex {@code s} to vertex {@code t}. @@ -77,7 +76,7 @@ public FordFulkerson(FlowNetwork G, int s, int t) { // augment flow for (int v = t; v != s; v = edgeTo[v].other(v)) { - edgeTo[v].addResidualFlowTo(v, bottle); + edgeTo[v].addResidualFlowTo(v, bottle); } value += bottle; @@ -100,7 +99,7 @@ public double value() { * Returns true if the specified vertex is on the {@code s} side of the mincut. * * @param v vertex - * @return {@code true} if vertex {@code v} is on the {@code s} side of the micut; + * @return {@code true} if vertex {@code v} is on the {@code s} side of the mincut; * {@code false} otherwise * @throws IllegalArgumentException unless {@code 0 <= v < V} */ @@ -109,14 +108,14 @@ public boolean inCut(int v) { return marked[v]; } - // throw an IllegalArgumentException if v is outside prescibed range + // throw an IllegalArgumentException if v is outside prescribed range private void validate(int v) { if (v < 0 || v >= V) throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1)); } - // is there an augmenting path? + // is there an augmenting path? // if so, upon termination edgeTo[] will contain a parent-link representation of such a path // this implementation finds a shortest augmenting path (fewest number of edges), // which performs well both in theory and in practice @@ -271,7 +270,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/FrequencyCounter.java b/src/main/java/edu/princeton/cs/algs4/FrequencyCounter.java index df02efd38..1db88fc0f 100644 --- a/src/main/java/edu/princeton/cs/algs4/FrequencyCounter.java +++ b/src/main/java/edu/princeton/cs/algs4/FrequencyCounter.java @@ -2,7 +2,7 @@ * Compilation: javac FrequencyCounter.java * Execution: java FrequencyCounter L < input.txt * Dependencies: ST.java StdIn.java StdOut.java - * Data files: https://algs4.cs.princeton.edu/31elementary/tnyTale.txt + * Data files: https://algs4.cs.princeton.edu/31elementary/tinyTale.txt * https://algs4.cs.princeton.edu/31elementary/tale.txt * https://algs4.cs.princeton.edu/31elementary/leipzig100K.txt * https://algs4.cs.princeton.edu/31elementary/leipzig300K.txt @@ -27,7 +27,7 @@ package edu.princeton.cs.algs4; /** - * The {@code FrequencyCounter} class provides a client for + * The {@code FrequencyCounter} class provides a client for * reading in a sequence of words and printing a word (exceeding * a given length) that occurs most frequently. It is useful as * a test client for various symbol table implementations. @@ -86,7 +86,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/GREP.java b/src/main/java/edu/princeton/cs/algs4/GREP.java index d93989f58..52b21a41b 100644 --- a/src/main/java/edu/princeton/cs/algs4/GREP.java +++ b/src/main/java/edu/princeton/cs/algs4/GREP.java @@ -6,7 +6,7 @@ * * This program takes an RE as a command-line argument and prints * the lines from standard input having some substring that - * is in the language described by the RE. + * is in the language described by the RE. * * % more tinyL.txt * AC @@ -52,20 +52,20 @@ private GREP() { } * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { String regexp = "(.*" + args[0] + ".*)"; NFA nfa = new NFA(regexp); - while (StdIn.hasNextLine()) { + while (StdIn.hasNextLine()) { String line = StdIn.readLine(); if (nfa.recognizes(line)) { StdOut.println(line); } } - } -} + } +} /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/GabowSCC.java b/src/main/java/edu/princeton/cs/algs4/GabowSCC.java index 83aa455c7..75e85ef99 100644 --- a/src/main/java/edu/princeton/cs/algs4/GabowSCC.java +++ b/src/main/java/edu/princeton/cs/algs4/GabowSCC.java @@ -6,43 +6,43 @@ * https://algs4.cs.princeton.edu/42digraph/mediumDG.txt * https://algs4.cs.princeton.edu/42digraph/largeDG.txt * - * Compute the strongly-connected components of a digraph using + * Compute the strongly-connected components of a digraph using * Gabow's algorithm (aka Cheriyan-Mehlhorn algorithm). * * Runs in O(E + V) time. * * % java GabowSCC tinyDG.txt * 5 components - * 1 + * 1 * 0 2 3 4 5 * 9 10 11 12 * 6 8 - * 7 + * 7 * ******************************************************************************/ package edu.princeton.cs.algs4; /** - * The {@code GabowSCC} class represents a data type for + * The {@code GabowSCC} class represents a data type for * determining the strong components in a digraph. * The id operation determines in which strong component * a given vertex lies; the areStronglyConnected operation * determines whether two vertices are in the same strong component; * and the count operation determines the number of strong * components. - - * The component identifier of a component is one of the - * vertices in the strong component: two vertices have the same component - * identifier if and only if they are in the same strong component. - + *

+ * The component identifier of a vertex is an integer between + * 0 and k–1, where k is the number of strong components. + * Two vertices have the same component identifier if and only if they + * are in the same strong component. *

* This implementation uses the Gabow's algorithm. - * The constructor takes time proportional to V + E - * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * Afterwards, the id, count, and areStronglyConnected - * operations take constant time. - * For alternate implementations of the same API, see + * The constructor takes Θ(V + E) time, + * where V is the number of vertices and E is + * the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the digraph). + * For alternative implementations of the same API, see * {@link KosarajuSharirSCC} and {@link TarjanSCC}. *

* For additional documentation, @@ -71,7 +71,7 @@ public GabowSCC(Digraph G) { marked = new boolean[G.V()]; stack1 = new Stack(); stack2 = new Stack(); - id = new int[G.V()]; + id = new int[G.V()]; preorder = new int[G.V()]; for (int v = 0; v < G.V(); v++) id[v] = -1; @@ -84,7 +84,7 @@ public GabowSCC(Digraph G) { assert check(G); } - private void dfs(Digraph G, int v) { + private void dfs(Digraph G, int v) { marked[v] = true; preorder[v] = pre++; stack1.push(v); @@ -198,7 +198,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/GaussJordanElimination.java b/src/main/java/edu/princeton/cs/algs4/GaussJordanElimination.java index 9c23a1b1f..ce02f2e3d 100644 --- a/src/main/java/edu/princeton/cs/algs4/GaussJordanElimination.java +++ b/src/main/java/edu/princeton/cs/algs4/GaussJordanElimination.java @@ -2,7 +2,7 @@ * Compilation: javac GaussJordanElimination.java * Execution: java GaussJordanElimination n * Dependencies: StdOut.java - * + * * Finds a solutions to Ax = b using Gauss-Jordan elimination with partial * pivoting. If no solution exists, find a solution to yA = 0, yb != 0, * which serves as a certificate of infeasibility. @@ -15,7 +15,7 @@ * 3.000000 * -1.000000 * -2.000000 - * + * * System is infeasible * * -6.250000 @@ -41,14 +41,21 @@ * where A is an n-by-n matrix * and b is a length n vector. * If no solution exists, it finds a solution y to - * yA = 0, yb ≠ 0, which + * yA = 0, yb ≠ 0, * which serves as a certificate of infeasibility. *

* This implementation uses Gauss-Jordan elimination with partial pivoting. * See {@link GaussianElimination} for an implementation that uses * Gaussian elimination (but does not provide the certificate of infeasibility). * For an industrial-strength numerical linear algebra library, - * see JAMA. + * see JAMA. + *

+ * This computes correct results if all arithmetic performed is + * without floating-point rounding error or arithmetic overflow. + * In practice, there will be floating-point rounding error; + * partial pivoting helps prevent accumulated floating-point rounding + * errors from growing out of control (though it does not + * provide any guarantees). *

* For additional documentation, see * Section 9.9 @@ -153,7 +160,7 @@ private void pivot(int p, int q) { /** * Returns a solution to the linear system of equations Ax = b. - * + * * @return a solution x to the linear system of equations * Ax = b; {@code null} if no such solution */ @@ -171,7 +178,7 @@ else if (Math.abs(a[i][n+n]) > EPSILON) /** * Returns a solution to the linear system of equations yA = 0, * yb ≠ 0. - * + * * @return a solution y to the linear system of equations * yA = 0, yb ≠ 0; {@code null} if no such solution */ @@ -190,7 +197,7 @@ public double[] dual() { /** * Returns true if there exists a solution to the linear system of * equations Ax = b. - * + * * @return {@code true} if there exists a solution to the linear system * of equations Ax = b; {@code false} otherwise */ @@ -322,7 +329,7 @@ private static void test3() { test("test 3", A, b); } - // 5-by-5 singluar: infinitely many solutions + // 5-by-5 singular: infinitely many solutions private static void test4() { double[][] A = { { 2, -3, -1, 2, 3 }, @@ -377,10 +384,10 @@ public static void main(String[] args) { double[][] A = new double[n][n]; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) - A[i][j] = StdRandom.uniform(1000); + A[i][j] = StdRandom.uniformInt(1000); double[] b = new double[n]; for (int i = 0; i < n; i++) - b[i] = StdRandom.uniform(1000); + b[i] = StdRandom.uniformInt(1000); test("random " + n + "-by-" + n + " (likely full rank)", A, b); @@ -388,23 +395,23 @@ public static void main(String[] args) { A = new double[n][n]; for (int i = 0; i < n-1; i++) for (int j = 0; j < n; j++) - A[i][j] = StdRandom.uniform(1000); + A[i][j] = StdRandom.uniformInt(1000); for (int i = 0; i < n-1; i++) { - double alpha = StdRandom.uniform(11) - 5.0; + double alpha = StdRandom.uniformInt(-5, 5); for (int j = 0; j < n; j++) { A[n-1][j] += alpha * A[i][j]; } } b = new double[n]; for (int i = 0; i < n; i++) - b[i] = StdRandom.uniform(1000); + b[i] = StdRandom.uniformInt(1000); test("random " + n + "-by-" + n + " (likely infeasible)", A, b); } } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/GaussianElimination.java b/src/main/java/edu/princeton/cs/algs4/GaussianElimination.java index 62fd2d6d5..7fb58e7a0 100644 --- a/src/main/java/edu/princeton/cs/algs4/GaussianElimination.java +++ b/src/main/java/edu/princeton/cs/algs4/GaussianElimination.java @@ -2,7 +2,7 @@ * Compilation: javac GaussianElimination.java * Execution: java GaussianElimination m n * Dependencies: StdOut.java - * + * * Gaussian elimination with partial pivoting for m-by-n system. * * % java GaussianElimination m n @@ -13,7 +13,7 @@ * 3.000000 * -1.000000 * -2.000000 - * + * * System is infeasible * * -6.250000 @@ -47,6 +47,13 @@ * For an industrial-strength numerical linear algebra library, * see JAMA. *

+ * This computes correct results if all arithmetic performed is + * without floating-point rounding error or arithmetic overflow. + * In practice, there will be floating-point rounding error; + * partial pivoting helps prevent accumulated floating-point rounding + * errors from growing out of control (though it does not + * provide any guarantees). + *

* For additional documentation, see * Section 9.9 * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. @@ -55,7 +62,7 @@ * @author Kevin Wayne */ public class GaussianElimination { - private static final double EPSILON = 1e-8; + private static final double EPSILON = 1.0E-8; private final int m; // number of rows private final int n; // number of columns @@ -134,7 +141,7 @@ private void pivot(int p) { /** * Returns a solution to the linear system of equations Ax = b. - * + * * @return a solution x to the linear system of equations * Ax = b; {@code null} if no such solution */ @@ -168,7 +175,7 @@ else if (Math.abs(a[i][n] - sum) > EPSILON) /** * Returns true if there exists a solution to the linear system of * equations Ax = b. - * + * * @return {@code true} if there exists a solution to the linear system * of equations Ax = b; {@code false} otherwise */ @@ -344,10 +351,10 @@ public static void main(String[] args) { double[][] A = new double[n][n]; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) - A[i][j] = StdRandom.uniform(1000); + A[i][j] = StdRandom.uniformInt(1000); double[] b = new double[n]; for (int i = 0; i < n; i++) - b[i] = StdRandom.uniform(1000); + b[i] = StdRandom.uniformInt(1000); test(n + "-by-" + n + " (probably nonsingular)", A, b); } @@ -355,7 +362,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Genome.java b/src/main/java/edu/princeton/cs/algs4/Genome.java index a1e11c0fe..a3ac4fabf 100644 --- a/src/main/java/edu/princeton/cs/algs4/Genome.java +++ b/src/main/java/edu/princeton/cs/algs4/Genome.java @@ -22,7 +22,7 @@ * and expanding a genomic sequence using a 2-bit code. *

* For additional documentation, - * see Section 5.5 of + * see Section 5.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -38,19 +38,19 @@ private Genome() { } * { A, C, T, G } from standard input; compresses them using two bits per * character; and writes the results to standard output. */ - public static void compress() { + public static void compress() { Alphabet DNA = Alphabet.DNA; String s = BinaryStdIn.readString(); int n = s.length(); BinaryStdOut.write(n); - // Write two-bit code for char. + // Write two-bit code for char. for (int i = 0; i < n; i++) { int d = DNA.toIndex(s.charAt(i)); BinaryStdOut.write(d, 2); } BinaryStdOut.close(); - } + } /** * Reads a binary sequence from standard input; converts each two bits @@ -60,7 +60,7 @@ public static void compress() { public static void expand() { Alphabet DNA = Alphabet.DNA; int n = BinaryStdIn.readInt(); - // Read two bits; write char. + // Read two bits; write char. for (int i = 0; i < n; i++) { char c = BinaryStdIn.readChar(2); BinaryStdOut.write(DNA.toChar(c), 8); @@ -84,7 +84,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/GlobalMincut.java b/src/main/java/edu/princeton/cs/algs4/GlobalMincut.java index c65b88136..60f3387f2 100644 --- a/src/main/java/edu/princeton/cs/algs4/GlobalMincut.java +++ b/src/main/java/edu/princeton/cs/algs4/GlobalMincut.java @@ -1,20 +1,20 @@ /****************************************************************************** * Compilation: javac GlobalMincut.java * Execution: java GlobalMincut filename.txt - * Dependencies: EdgeWeightedGraph.java Edge.java UF.java - * IndexMaxPQ.java FlowNetwork.java FlowEdge.java + * Dependencies: EdgeWeightedGraph.java Edge.java UF.java + * IndexMaxPQ.java FlowNetwork.java FlowEdge.java * FordFulkerson.java In.java StdOut.java * Data files: https://algs4.cs.princeton.edu/43mst/tinyEWG.txt * https://algs4.cs.princeton.edu/43mst/mediumEWG.txt * * Computes a minimum cut using Stoer-Wagner's algorithm. * - * % java GlobalMincut tinyEWG.txt - * Min cut: 5 + * % java GlobalMincut tinyEWG.txt + * Min cut: 5 * Min cut weight = 0.9500000000000001 - * - * % java GlobalMincut mediumEWG.txt - * Min cut: 25 60 63 96 199 237 + * + * % java GlobalMincut mediumEWG.txt + * Min cut: 25 60 63 96 199 237 * Min cut weight = 0.14021 * ******************************************************************************/ @@ -22,43 +22,33 @@ package edu.princeton.cs.algs4; /** - * The {@code GlobalMincut} class represents a data type for computing a - * global minimum cut in an edge-weighted graph where the edge - * weights are nonnegative. A cut is a partition of the set - * of vertices of a graph into two nonempty subsets. An edge that has one - * endpoint in each subset of a cut is a crossing edge. The weight - * of a cut is the sum of the weights of its crossing edges. - * A global minimum cut is a cut for which the weight is not - * larger than the weight of any other cut. - *

- * The {@code weight()} method returns the weight of the minimum cut and the - * {@code cut(int v)} method determines if a vertex {@code v} is on the first or - * on the second subset of vertices of the minimum cut. - *

- * This is an implementation of Stoer–Wagner's algorithm using an index - * priority queue and the union-find data type in order to simplify dealing with - * contracting edges. Precisely, the index priority queue is an instance of - * {@link IndexMaxPQ} which is based on a binary heap. As a consequence, the - * constructor takes O(V (V + E ) log - * V ) time and O(V) extra space (not including the - * graph), where V is the number of vertices and E is the - * number of edges. However, this time can be reduced to O(V E - * + V2 log V) by using an index priority queue - * implemented using Fibonacci heaps. - *

- * Afterwards, the {@code weight()} and {@code cut(int v)} methods take constant - * time. - *

- * For additional documentation, see - *

    - *
  • M. Stoer and F. Wagner (1997). A simple min-cut algorithm. Journal of - * the ACM , 44(4):585-591. - *
- * + * The {@code GlobalMincut} class represents a data type for computing a + * global minimum cut in a graph with non-negative edge weights. + * A cut is a partition of the vertices into two nonempty subsets. + * An edge that has one + * endpoint in each subset of a cut is a crossing edge. The weight + * of a cut is the sum of the weights of its crossing edges. + * A global minimum cut whose weight is no larger than the weight + * of any other cut. + *

+ * This is an implementation of Stoer-Wagner's algorithm. + * The constructor takes + * O(V (V + E) log V) time, + * where V is the number of vertices and E is the + * number of edges. + * The weight and isCut methods take Θ(1) time. + * It uses Θ(V) extra space (not including the graph). + *

+ * For additional documentation, see + *

    + *
  • M. Stoer and F. Wagner (1997). A simple min-cut algorithm. Journal of + * the ACM , 44(4):585-591. + *
+ * * @author Marcelo Silva */ public class GlobalMincut { - private static final double FLOATING_POINT_EPSILON = 1E-11; + private static final double FLOATING_POINT_EPSILON = 1.0E-11; // the weight of the minimum cut private double weight = Double.POSITIVE_INFINITY; @@ -89,11 +79,12 @@ public CutPhase(double weight, int s, int t) { } /** - * Computes a minimum cut of an edge-weighted graph. - * + * Computes a minimum cut in an edge-weighted graph. + * * @param G the edge-weighted graph * @throws IllegalArgumentException if the number of vertices of {@code G} - * is less than {@code 2} or if anny edge weight is negative + * is less than {@code 2}. + * @throws IllegalArgumentException if any edge weight is negative */ public GlobalMincut(EdgeWeightedGraph G) { V = G.V(); @@ -104,7 +95,7 @@ public GlobalMincut(EdgeWeightedGraph G) { /** * Validates the edge-weighted graph. - * + * * @param G the edge-weighted graph * @throws IllegalArgumentException if the number of vertices of {@code G} * is less than {@code 2} or if any edge weight is negative @@ -118,7 +109,7 @@ private void validate(EdgeWeightedGraph G) { /** * Returns the weight of the minimum cut. - * + * * @return the weight of the minimum cut */ public double weight() { @@ -126,16 +117,17 @@ public double weight() { } /** - * Returns {@code true} if the vertex {@code v} is on the first subset of - * vertices of the minimum cut; or {@code false} if the vertex {@code v} is - * on the second subset. - * + * Returns {@code true} if the vertex {@code v} is one side of the + * mincut and {@code false} otherwise. An edge v-w + * crosses the mincut if and only if v and w have + * opposite parity. + * * @param v the vertex to check * @return {@code true} if the vertex {@code v} is on the first subset of * vertices of the minimum cut; or {@code false} if the vertex * {@code v} is on the second subset. * @throws IllegalArgumentException unless vertex {@code v} is between - * {@code 0} and {@code (G.V() - 1)} + * {@code 0 <= v < V} */ public boolean cut(int v) { validateVertex(v); @@ -143,17 +135,17 @@ public boolean cut(int v) { } /** - * Makes a cut for the current edge-weighted graph by partitioning its set - * of vertices into two nonempty subsets. The vertices connected to the + * Makes a cut for the current edge-weighted graph by partitioning its + * vertices into two nonempty subsets. The vertices connected to the * vertex {@code t} belong to the first subset. Other vertices not connected * to {@code t} belong to the second subset. - * + * * @param t the vertex {@code t} * @param uf the union-find data type */ private void makeCut(int t, UF uf) { for (int v = 0; v < cut.length; v++) { - cut[v] = uf.connected(v, t); + cut[v] = (uf.find(v) == uf.find(t)); } } @@ -161,7 +153,7 @@ private void makeCut(int t, UF uf) { * Computes a minimum cut of the edge-weighted graph. Precisely, it computes * the lightest of the cuts-of-the-phase which yields the desired minimum * cut. - * + * * @param G the edge-weighted graph * @param a the starting vertex */ @@ -171,7 +163,7 @@ private void minCut(EdgeWeightedGraph G, int a) { cut = new boolean[G.V()]; CutPhase cp = new CutPhase(0.0, a, a); for (int v = G.V(); v > 1; v--) { - cp = minCutPhase(G, marked, cp); + minCutPhase(G, marked, cp); if (cp.weight < weight) { weight = cp.weight; makeCut(cp.t, uf); @@ -187,7 +179,7 @@ private void minCut(EdgeWeightedGraph G, int a) { * in the current graph, where {@code s} and {@code t} are the two vertices * added last in the phase. This algorithm is known in the literature as * maximum adjacency search or maximum cardinality search. - * + * * @param G the edge-weighted graph * @param marked the array of contracted vertices, where {@code marked[v]} * is {@code true} if the vertex {@code v} was already @@ -195,7 +187,7 @@ private void minCut(EdgeWeightedGraph G, int a) { * @param cp the previous cut-of-the-phase * @return the cut-of-the-phase */ - private CutPhase minCutPhase(EdgeWeightedGraph G, boolean[] marked, CutPhase cp) { + private void minCutPhase(EdgeWeightedGraph G, boolean[] marked, CutPhase cp) { IndexMaxPQ pq = new IndexMaxPQ(G.V()); for (int v = 0; v < G.V(); v++) { if (v != cp.s && !marked[v]) pq.insert(v, 0.0); @@ -214,13 +206,12 @@ private CutPhase minCutPhase(EdgeWeightedGraph G, boolean[] marked, CutPhase cp) for (Edge e : G.adj(cp.t)) { cp.weight += e.weight(); } - return cp; } /** * Contracts the edges incidents on the vertices {@code s} and {@code t} of * the given edge-weighted graph. - * + * * @param G the edge-weighted graph * @param s the vertex {@code s} * @param t the vertex {@code t} @@ -245,7 +236,7 @@ private EdgeWeightedGraph contractEdge(EdgeWeightedGraph G, int s, int t) { /** * Checks optimality conditions. - * + * * @param G the edge-weighted graph * @return {@code true} if optimality conditions are fine */ @@ -281,7 +272,7 @@ private void validateVertex(int v) { /** * Unit tests the {@code GlobalMincut} data type. - * + * * @param args the command-line arguments */ public static void main(String[] args) { @@ -298,7 +289,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/GrahamScan.java b/src/main/java/edu/princeton/cs/algs4/GrahamScan.java index 32a75c579..c121ad0bc 100644 --- a/src/main/java/edu/princeton/cs/algs4/GrahamScan.java +++ b/src/main/java/edu/princeton/cs/algs4/GrahamScan.java @@ -1,16 +1,16 @@ /****************************************************************************** - * Compilation: javac GrahamaScan.java + * Compilation: javac GrahamScan.java * Execution: java GrahamScan < input.txt * Dependencies: Point2D.java * Data files: https://algs4.cs.princeton.edu/99hull/rs1423.txt * https://algs4.cs.princeton.edu/99hull/kw1260.txt - * + * * Create points from standard input and compute the convex hull using * Graham scan algorithm. * * May be floating-point issues if x- and y-coordinates are not integers. * - * % java GrahamScan < input100.txt + * % java GrahamScan < input100.txt * (7486.0, 422.0) * (29413.0, 596.0) * (32011.0, 3140.0) @@ -158,7 +158,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Graph.java b/src/main/java/edu/princeton/cs/algs4/Graph.java index a451c6eb3..57eb9b0b7 100644 --- a/src/main/java/edu/princeton/cs/algs4/Graph.java +++ b/src/main/java/edu/princeton/cs/algs4/Graph.java @@ -1,5 +1,5 @@ /****************************************************************************** - * Compilation: javac Graph.java + * Compilation: javac Graph.java * Execution: java Graph input.txt * Dependencies: Bag.java Stack.java In.java StdOut.java * Data files: https://algs4.cs.princeton.edu/41graph/tinyG.txt @@ -10,28 +10,28 @@ * Parallel edges and self-loops allowed. * * % java Graph tinyG.txt - * 13 vertices, 13 edges - * 0: 6 2 1 5 - * 1: 0 - * 2: 0 - * 3: 5 4 - * 4: 5 6 3 - * 5: 3 4 0 - * 6: 0 4 - * 7: 8 - * 8: 7 - * 9: 11 10 12 - * 10: 9 - * 11: 9 12 - * 12: 11 9 + * 13 vertices, 13 edges + * 0: 6 2 1 5 + * 1: 0 + * 2: 0 + * 3: 5 4 + * 4: 5 6 3 + * 5: 3 4 0 + * 6: 0 4 + * 7: 8 + * 8: 7 + * 9: 11 10 12 + * 10: 9 + * 11: 9 12 + * 12: 11 9 * * % java Graph mediumG.txt - * 250 vertices, 1273 edges - * 0: 225 222 211 209 204 202 191 176 163 160 149 114 97 80 68 59 58 49 44 24 15 - * 1: 220 203 200 194 189 164 150 130 107 72 - * 2: 141 110 108 86 79 51 42 18 14 + * 250 vertices, 1273 edges + * 0: 225 222 211 209 204 202 191 176 163 160 149 114 97 80 68 59 58 49 44 24 15 + * 1: 220 203 200 194 189 164 150 130 107 72 + * 2: 141 110 108 86 79 51 42 18 14 * ... - * + * ******************************************************************************/ package edu.princeton.cs.algs4; @@ -43,19 +43,26 @@ * named 0 through V – 1. * It supports the following two primary operations: add an edge to the graph, * iterate over all of the vertices adjacent to a vertex. It also provides - * methods for returning the number of vertices V and the number - * of edges E. Parallel edges and self-loops are permitted. + * methods for returning the degree of a vertex, the number of vertices + * V in the graph, and the number of edges E in the graph. + * Parallel edges and self-loops are permitted. * By convention, a self-loop v-v appears in the * adjacency list of v twice and contributes two to the degree * of v. *

- * This implementation uses an adjacency-lists representation, which + * This implementation uses an adjacency-lists representation, which * is a vertex-indexed array of {@link Bag} objects. - * All operations take constant time (in the worst case) except - * iterating over the vertices adjacent to a given vertex, which takes - * time proportional to the number of such vertices. + * It uses Θ(E + V) space, where E is + * the number of edges and V is the number of vertices. + * All instance methods take Θ(1) time. (Though, iterating over + * the vertices returned by {@link #adj(int)} takes time proportional + * to the degree of the vertex.) + * Constructing an empty graph with V vertices takes + * Θ(V) time; constructing a graph with E edges + * and V vertices takes Θ(E + V) time. *

- * For additional documentation, see Section 4.1 + * For additional documentation, see + * Section 4.1 * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -67,7 +74,7 @@ public class Graph { private final int V; private int E; private Bag[] adj; - + /** * Initializes an empty graph with {@code V} vertices and 0 edges. * param V the number of vertices @@ -76,7 +83,7 @@ public class Graph { * @throws IllegalArgumentException if {@code V < 0} */ public Graph(int V) { - if (V < 0) throw new IllegalArgumentException("Number of vertices must be nonnegative"); + if (V < 0) throw new IllegalArgumentException("Number of vertices must be non-negative"); this.V = V; this.E = 0; adj = (Bag[]) new Bag[V]; @@ -85,33 +92,35 @@ public Graph(int V) { } } - /** + /** * Initializes a graph from the specified input stream. * The format is the number of vertices V, * followed by the number of edges E, * followed by E pairs of vertices, with each entry separated by whitespace. * * @param in the input stream + * @throws IllegalArgumentException if {@code in} is {@code null} * @throws IllegalArgumentException if the endpoints of any edge are not in prescribed range * @throws IllegalArgumentException if the number of vertices or edges is negative * @throws IllegalArgumentException if the input stream is in the wrong format */ public Graph(In in) { + if (in == null) throw new IllegalArgumentException("argument is null"); try { this.V = in.readInt(); - if (V < 0) throw new IllegalArgumentException("number of vertices in a Graph must be nonnegative"); + if (V < 0) throw new IllegalArgumentException("number of vertices in a Graph must be non-negative"); adj = (Bag[]) new Bag[V]; for (int v = 0; v < V; v++) { adj[v] = new Bag(); } int E = in.readInt(); - if (E < 0) throw new IllegalArgumentException("number of edges in a Graph must be nonnegative"); + if (E < 0) throw new IllegalArgumentException("number of edges in a Graph must be non-negative"); for (int i = 0; i < E; i++) { int v = in.readInt(); int w = in.readInt(); validateVertex(v); validateVertex(w); - addEdge(v, w); + addEdge(v, w); } } catch (NoSuchElementException e) { @@ -124,10 +133,19 @@ public Graph(In in) { * Initializes a new graph that is a deep copy of {@code G}. * * @param G the graph to copy + * @throws IllegalArgumentException if {@code G} is {@code null} */ public Graph(Graph G) { - this(G.V()); + this.V = G.V(); this.E = G.E(); + if (V < 0) throw new IllegalArgumentException("Number of vertices must be non-negative"); + + // update adjacency lists + adj = (Bag[]) new Bag[V]; + for (int v = 0; v < V; v++) { + adj[v] = new Bag(); + } + for (int v = 0; v < G.V(); v++) { // reverse so that adjacency list is in same order as original Stack reverse = new Stack(); @@ -224,6 +242,50 @@ public String toString() { return s.toString(); } + /** + * Returns a string representation of this graph in DOT format, + * suitable for visualization with Graphviz. + * + * To visualize the graph, install Graphviz (e.g., "brew install graphviz"). + * Then use one of the graph visualization tools + * - dot (hierarchical or layer drawing) + * - neato (spring model) + * - fdp (force-directed placement) + * - sfdp (scalable force-directed placement) + * - twopi (radial layout) + * + * For example, the following commands will create graph drawings in SVG + * and PDF formats + * - dot input.dot -Tsvg -o output.svg + * - dot input.dot -Tpdf -o output.pdf + * + * To change the graph attributes (e.g., vertex and edge shapes, arrows, colors) + * in the DOT format, see https://graphviz.org/doc/info/lang.html + * + * @return a string representation of this graph in DOT format + */ + public String toDot() { + StringBuilder s = new StringBuilder(); + s.append("graph {" + NEWLINE); + s.append("node[shape=circle, style=filled, fixedsize=true, width=0.3, fontsize=\"10pt\"]" + NEWLINE); + int selfLoops = 0; + for (int v = 0; v < V; v++) { + for (int w : adj[v]) { + if (v < w) { + s.append(v + " -- " + w + NEWLINE); + } + else if (v == w) { + // include only one copy of each self loop (self loops will be consecutive) + if (selfLoops % 2 == 0) { + s.append(v + " -- " + w + NEWLINE); + } + selfLoops++; + } + } + } + s.append("}" + NEWLINE); + return s.toString(); + } /** * Unit tests the {@code Graph} data type. @@ -239,7 +301,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/GraphGenerator.java b/src/main/java/edu/princeton/cs/algs4/GraphGenerator.java index 8f01a76d0..154ee2bb1 100644 --- a/src/main/java/edu/princeton/cs/algs4/GraphGenerator.java +++ b/src/main/java/edu/princeton/cs/algs4/GraphGenerator.java @@ -65,8 +65,8 @@ public static Graph simple(int V, int E) { Graph G = new Graph(V); SET set = new SET(); while (G.E() < E) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); Edge e = new Edge(v, w); if ((v != w) && !set.contains(e)) { set.add(e); @@ -77,7 +77,7 @@ public static Graph simple(int V, int E) { } /** - * Returns a random simple graph on {@code V} vertices, with an + * Returns a random simple graph on {@code V} vertices, with an * edge between any two vertices with probability {@code p}. This is sometimes * referred to as the Erdos-Renyi random graph model. * @param V the number of vertices @@ -139,8 +139,8 @@ public static Graph bipartite(int V1, int V2, int E) { SET set = new SET(); while (G.E() < E) { - int i = StdRandom.uniform(V1); - int j = V1 + StdRandom.uniform(V2); + int i = StdRandom.uniformInt(V1); + int j = V1 + StdRandom.uniformInt(V2); Edge e = new Edge(vertices[i], vertices[j]); if (!set.contains(e)) { set.add(e); @@ -244,7 +244,7 @@ public static Graph eulerianCycle(int V, int E) { Graph G = new Graph(V); int[] vertices = new int[E]; for (int i = 0; i < E; i++) - vertices[i] = StdRandom.uniform(V); + vertices[i] = StdRandom.uniformInt(V); for (int i = 0; i < E-1; i++) { G.addEdge(vertices[i], vertices[i+1]); } @@ -269,7 +269,7 @@ public static Graph eulerianPath(int V, int E) { Graph G = new Graph(V); int[] vertices = new int[E+1]; for (int i = 0; i < E+1; i++) - vertices[i] = StdRandom.uniform(V); + vertices[i] = StdRandom.uniformInt(V); for (int i = 0; i < E; i++) { G.addEdge(vertices[i], vertices[i+1]); } @@ -375,9 +375,9 @@ public static Graph tree(int V) { // with labeled trees on V vertices int[] prufer = new int[V-2]; for (int i = 0; i < V-2; i++) - prufer[i] = StdRandom.uniform(V); + prufer[i] = StdRandom.uniformInt(V); - // degree of vertex v = 1 + number of times it appers in Prufer sequence + // degree of vertex v = 1 + number of times it appears in Prufer sequence int[] degree = new int[V]; for (int v = 0; v < V; v++) degree[v] = 1; @@ -470,7 +470,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/GrayscalePicture.java b/src/main/java/edu/princeton/cs/algs4/GrayscalePicture.java new file mode 100644 index 000000000..063bd2e85 --- /dev/null +++ b/src/main/java/edu/princeton/cs/algs4/GrayscalePicture.java @@ -0,0 +1,549 @@ +/****************************************************************************** + * Compilation: javac GrayscalePicture.java + * Execution: java GrayscalePicture filename + * Dependencies: none + * + * Data type for manipulating individual pixels of a grayscale image. The + * original image can be read from a file in JPEG, GIF, or PNG format, or the + * user can create a blank image of a given dimension. Includes methods for + * displaying the image in a window on the screen or saving to a file. + * + * % java GrayscalePicture mandrill.jpg + * + * Remarks + * ------- + * - uses BufferedImage.TYPE_INT_RGB because BufferedImage.TYPE_BYTE_GRAY + * seems to do some undesirable color correction when calling getRGB() and + * setRGB() + * + ******************************************************************************/ + +package edu.princeton.cs.algs4; + +import java.awt.Color; +import java.awt.FileDialog; +import java.awt.Toolkit; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; + +import javax.imageio.ImageIO; + +import java.io.File; +import java.io.IOException; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.KeyStroke; + + +/** + * The GrayscalePicture data type provides a basic capability + * for manipulating the individual pixels of a grayscale image. + * The original image can be read from a {@code PNG}, {@code GIF}, + * or {@code JPEG} file or the user can create a blank image of a given dimension. + * This class includes methods for displaying the image in a window on + * the screen or saving it to a file. + *

+ * Pixel (col, row) is column col and row row. + * By default, the origin (0, 0) is the pixel in the top-left corner. + * These are common conventions in image processing and consistent with Java's + * {@link java.awt.image.BufferedImage} data type. + * The method {@link #setOriginLowerLeft()} change the origin to the lower left. + *

+ * The {@code get()} and {@code set()} methods use {@link Color} objects to get + * or set the color of the specified pixel. The {@link Color} objects are converted + * to grayscale if they have different values for the R, G, and B channels. + * The {@code getGrayscale()} and {@code setGrayscale()} methods use an + * 8-bit {@code int} to encode the grayscale value, thereby avoiding the need to + * create temporary {@code Color} objects. + *

+ * A W-by-H picture uses ~ 4 W H bytes of memory, + * since the color of each pixel is encoded as a 32-bit int + * (even though, in principle, only ~ W H bytes are needed). + *

+ * For additional documentation, see + * Section 3.1 of + * Computer Science: An Interdisciplinary Approach + * by Robert Sedgewick and Kevin Wayne. + * See {@link Picture} for a version that supports 32-bit RGB color images. + * + * @author Robert Sedgewick + * @author Kevin Wayne + */ +public final class GrayscalePicture implements ActionListener { + private BufferedImage image; // the rasterized image + private JFrame frame; // on-screen view + private String title; // name of file + private boolean isOriginUpperLeft = true; // location of origin + private boolean isVisible = false; // is the frame visible? + private final int width, height; // width and height + + /** + * Creates a {@code width}-by-{@code height} picture, with {@code width} columns + * and {@code height} rows, where each pixel is black. + * + * @param width the width of the picture + * @param height the height of the picture + * @throws IllegalArgumentException if {@code width} is negative + * @throws IllegalArgumentException if {@code height} is negative + */ + public GrayscalePicture(int width, int height) { + if (width < 0) throw new IllegalArgumentException("width must be non-negative"); + if (height < 0) throw new IllegalArgumentException("height must be non-negative"); + this.width = width; + this.height = height; + image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + } + + /** + * Creates a new grayscale picture that is a deep copy of the argument picture. + * + * @param picture the picture to copy + * @throws IllegalArgumentException if {@code picture} is {@code null} + */ + public GrayscalePicture(GrayscalePicture picture) { + if (picture == null) throw new IllegalArgumentException("constructor argument is null"); + + width = picture.width(); + height = picture.height(); + image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + title = picture.title; + isOriginUpperLeft = picture.isOriginUpperLeft; + for (int col = 0; col < width(); col++) + for (int row = 0; row < height(); row++) + image.setRGB(col, row, picture.image.getRGB(col, row)); + } + + /** + * Creates a grayscale picture by reading an image from a file or URL. + * + * @param filename the name of the file (.png, .gif, or .jpg) or URL. + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if cannot read image from file or URL + */ + public GrayscalePicture(String filename) { + if (filename == null) throw new IllegalArgumentException("constructor argument is null"); + title = filename; + try { + // try to read from file in working directory + File file = new File(filename); + if (file.isFile()) { + image = ImageIO.read(file); + } + + else { + + // resource relative to .class file + URL url = getClass().getResource(filename); + + // resource relative to classloader root + if (url == null) { + url = getClass().getClassLoader().getResource(filename); + } + + // or URL from web or jar + if (url == null) { + URI uri = new URI(filename); + if (uri.isAbsolute()) url = uri.toURL(); + else throw new IllegalArgumentException("could not read image: '" + filename + "'"); + } + + image = ImageIO.read(url); + } + + if (image == null) { + throw new IllegalArgumentException("could not read image: '" + filename + "'"); + } + + width = image.getWidth(null); + height = image.getHeight(null); + + // convert to grayscale in-place + for (int col = 0; col < width; col++) { + for (int row = 0; row < height; row++) { + Color color = new Color(image.getRGB(col, row)); + Color gray = toGray(color); + image.setRGB(col, row, gray.getRGB()); + } + } + } + catch (IOException | URISyntaxException e) { + throw new IllegalArgumentException("could not open image: " + filename, e); + } + } + + // Returns a grayscale version of the given color as a Color object. + private static Color toGray(Color color) { + int r = color.getRed(); + int g = color.getGreen(); + int b = color.getBlue(); + int y = (int) (Math.round(0.299*r + 0.587*g + 0.114*b)); + return new Color(y, y, y); + } + + /** + * Returns a {@link JLabel} containing this picture, for embedding in a {@link JPanel}, + * {@link JFrame} or other GUI widget. + * + * @return the {@code JLabel} + */ + public JLabel getJLabel() { + if (image == null) return null; // no image available + ImageIcon icon = new ImageIcon(image); + return new JLabel(icon); + } + + /** + * Sets the origin to be the upper left pixel. This is the default. + */ + public void setOriginUpperLeft() { + isOriginUpperLeft = true; + } + + /** + * Sets the origin to be the lower left pixel. + */ + public void setOriginLowerLeft() { + isOriginUpperLeft = false; + } + + /** + * Displays the picture in a window on the screen. + */ + public void show() { + + // create the GUI for viewing the image if needed + if (frame == null) { + frame = new JFrame(); + + JMenuBar menuBar = new JMenuBar(); + JMenu menu = new JMenu("File"); + menuBar.add(menu); + JMenuItem menuItem1 = new JMenuItem(" Save... "); + menuItem1.addActionListener(this); + // Java 11: use getMenuShortcutKeyMaskEx() + // Java 8: use getMenuShortcutKeyMask() + menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); + menu.add(menuItem1); + frame.setJMenuBar(menuBar); + + + + frame.setContentPane(getJLabel()); + // f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + if (title == null) frame.setTitle(width + "-by-" + height); + else frame.setTitle(title); + frame.setResizable(false); + frame.pack(); + } + + // draw + frame.setVisible(true); + isVisible = true; + frame.repaint(); + } + + /** + * Hides the window on the screen. + */ + public void hide() { + if (frame != null) { + isVisible = false; + frame.setVisible(false); + } + } + + /** + * Is the window containing the picture visible? + * @return {@code true} if the picture is visible, and {@code false} otherwise + */ + public boolean isVisible() { + return isVisible; + } + + /** + * Returns the height of the picture. + * + * @return the height of the picture (in pixels) + */ + public int height() { + return height; + } + + /** + * Returns the width of the picture. + * + * @return the width of the picture (in pixels) + */ + public int width() { + return width; + } + + private void validateRowIndex(int row) { + if (row < 0 || row >= height()) + throw new IndexOutOfBoundsException("row index must be between 0 and " + (height() - 1) + ": " + row); + } + + private void validateColumnIndex(int col) { + if (col < 0 || col >= width()) + throw new IndexOutOfBoundsException("column index must be between 0 and " + (width() - 1) + ": " + col); + } + + private void validateGrayscaleValue(int gray) { + if (gray < 0 || gray >= 256) + throw new IllegalArgumentException("grayscale value must be between 0 and 255"); + } + + /** + * Returns the grayscale value of pixel ({@code col}, {@code row}) as a {@link java.awt.Color}. + * + * @param col the column index + * @param row the row index + * @return the grayscale value of pixel ({@code col}, {@code row}) + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + */ + public Color get(int col, int row) { + validateColumnIndex(col); + validateRowIndex(row); + Color color = new Color(image.getRGB(col, row)); + return toGray(color); + } + + /** + * Returns the grayscale value of pixel ({@code col}, {@code row}) as an {@code int} + * between 0 and 255. + * Using this method can be more efficient than {@link #get(int, int)} because + * it does not create a {@code Color} object. + * + * @param col the column index + * @param row the row index + * @return the 8-bit integer representation of the grayscale value of pixel ({@code col}, {@code row}) + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + */ + public int getGrayscale(int col, int row) { + validateColumnIndex(col); + validateRowIndex(row); + if (isOriginUpperLeft) return image.getRGB(col, row) & 0xFF; + else return image.getRGB(col, height - row - 1) & 0xFF; + } + + /** + * Sets the color of pixel ({@code col}, {@code row}) to the given grayscale value. + * + * @param col the column index + * @param row the row index + * @param color the color (converts to grayscale if color is not a shade of gray) + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + * @throws IllegalArgumentException if {@code color} is {@code null} + */ + public void set(int col, int row, Color color) { + validateColumnIndex(col); + validateRowIndex(row); + if (color == null) throw new IllegalArgumentException("color argument is null"); + Color gray = toGray(color); + image.setRGB(col, row, gray.getRGB()); + } + + /** + * Sets the color of pixel ({@code col}, {@code row}) to the given grayscale value + * between 0 and 255. + * + * @param col the column index + * @param row the row index + * @param gray the 8-bit integer representation of the grayscale value + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + */ + public void setGrayscale(int col, int row, int gray) { + validateColumnIndex(col); + validateRowIndex(row); + validateGrayscaleValue(gray); + int rgb = gray | (gray << 8) | (gray << 16); + if (isOriginUpperLeft) image.setRGB(col, row, rgb); + else image.setRGB(col, height - row - 1, rgb); + } + + /** + * Returns true if this picture is equal to the argument picture. + * + * @param other the other picture + * @return {@code true} if this picture is the same dimension as {@code other} + * and if all pixels have the same color; {@code false} otherwise + */ + public boolean equals(Object other) { + if (other == this) return true; + if (other == null) return false; + if (other.getClass() != this.getClass()) return false; + GrayscalePicture that = (GrayscalePicture) other; + if (this.width() != that.width()) return false; + if (this.height() != that.height()) return false; + for (int col = 0; col < width(); col++) + for (int row = 0; row < height(); row++) + if (this.getGrayscale(col, row) != that.getGrayscale(col, row)) return false; + return true; + } + + /** + * Returns a string representation of this picture. + * The result is a width-by-height matrix of pixels, + * where the grayscale value of a pixel is an integer between 0 and 255. + * + * @return a string representation of this picture + */ + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(width +"-by-" + height + " grayscale picture (grayscale values given in hex)\n"); + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + int gray; + if (isOriginUpperLeft) gray = 0xFF & image.getRGB(col, row); + else gray = 0xFF & image.getRGB(col, height - row - 1); + sb.append(String.format("%3d ", gray)); + } + sb.append("\n"); + } + return sb.toString().trim(); + } + + /** + * This operation is not supported because pictures are mutable. + * + * @return does not return a value + * @throws UnsupportedOperationException if called + */ + public int hashCode() { + throw new UnsupportedOperationException("hashCode() is not supported because pictures are mutable"); + } + + /** + * Saves the picture to a file in either PNG or JPEG format. + * The filetype extension must be either .png or .jpg. + * + * @param filename the name of the file + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if {@code filename} is the empty string + * @throws IllegalArgumentException if {@code filename} has invalid filetype extension + * @throws IllegalArgumentException if cannot write the file {@code filename} + */ + public void save(String filename) { + if (filename == null) throw new IllegalArgumentException("argument to save() is null"); + save(new File(filename)); + title = filename; + } + + /** + * Saves the picture to a file in a PNG or JPEG image format. + * + * @param file the file + * @throws IllegalArgumentException if {@code file} is {@code null} + */ + public void save(File file) { + if (file == null) throw new IllegalArgumentException("argument to save() is null"); + title = file.getName(); + if (frame != null) frame.setTitle(title); + + String suffix = title.substring(title.lastIndexOf('.') + 1); + if (!title.contains(".") || suffix.length() == 0) { + System.out.printf("Error: the filename '%s' has no file extension, such as .jpg or .png\n", title); + return; + } + + try { + // for formats that support transparency (e.g., PNG and GIF) + if (ImageIO.write(image, suffix, file)) return; + + // for formats that don't support transparency (e.g., JPG and BMP) + // create BufferedImage in RGB format and use white background + BufferedImage imageRGB = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + imageRGB.createGraphics().drawImage(image, 0, 0, Color.WHITE, null); + if (ImageIO.write(imageRGB, suffix, file)) return; + + // failed to save the file; probably wrong format + throw new IllegalArgumentException("The filetype '" + suffix + "' is not supported"); + } + catch (IOException e) { + throw new IllegalArgumentException("could not write file '" + title + "'", e); + } + } + + /** + * Opens a save dialog box when the user selects "Save As" from the menu. + */ + @Override + public void actionPerformed(ActionEvent event) { + FileDialog chooser = new FileDialog(frame, + "Use a .png or .jpg extension", FileDialog.SAVE); + chooser.setVisible(true); + String selectedDirectory = chooser.getDirectory(); + String selectedFilename = chooser.getFile(); + if (selectedDirectory != null && selectedFilename != null) { + try { + save(selectedDirectory + selectedFilename); + } + catch (IllegalArgumentException e) { + System.err.println(e.getMessage()); + } + } + } + + /** + * Unit tests this {@code Picture} data type. + * Reads a picture specified by the command-line argument, + * and shows it in a window on the screen. + * + * @param args the command-line arguments + */ + public static void main(String[] args) { + GrayscalePicture picture = new GrayscalePicture(args[0]); + StdOut.printf("%d-by-%d\n", picture.width(), picture.height()); + GrayscalePicture copy = new GrayscalePicture(picture); + picture.show(); + copy.show(); + while (!StdIn.isEmpty()) { + int row = StdIn.readInt(); + int col = StdIn.readInt(); + int gray = StdIn.readInt(); + picture.setGrayscale(row, col, gray); + StdOut.println(picture.get(row, col)); + StdOut.println(picture.getGrayscale(row, col)); + } + } + +} + +/****************************************************************************** + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. + * + * This file is part of algs4.jar, which accompanies the textbook + * + * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, + * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. + * http://algs4.cs.princeton.edu + * + * + * algs4.jar is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * algs4.jar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with algs4.jar. If not, see http://www.gnu.org/licenses. + ******************************************************************************/ diff --git a/src/main/java/edu/princeton/cs/algs4/Heap.java b/src/main/java/edu/princeton/cs/algs4/Heap.java index dc7586e9f..e9108f41b 100644 --- a/src/main/java/edu/princeton/cs/algs4/Heap.java +++ b/src/main/java/edu/princeton/cs/algs4/Heap.java @@ -4,7 +4,7 @@ * Dependencies: StdOut.java StdIn.java * Data files: https://algs4.cs.princeton.edu/24pq/tiny.txt * https://algs4.cs.princeton.edu/24pq/words3.txt - * + * * Sorts a sequence of strings from standard input using heapsort. * * % more tiny.txt @@ -24,10 +24,19 @@ package edu.princeton.cs.algs4; /** - * The {@code Heap} class provides a static methods for heapsorting - * an array. + * The {@code Heap} class provides a static method to sort an array + * using heapsort. + *

+ * This implementation takes Θ(n log n) time + * to sort any array of length n (assuming comparisons + * take constant time). It makes at most + * 2 n log2 n compares. + *

+ * This sorting algorithm is not stable. + * It uses Θ(1) extra memory (not including the input array). *

- * For additional documentation, see Section 2.4 of + * For additional documentation, see + * Section 2.4 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -44,11 +53,16 @@ private Heap() { } */ public static void sort(Comparable[] pq) { int n = pq.length; + + // heapify phase for (int k = n/2; k >= 1; k--) sink(pq, k, n); - while (n > 1) { - exch(pq, 1, n--); - sink(pq, 1, n); + + // sortdown phase + int k = n; + while (k > 1) { + exch(pq, 1, k--); + sink(pq, 1, k); } } @@ -88,8 +102,8 @@ private static void show(Comparable[] a) { } /** - * Reads in a sequence of strings from standard input; heapsorts them; - * and prints them to standard output in ascending order. + * Reads in a sequence of strings from standard input; heapsorts them; + * and prints them to standard output in ascending order. * * @param args the command-line arguments */ @@ -101,7 +115,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/HexDump.java b/src/main/java/edu/princeton/cs/algs4/HexDump.java index 24a21f840..48484a8de 100644 --- a/src/main/java/edu/princeton/cs/algs4/HexDump.java +++ b/src/main/java/edu/princeton/cs/algs4/HexDump.java @@ -3,7 +3,7 @@ * Execution: java HexDump < file * Dependencies: BinaryStdIn.java StdOut.java * Data file: https://algs4.cs.princeton.edu/55compression/abra.txt - * + * * Reads in a binary file and writes out the bytes in hex, 16 per line. * * % more abra.txt @@ -18,7 +18,7 @@ * -------------------------- * - Similar to the Unix utilities od (octal dump) or hexdump (hexadecimal dump). * - * % od -t x1 < abra.txt + * % od -t x1 < abra.txt * 0000000 41 42 52 41 43 41 44 41 42 52 41 21 * 0000014 * @@ -31,7 +31,7 @@ * of a binary file in hexadecimal. *

* For additional documentation, - * see Section 5.5 of + * see Section 5.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. *

* See also {@link BinaryDump} and {@link PictureDump}. @@ -49,7 +49,7 @@ private HexDump() { } /** * Reads in a sequence of bytes from standard input and writes - * them to standard output using hexademical notation, k hex digits + * them to standard output using hexadecimal notation, k hex digits * per line, where k is given as a command-line integer (defaults * to 16 if no integer is specified); also writes the number * of bits. @@ -80,7 +80,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/HopcroftKarp.java b/src/main/java/edu/princeton/cs/algs4/HopcroftKarp.java index 9ce7983cb..5a7442e02 100644 --- a/src/main/java/edu/princeton/cs/algs4/HopcroftKarp.java +++ b/src/main/java/edu/princeton/cs/algs4/HopcroftKarp.java @@ -26,7 +26,7 @@ * A vertex cover in a graph is a subset of its vertices such that * every edge is incident to at least one vertex. A minimum vertex cover * is a vertex cover with the minimum number of vertices. - * By Konig's theorem, in any biparite + * By Konig's theorem, in any bipartite * graph, the maximum number of edges in matching equals the minimum number * of vertices in a vertex cover. * The maximum matching problem in nonbipartite graphs is @@ -292,7 +292,7 @@ private void validate(int v) { } /************************************************************************** - * + * * The code below is solely for testing correctness of the data type. * **************************************************************************/ @@ -349,8 +349,8 @@ private boolean certifySolution(Graph G) { return true; } - /** - * Unit tests the {@code HopcroftKarp} data type. + /** + * Unit tests the {@code HopcroftKarp} data type. * Takes three command-line arguments {@code V1}, {@code V2}, and {@code E}; * creates a random bipartite graph with {@code V1} + {@code V2} vertices * and {@code E} edges; computes a maximum matching and minimum vertex cover; @@ -395,7 +395,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Huffman.java b/src/main/java/edu/princeton/cs/algs4/Huffman.java index 639e9eee7..56930b2fd 100644 --- a/src/main/java/edu/princeton/cs/algs4/Huffman.java +++ b/src/main/java/edu/princeton/cs/algs4/Huffman.java @@ -28,7 +28,7 @@ * ASCII alphabet. *

* For additional documentation, - * see Section 5.5 of + * see Section 5.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -116,17 +116,11 @@ else if (code.charAt(j) == '1') { // build the Huffman trie given frequencies private static Node buildTrie(int[] freq) { - // initialze priority queue with singleton trees + // initialize priority queue with singleton trees MinPQ pq = new MinPQ(); - for (char i = 0; i < R; i++) - if (freq[i] > 0) - pq.insert(new Node(i, freq[i], null, null)); - - // special case in case there is only one character with a nonzero frequency - if (pq.size() == 1) { - if (freq['\0'] == 0) pq.insert(new Node('\0', 0, null, null)); - else pq.insert(new Node('\1', 0, null, null)); - } + for (char c = 0; c < R; c++) + if (freq[c] > 0) + pq.insert(new Node(c, freq[c], null, null)); // merge two smallest trees while (pq.size() > 1) { @@ -169,7 +163,7 @@ private static void buildCode(String[] st, Node x, String s) { public static void expand() { // read in Huffman trie from input stream - Node root = readTrie(); + Node root = readTrie(); // number of bytes to write int length = BinaryStdIn.readInt(); @@ -213,7 +207,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/In.java b/src/main/java/edu/princeton/cs/algs4/In.java index f1d24765b..3e5007721 100644 --- a/src/main/java/edu/princeton/cs/algs4/In.java +++ b/src/main/java/edu/princeton/cs/algs4/In.java @@ -14,10 +14,13 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URL; + +import java.net.URI; +import java.net.URISyntaxException; import java.net.Socket; -// import java.net.HttpURLConnection; +import java.net.URL; import java.net.URLConnection; + import java.util.ArrayList; import java.util.InputMismatchException; import java.util.Locale; @@ -26,23 +29,23 @@ import java.util.regex.Pattern; /** - * Input. This class provides methods for reading strings - * and numbers from standard input, file input, URLs, and sockets. + * The In data type provides methods for reading strings + * and numbers from standard input, file input, URLs, and sockets. *

* The Locale used is: language = English, country = US. This is consistent * with the formatting conventions with Java floating-point literals, * command-line arguments (via {@link Double#parseDouble(String)}) - * and standard output. + * and standard output. *

- * For additional documentation, see + * For additional documentation, see * Section 3.1 of - * Computer Science: An Interdisciplinary Approach + * Computer Science: An Interdisciplinary Approach * by Robert Sedgewick and Kevin Wayne. *

* Like {@link Scanner}, reading a token also consumes preceding Java * whitespace, reading a full line consumes - * the following end-of-line delimeter, while reading a character consumes - * nothing extra. + * the following end-of-line delimiter, while reading a character consumes + * nothing extra. *

* Whitespace is defined in {@link Character#isWhitespace(char)}. Newlines * consist of \n, \r, \r\n, and Unicode hex code points 0x2028, 0x2029, 0x0085; @@ -54,20 +57,20 @@ * @author Kevin Wayne */ public final class In { - + ///// begin: section (1 of 2) of code duplicated from In to StdIn. - + // assume Unicode UTF-8 encoding private static final String CHARSET_NAME = "UTF-8"; // assume language = English, country = US for consistency with System.out. private static final Locale LOCALE = Locale.US; - // the default token separator; we maintain the invariant that this value + // the default token separator; we maintain the invariant that this value // is held by the scanner's delimiter between calls private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\p{javaWhitespace}+"); - // makes whitespace characters significant + // makes whitespace characters significant private static final Pattern EMPTY_PATTERN = Pattern.compile(""); // used to read the entire input. source: @@ -101,7 +104,7 @@ public In(Socket socket) { scanner.useLocale(LOCALE); } catch (IOException ioe) { - throw new IllegalArgumentException("Could not open " + socket, ioe); + throw new IllegalArgumentException("could not open socket: " + socket, ioe); } } @@ -121,7 +124,7 @@ public In(URL url) { scanner.useLocale(LOCALE); } catch (IOException ioe) { - throw new IllegalArgumentException("Could not open " + url, ioe); + throw new IllegalArgumentException("could not read URL: '" + url + "'", ioe); } } @@ -141,8 +144,8 @@ public In(File file) { scanner = new Scanner(new BufferedInputStream(fis), CHARSET_NAME); scanner.useLocale(LOCALE); } - catch (IOException ioe) { - throw new IllegalArgumentException("Could not open " + file, ioe); + catch (IOException ioe) {; + throw new IllegalArgumentException("could not read file: " + file, ioe); } } @@ -157,6 +160,7 @@ public In(File file) { */ public In(String name) { if (name == null) throw new IllegalArgumentException("argument is null"); + if (name.length() == 0) throw new IllegalArgumentException("argument is the empty string"); try { // first try to read file from local file system File file = new File(name); @@ -169,16 +173,19 @@ public In(String name) { return; } - // next try for files included in jar + // resource relative to .class file URL url = getClass().getResource(name); - // try this as well + // resource relative to classloader root if (url == null) { url = getClass().getClassLoader().getResource(name); } // or URL from web if (url == null) { + URI uri = new URI(name); + if (uri.isAbsolute()) url = uri.toURL(); + else throw new IllegalArgumentException("could not read: '" + name + "'"); url = new URL(name); } @@ -192,17 +199,17 @@ public In(String name) { scanner = new Scanner(new BufferedInputStream(is), CHARSET_NAME); scanner.useLocale(LOCALE); } - catch (IOException ioe) { - throw new IllegalArgumentException("Could not open " + name, ioe); + catch (IOException | URISyntaxException e) { + throw new IllegalArgumentException("could not read: '" + name + "'"); } } /** - * Initializes an input stream from a given {@link Scanner} source; use with + * Initializes an input stream from a given {@link Scanner} source; use with * {@code new Scanner(String)} to read from a string. *

* Note that this does not create a defensive copy, so the - * scanner will be mutated as you read on. + * scanner will be mutated as you read on. * * @param scanner the scanner * @throws IllegalArgumentException if {@code scanner} is {@code null} @@ -220,14 +227,14 @@ public In(Scanner scanner) { public boolean exists() { return scanner != null; } - + //// begin: section (2 of 2) of code duplicated from In to StdIn, //// with all methods changed from "public" to "public static". /** * Returns true if input stream is empty (except possibly whitespace). - * Use this to know whether the next call to {@link #readString()}, - * {@link #readDouble()}, etc will succeed. + * Use this to know whether the next call to {@link #readString()}, + * {@link #readDouble()}, etc. will succeed. * * @return {@code true} if this input stream is empty (except possibly whitespace); * {@code false} otherwise @@ -236,7 +243,7 @@ public boolean isEmpty() { return !scanner.hasNext(); } - /** + /** * Returns true if this input stream has a next line. * Use this method to know whether the * next call to {@link #readLine()} will succeed. @@ -253,9 +260,9 @@ public boolean hasNextLine() { * Returns true if this input stream has more input (including whitespace). * Use this method to know whether the next call to {@link #readChar()} will succeed. * This method is functionally equivalent to {@link #hasNextLine()}. - * + * * @return {@code true} if this input stream has more input (including whitespace); - * {@code false} otherwise + * {@code false} otherwise */ public boolean hasNextChar() { scanner.useDelimiter(EMPTY_PATTERN); @@ -300,7 +307,7 @@ public char readChar() { throw new NoSuchElementException("attempts to read a 'char' value from the input stream, " + "but no more tokens are available"); } - } + } /** @@ -529,7 +536,7 @@ public String[] readAllLines() { while (hasNextLine()) { lines.add(readLine()); } - return lines.toArray(new String[lines.size()]); + return lines.toArray(new String[0]); } @@ -574,14 +581,14 @@ public double[] readAllDoubles() { vals[i] = Double.parseDouble(fields[i]); return vals; } - + ///// end: section (2 of 2) of code duplicated from In to StdIn */ /** * Closes this input stream. */ public void close() { - scanner.close(); + scanner.close(); } /** @@ -628,7 +635,7 @@ public static String[] readStrings(String filename) { * an array of integers. * * @return the integers on standard input - * @deprecated Replaced by {@link StdIn#readAllInts()}. + * @deprecated Replaced by {@code new In()}.{@link #readAllInts()}. */ @Deprecated public static int[] readInts() { @@ -640,7 +647,7 @@ public static int[] readInts() { * an array of doubles. * * @return the doubles on standard input - * @deprecated Replaced by {@link StdIn#readAllDoubles()}. + * @deprecated Replaced by {@code new In()}.{@link #readAllDoubles()}. */ @Deprecated public static double[] readDoubles() { @@ -652,13 +659,13 @@ public static double[] readDoubles() { * an array of strings. * * @return the strings on standard input - * @deprecated Replaced by {@link StdIn#readAllStrings()}. + * @deprecated Replaced by {@code new In()}.{@link #readAllStrings()}. */ @Deprecated public static String[] readStrings() { return new In().readAllStrings(); } - + /** * Unit tests the {@code In} data type. * @@ -666,7 +673,7 @@ public static String[] readStrings() { */ public static void main(String[] args) { In in; - String urlName = "https://introcs.cs.princeton.edu/stdlib/InTest.txt"; + String urlName = "https://introcs.cs.princeton.edu/java/stdlib/InTest.txt"; // read from a URL System.out.println("readAll() from URL " + urlName); @@ -761,8 +768,8 @@ public static void main(String[] args) { // read one line at a time from absolute OS X / Linux path System.out.println("readLine() from absolute OS X / Linux path"); System.out.println("---------------------------------------------------------------------------"); - in = new In("/n/fs/introcs/www/java/stdlib/InTest.txt"); try { + in = new In("/n/fs/introcs/www/java/stdlib/InTest.txt"); while (!in.isEmpty()) { String s = in.readLine(); System.out.println(s); @@ -795,7 +802,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/IndexBinomialMinPQ.java b/src/main/java/edu/princeton/cs/algs4/IndexBinomialMinPQ.java index 8ac359050..102c5f65b 100644 --- a/src/main/java/edu/princeton/cs/algs4/IndexBinomialMinPQ.java +++ b/src/main/java/edu/princeton/cs/algs4/IndexBinomialMinPQ.java @@ -502,7 +502,7 @@ public int compare(Key key1, Key key2) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/IndexFibonacciMinPQ.java b/src/main/java/edu/princeton/cs/algs4/IndexFibonacciMinPQ.java index fd2cf2d16..70aa0063a 100644 --- a/src/main/java/edu/princeton/cs/algs4/IndexFibonacciMinPQ.java +++ b/src/main/java/edu/princeton/cs/algs4/IndexFibonacciMinPQ.java @@ -32,6 +32,8 @@ * The delete, increase-key, delete-the-minimum, change-key take amortized logarithmic time. * Construction takes time proportional to the specified capacity * + * WARNING: THIS VERSION HAS AT LEAST ONE BUG. + * * @author Tristan Claverie */ public class IndexFibonacciMinPQ implements Iterable { @@ -469,7 +471,7 @@ public int compare(Key key1, Key key2) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/IndexMaxPQ.java b/src/main/java/edu/princeton/cs/algs4/IndexMaxPQ.java index d524ca6a4..e51cdb9fa 100644 --- a/src/main/java/edu/princeton/cs/algs4/IndexMaxPQ.java +++ b/src/main/java/edu/princeton/cs/algs4/IndexMaxPQ.java @@ -15,7 +15,7 @@ /** * The {@code IndexMaxPQ} class represents an indexed priority queue of generic keys. * It supports the usual insert and delete-the-maximum - * operations, along with delete and change-the-key + * operations, along with delete and change-the-key * methods. In order to let the client refer to items on the priority queue, * an integer between {@code 0} and {@code maxN - 1} * is associated with each key—the client @@ -24,16 +24,16 @@ * testing if the priority queue is empty, and iterating through * the keys. *

- * This implementation uses a binary heap along with an array to associate - * keys with integers in the given range. + * This implementation uses a binary heap along with an + * array to associate keys with integers in the given range. * The insert, delete-the-maximum, delete, * change-key, decrease-key, and increase-key - * operations take logarithmic time. - * The is-empty, size, max-index, max-key, - * and key-of operations take constant time. + * operations take Θ(log n) time in the worst case, + * where n is the number of elements in the priority queue. * Construction takes time proportional to the specified capacity. *

- * For additional documentation, see Section 2.4 of + * For additional documentation, see + * Section 2.4 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -42,6 +42,7 @@ * @param the generic type of key on this priority queue */ public class IndexMaxPQ> implements Iterable { + private int maxN; // maximum number of elements on PQ private int n; // number of elements on PQ private int[] pq; // binary heap using 1-based indexing private int[] qp; // inverse of pq - qp[pq[i]] = pq[qp[i]] = i @@ -56,6 +57,7 @@ public class IndexMaxPQ> implements Iterable= 0) - throw new IllegalArgumentException("Calling increaseKey() with given argument would not strictly increase the key"); + if (keys[i].compareTo(key) == 0) + throw new IllegalArgumentException("Calling increaseKey() with a key equal to the key in the priority queue"); + if (keys[i].compareTo(key) > 0) + throw new IllegalArgumentException("Calling increaseKey() with a key that is strictly less than the key in the priority queue"); keys[i] = key; swim(qp[i]); @@ -222,10 +232,12 @@ public void increaseKey(int i, Key key) { * @throws NoSuchElementException no key is associated with index {@code i} */ public void decreaseKey(int i, Key key) { + validateIndex(i); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); - if (keys[i].compareTo(key) <= 0) - throw new IllegalArgumentException("Calling decreaseKey() with given argument would not strictly decrease the key"); - + if (keys[i].compareTo(key) == 0) + throw new IllegalArgumentException("Calling decreaseKey() with a key equal to the key in the priority queue"); + if (keys[i].compareTo(key) < 0) + throw new IllegalArgumentException("Calling decreaseKey() with a key that is strictly greater than the key in the priority queue"); keys[i] = key; sink(qp[i]); } @@ -238,6 +250,7 @@ public void decreaseKey(int i, Key key) { * @throws NoSuchElementException no key is associated with index {@code i} */ public void delete(int i) { + validateIndex(i); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); int index = qp[i]; exch(index, n--); @@ -247,6 +260,11 @@ public void delete(int i) { qp[i] = -1; } + // throw an IllegalArgumentException if i is an invalid index + private void validateIndex(int i) { + if (i < 0) throw new IllegalArgumentException("index is negative: " + i); + if (i >= maxN) throw new IllegalArgumentException("index >= capacity: " + i); + } /*************************************************************************** * General helper functions. @@ -340,7 +358,7 @@ public static void main(String[] args) { // increase or decrease the key for (int i = 0; i < strings.length; i++) { - if (StdRandom.uniform() < 0.5) + if (StdRandom.bernoulli(0.5)) pq.increaseKey(i, strings[i] + strings[i]); else pq.decreaseKey(i, strings[i].substring(0, 1)); @@ -374,7 +392,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/IndexMinPQ.java b/src/main/java/edu/princeton/cs/algs4/IndexMinPQ.java index df44446ee..89f423d35 100644 --- a/src/main/java/edu/princeton/cs/algs4/IndexMinPQ.java +++ b/src/main/java/edu/princeton/cs/algs4/IndexMinPQ.java @@ -15,7 +15,7 @@ /** * The {@code IndexMinPQ} class represents an indexed priority queue of generic keys. * It supports the usual insert and delete-the-minimum - * operations, along with delete and change-the-key + * operations, along with delete and change-the-key * methods. In order to let the client refer to keys on the priority queue, * an integer between {@code 0} and {@code maxN - 1} * is associated with each key—the client uses this integer to specify @@ -28,12 +28,12 @@ * keys with integers in the given range. * The insert, delete-the-minimum, delete, * change-key, decrease-key, and increase-key - * operations take logarithmic time. - * The is-empty, size, min-index, min-key, - * and key-of operations take constant time. + * operations take Θ(log n) time in the worst case, + * where n is the number of elements in the priority queue. * Construction takes time proportional to the specified capacity. *

- * For additional documentation, see Section 2.4 of + * For additional documentation, see + * Section 2.4 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -85,7 +85,7 @@ public boolean isEmpty() { * @throws IllegalArgumentException unless {@code 0 <= i < maxN} */ public boolean contains(int i) { - if (i < 0 || i >= maxN) throw new IllegalArgumentException(); + validateIndex(i); return qp[i] != -1; } @@ -108,7 +108,7 @@ public int size() { * with index {@code i} */ public void insert(int i, Key key) { - if (i < 0 || i >= maxN) throw new IllegalArgumentException(); + validateIndex(i); if (contains(i)) throw new IllegalArgumentException("index is already in the priority queue"); n++; qp[i] = n; @@ -165,7 +165,7 @@ public int delMin() { * @throws NoSuchElementException no key is associated with index {@code i} */ public Key keyOf(int i) { - if (i < 0 || i >= maxN) throw new IllegalArgumentException(); + validateIndex(i); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); else return keys[i]; } @@ -179,7 +179,7 @@ public Key keyOf(int i) { * @throws NoSuchElementException no key is associated with index {@code i} */ public void changeKey(int i, Key key) { - if (i < 0 || i >= maxN) throw new IllegalArgumentException(); + validateIndex(i); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); keys[i] = key; swim(qp[i]); @@ -209,10 +209,12 @@ public void change(int i, Key key) { * @throws NoSuchElementException no key is associated with index {@code i} */ public void decreaseKey(int i, Key key) { - if (i < 0 || i >= maxN) throw new IllegalArgumentException(); + validateIndex(i); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); - if (keys[i].compareTo(key) <= 0) - throw new IllegalArgumentException("Calling decreaseKey() with given argument would not strictly decrease the key"); + if (keys[i].compareTo(key) == 0) + throw new IllegalArgumentException("Calling decreaseKey() with a key equal to the key in the priority queue"); + if (keys[i].compareTo(key) < 0) + throw new IllegalArgumentException("Calling decreaseKey() with a key strictly greater than the key in the priority queue"); keys[i] = key; swim(qp[i]); } @@ -227,10 +229,12 @@ public void decreaseKey(int i, Key key) { * @throws NoSuchElementException no key is associated with index {@code i} */ public void increaseKey(int i, Key key) { - if (i < 0 || i >= maxN) throw new IllegalArgumentException(); + validateIndex(i); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); - if (keys[i].compareTo(key) >= 0) - throw new IllegalArgumentException("Calling increaseKey() with given argument would not strictly increase the key"); + if (keys[i].compareTo(key) == 0) + throw new IllegalArgumentException("Calling increaseKey() with a key equal to the key in the priority queue"); + if (keys[i].compareTo(key) > 0) + throw new IllegalArgumentException("Calling increaseKey() with a key strictly less than the key in the priority queue"); keys[i] = key; sink(qp[i]); } @@ -243,7 +247,7 @@ public void increaseKey(int i, Key key) { * @throws NoSuchElementException no key is associated with index {@code i} */ public void delete(int i) { - if (i < 0 || i >= maxN) throw new IllegalArgumentException(); + validateIndex(i); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); int index = qp[i]; exch(index, n--); @@ -253,6 +257,11 @@ public void delete(int i) { qp[i] = -1; } + // throw an IllegalArgumentException if i is an invalid index + private void validateIndex(int i) { + if (i < 0) throw new IllegalArgumentException("index is negative: " + i); + if (i >= maxN) throw new IllegalArgumentException("index >= capacity: " + i); + } /*************************************************************************** * General helper functions. @@ -364,7 +373,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/IndexMultiwayMinPQ.java b/src/main/java/edu/princeton/cs/algs4/IndexMultiwayMinPQ.java index 5b58c33f4..8411a06aa 100644 --- a/src/main/java/edu/princeton/cs/algs4/IndexMultiwayMinPQ.java +++ b/src/main/java/edu/princeton/cs/algs4/IndexMultiwayMinPQ.java @@ -371,7 +371,7 @@ public int compare(Key key1, Key key2) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/InplaceMSD.java b/src/main/java/edu/princeton/cs/algs4/InplaceMSD.java index cb42b8a17..cf940e0ae 100644 --- a/src/main/java/edu/princeton/cs/algs4/InplaceMSD.java +++ b/src/main/java/edu/princeton/cs/algs4/InplaceMSD.java @@ -1,13 +1,13 @@ /****************************************************************************** * Compilation: javac InplaceMSD.java * Execution: java InplaceMSD < input.txt - * Dependencies: StdIn.java StdOut.java + * Dependencies: StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/51radix/words3.txt * https://algs4.cs.princeton.edu/51radix/shells.txt * * Sort an array of strings or integers using in-place MSD radix sort. * - * % java InplaceMSD < shells.txt + * % java InplaceMSD < shells.txt * are * by * sea @@ -43,7 +43,7 @@ public class InplaceMSD { private static final int CUTOFF = 15; // cutoff to insertion sort // do not instantiate - private InplaceMSD() { } + private InplaceMSD() { } /** * Rearranges the array of extended ASCII strings in ascending order. @@ -98,7 +98,7 @@ private static void sort(String[] a, int lo, int hi, int d) { heads[r]++; } } - + // recursively sort for each character (excludes sentinel -1) for (int r = 0; r < R; r++) sort(a, tails[r], tails[r+1] - 1, d+1); @@ -147,7 +147,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Insertion.java b/src/main/java/edu/princeton/cs/algs4/Insertion.java index 927ee9c3a..a8d28103a 100644 --- a/src/main/java/edu/princeton/cs/algs4/Insertion.java +++ b/src/main/java/edu/princeton/cs/algs4/Insertion.java @@ -4,7 +4,7 @@ * Dependencies: StdOut.java StdIn.java * Data files: https://algs4.cs.princeton.edu/21elementary/tiny.txt * https://algs4.cs.princeton.edu/21elementary/words3.txt - * + * * Sorts a sequence of strings from standard input using insertion sort. * * % more tiny.txt @@ -29,13 +29,15 @@ * The {@code Insertion} class provides static methods for sorting an * array using insertion sort. *

- * This implementation makes ~ 1/2 n^2 compares and exchanges in - * the worst case, so it is not suitable for sorting large arbitrary arrays. - * More precisely, the number of exchanges is exactly equal to the number - * of inversions. So, for example, it sorts a partially-sorted array + * In the worst case, this implementation makes ~ ½ n2 + * compares and ~ ½ n2 exchanges to sort an array + * of length n. So, it is not suitable for sorting large arbitrary + * arrays. More precisely, the number of exchanges is exactly equal to the + * number of inversions. So, for example, it sorts a partially-sorted array * in linear time. *

- * The sorting algorithm is stable and uses O(1) extra memory. + * This sorting algorithm is stable. + * It uses Θ(1) extra memory (not including the input array). *

* See InsertionPedantic.java * for a version that eliminates the compiler warning. @@ -57,7 +59,7 @@ private Insertion() { } */ public static void sort(Comparable[] a) { int n = a.length; - for (int i = 0; i < n; i++) { + for (int i = 1; i < n; i++) { for (int j = i; j > 0 && less(a[j], a[j-1]); j--) { exch(a, j, j-1); } @@ -73,7 +75,7 @@ public static void sort(Comparable[] a) { * @param hi right endpoint (exclusive) */ public static void sort(Comparable[] a, int lo, int hi) { - for (int i = lo; i < hi; i++) { + for (int i = lo + 1; i < hi; i++) { for (int j = i; j > lo && less(a[j], a[j-1]); j--) { exch(a, j, j-1); } @@ -88,7 +90,7 @@ public static void sort(Comparable[] a, int lo, int hi) { */ public static void sort(Object[] a, Comparator comparator) { int n = a.length; - for (int i = 0; i < n; i++) { + for (int i = 1; i < n; i++) { for (int j = i; j > 0 && less(a[j], a[j-1], comparator); j--) { exch(a, j, j-1); } @@ -105,7 +107,7 @@ public static void sort(Object[] a, Comparator comparator) { * @param comparator the comparator specifying the order */ public static void sort(Object[] a, int lo, int hi, Comparator comparator) { - for (int i = lo; i < hi; i++) { + for (int i = lo + 1; i < hi; i++) { for (int j = i; j > lo && less(a[j], a[j-1], comparator); j--) { exch(a, j, j-1); } @@ -128,7 +130,7 @@ public static int[] indexSort(Comparable[] a) { for (int i = 0; i < n; i++) index[i] = i; - for (int i = 0; i < n; i++) + for (int i = 1; i < n; i++) for (int j = i; j > 0 && less(a[index[j]], a[index[j-1]]); j--) exch(index, j, j-1); @@ -138,7 +140,7 @@ public static int[] indexSort(Comparable[] a) { /*************************************************************************** * Helper sorting functions. ***************************************************************************/ - + // is v < w ? private static boolean less(Comparable v, Comparable w) { return v.compareTo(w) < 0; @@ -148,7 +150,7 @@ private static boolean less(Comparable v, Comparable w) { private static boolean less(Object v, Object w, Comparator comparator) { return comparator.compare(v, w) < 0; } - + // exchange a[i] and a[j] private static void exch(Object[] a, int i, int j) { Object swap = a[i]; @@ -172,7 +174,7 @@ private static boolean isSorted(Comparable[] a) { // is the array a[lo..hi) sorted private static boolean isSorted(Comparable[] a, int lo, int hi) { - for (int i = lo+1; i < hi; i++) + for (int i = lo + 1; i < hi; i++) if (less(a[i], a[i-1])) return false; return true; } @@ -183,7 +185,7 @@ private static boolean isSorted(Object[] a, Comparator comparator) { // is the array a[lo..hi) sorted private static boolean isSorted(Object[] a, int lo, int hi, Comparator comparator) { - for (int i = lo+1; i < hi; i++) + for (int i = lo + 1; i < hi; i++) if (less(a[i], a[i-1], comparator)) return false; return true; } @@ -209,7 +211,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/InsertionX.java b/src/main/java/edu/princeton/cs/algs4/InsertionX.java index 504072195..57210d99a 100644 --- a/src/main/java/edu/princeton/cs/algs4/InsertionX.java +++ b/src/main/java/edu/princeton/cs/algs4/InsertionX.java @@ -4,9 +4,9 @@ * Dependencies: StdOut.java StdIn.java * Data files: https://algs4.cs.princeton.edu/21elementary/tiny.txt * https://algs4.cs.princeton.edu/21elementary/words3.txt - * + * * Sorts a sequence of strings from standard input using an optimized - * version of insertion sort that uses half exchanges instead of + * version of insertion sort that uses half exchanges instead of * full exchanges to reduce data movement.. * * % more tiny.txt @@ -29,7 +29,16 @@ * an array using an optimized version of insertion sort (with half exchanges * and a sentinel). *

- * For additional documentation, see Section 2.1 of + * In the worst case, this implementation makes ~ 1/2 n2 + * compares to sort an array of length n. + * So, it is not suitable for sorting large arrays + * (unless the number of inversions is small). + *

+ * This sorting algorithm is stable. + * It uses Θ(1) extra memory (not including the input array). + *

+ * For additional documentation, see + * Section 2.1 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -77,12 +86,12 @@ public static void sort(Comparable[] a) { /*************************************************************************** * Helper sorting functions. ***************************************************************************/ - + // is v < w ? private static boolean less(Comparable v, Comparable w) { return v.compareTo(w) < 0; } - + // exchange a[i] and a[j] private static void exch(Object[] a, int i, int j) { Object swap = a[i]; @@ -122,7 +131,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Interval1D.java b/src/main/java/edu/princeton/cs/algs4/Interval1D.java index 3c74c3089..460f5ff59 100644 --- a/src/main/java/edu/princeton/cs/algs4/Interval1D.java +++ b/src/main/java/edu/princeton/cs/algs4/Interval1D.java @@ -2,7 +2,7 @@ * Compilation: javac Interval1D.java * Execution: java Interval1D * Dependencies: StdOut.java - * + * * 1-dimensional interval data type. * ******************************************************************************/ @@ -19,9 +19,9 @@ * The class {@code Interval1D} includes methods for checking whether * an interval contains a point and determining whether two intervals intersect. *

- * For additional documentation, - * see Section 1.2 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 1.2 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -81,7 +81,7 @@ public Interval1D(double min, double max) { * @deprecated Replaced by {@link #min()}. */ @Deprecated - public double left() { + public double left() { return min; } @@ -91,7 +91,7 @@ public double left() { * @deprecated Replaced by {@link #max()}. */ @Deprecated - public double right() { + public double right() { return max; } @@ -100,7 +100,7 @@ public double right() { * * @return the min endpoint of this interval */ - public double min() { + public double min() { return min; } @@ -109,7 +109,7 @@ public double min() { * * @return the max endpoint of this interval */ - public double max() { + public double max() { return max; } @@ -126,6 +126,17 @@ public boolean intersects(Interval1D that) { return true; } + /** + * Returns true if this interval contains the specified interval. + * + * @param that the other interval + * @return {@code true} if this interval contains the argument interval; + * {@code false} otherwise + */ + public boolean contains(Interval1D that) { + return (this.max >= that.max) && (this.min <= that.min); + } + /** * Returns true if this interval contains the specified value. * @@ -233,7 +244,7 @@ public static void main(String[] args) { for (int i = 0; i < intervals.length; i++) StdOut.println(intervals[i]); StdOut.println(); - + StdOut.println("Sort by min endpoint"); Arrays.sort(intervals, Interval1D.MIN_ENDPOINT_ORDER); for (int i = 0; i < intervals.length; i++) @@ -255,7 +266,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Interval2D.java b/src/main/java/edu/princeton/cs/algs4/Interval2D.java index 9767fb448..34189ebf0 100644 --- a/src/main/java/edu/princeton/cs/algs4/Interval2D.java +++ b/src/main/java/edu/princeton/cs/algs4/Interval2D.java @@ -2,7 +2,7 @@ * Compilation: javac Interval2D.java * Execution: java Interval2D * Dependencies: StdOut.java Interval1D.java StdDraw.java - * + * * 2-dimensional interval data type. * ******************************************************************************/ @@ -19,9 +19,9 @@ * a two-dimensional interval contains a point and determining whether * two two-dimensional intervals intersect. *

- * For additional documentation, - * see Section 1.2 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 1.2 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -52,6 +52,16 @@ public boolean intersects(Interval2D that) { return true; } + /** + * Does this two-dimensional interval contain that two-dimensional interval? + * @param that the other two-dimensional interval + * @return true if this two-dimensional interval contains + * that two-dimensional interval; false otherwise + */ + public boolean contains(Interval2D that) { + return this.x.contains(that.x) && this.y.contains(that.y); + } + /** * Does this two-dimensional interval contain the point p? * @param p the two-dimensional point @@ -68,7 +78,7 @@ public boolean contains(Point2D p) { public double area() { return x.length() * y.length(); } - + /** * Returns a string representation of this two-dimensional interval. * @return a string representation of this two-dimensional interval @@ -91,10 +101,10 @@ public boolean equals(Object other) { return this.x.equals(that.x) && this.y.equals(that.y); } - + /** - * Returns an integer hash code for this interval. - * @return an integer hash code for this interval + * Returns an integer hash code for this interval. + * @return an integer hash code for this interval */ public int hashCode() { int hash1 = x.hashCode(); @@ -130,8 +140,8 @@ public static void main(String[] args) { Counter counter = new Counter("hits"); for (int t = 0; t < trials; t++) { - double x = StdRandom.uniform(0.0, 1.0); - double y = StdRandom.uniform(0.0, 1.0); + double x = StdRandom.uniformDouble(0.0, 1.0); + double y = StdRandom.uniformDouble(0.0, 1.0); Point2D point = new Point2D(x, y); if (box.contains(point)) counter.increment(); @@ -144,7 +154,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Inversions.java b/src/main/java/edu/princeton/cs/algs4/Inversions.java index e5a4f64e8..0c5c70660 100644 --- a/src/main/java/edu/princeton/cs/algs4/Inversions.java +++ b/src/main/java/edu/princeton/cs/algs4/Inversions.java @@ -2,7 +2,7 @@ * Compilation: javac Inversions.java * Execution: java Inversions < input.txt * Dependencies: StdIn.java StdOut.java - * + * * Read array of n integers and count number of inversions in n log n time. * ******************************************************************************/ @@ -10,16 +10,18 @@ package edu.princeton.cs.algs4; /** - * The {@code Inversions} class provides static methods to count the + * The {@code Inversions} class provides static methods to count the * number of inversions in either an array of integers or comparables. * An inversion in an array {@code a[]} is a pair of indicies {@code i} and * {@code j} such that {@code i < j} and {@code a[i] > a[j]}. *

* This implementation uses a generalization of mergesort. The count - * operation takes time proportional to n log n, - * where n is the number of keys in the array. + * operation takes Θ(n log n) time to count the + * number of inversions in any array of length n (assuming + * comparisons take constant time). *

- * For additional documentation, see Section 2.2 + * For additional documentation, see + * Section 2.2 * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -36,7 +38,7 @@ private static long merge(int[] a, int[] aux, int lo, int mid, int hi) { // copy to aux[] for (int k = lo; k <= hi; k++) { - aux[k] = a[k]; + aux[k] = a[k]; } // merge back to a[] @@ -56,7 +58,7 @@ private static long count(int[] a, int[] b, int[] aux, int lo, int hi) { long inversions = 0; if (hi <= lo) return 0; int mid = lo + (hi - lo) / 2; - inversions += count(a, b, aux, lo, mid); + inversions += count(a, b, aux, lo, mid); inversions += count(a, b, aux, mid+1, hi); inversions += merge(b, aux, lo, mid, hi); assert inversions == brute(a, lo, hi); @@ -68,7 +70,7 @@ private static long count(int[] a, int[] b, int[] aux, int lo, int hi) { * Returns the number of inversions in the integer array. * The argument array is not modified. * @param a the array - * @return the number of inversions in the array. An inversion is a pair of + * @return the number of inversions in the array. An inversion is a pair of * indicies {@code i} and {@code j} such that {@code i < j} * and {@code a[i] > a[j]}. */ @@ -89,7 +91,7 @@ private static > long merge(Key[] a, Key[] aux, int // copy to aux[] for (int k = lo; k <= hi; k++) { - aux[k] = a[k]; + aux[k] = a[k]; } // merge back to a[] @@ -109,7 +111,7 @@ private static > long count(Key[] a, Key[] b, Key[] long inversions = 0; if (hi <= lo) return 0; int mid = lo + (hi - lo) / 2; - inversions += count(a, b, aux, lo, mid); + inversions += count(a, b, aux, lo, mid); inversions += count(a, b, aux, mid+1, hi); inversions += merge(b, aux, lo, mid, hi); assert inversions == brute(a, lo, hi); @@ -122,7 +124,7 @@ private static > long count(Key[] a, Key[] b, Key[] * The argument array is not modified. * @param a the array * @param the inferred type of the elements in the array - * @return the number of inversions in the array. An inversion is a pair of + * @return the number of inversions in the array. An inversion is a pair of * indicies {@code i} and {@code j} such that {@code i < j} * and {@code a[i].compareTo(a[j]) > 0}. */ @@ -175,7 +177,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/KMP.java b/src/main/java/edu/princeton/cs/algs4/KMP.java index f79840ede..1497b23e0 100644 --- a/src/main/java/edu/princeton/cs/algs4/KMP.java +++ b/src/main/java/edu/princeton/cs/algs4/KMP.java @@ -8,18 +8,18 @@ * KMP algorithm. * * % java KMP abracadabra abacadabrabracabracadabrabrabracad - * text: abacadabrabracabracadabrabrabracad - * pattern: abracadabra + * text: abacadabrabracabracadabrabrabracad + * pattern: abracadabra * * % java KMP rab abacadabrabracabracadabrabrabracad - * text: abacadabrabracabracadabrabrabracad + * text: abacadabrabracabracadabrabrabracad * pattern: rab * * % java KMP bcara abacadabrabracabracadabrabrabracad - * text: abacadabrabracabracadabrabrabracad + * text: abacadabrabracabracadabrabrabracad * pattern: bcara * - * % java KMP rabrabracad abacadabrabracabracadabrabrabracad + * % java KMP rabrabracad abacadabrabracabracadabrabrabracad * text: abacadabrabracabracadabrabrabracad * pattern: rabrabracad * @@ -47,10 +47,8 @@ */ public class KMP { private final int R; // the radix - private int[][] dfa; // the KMP automoton - - private char[] pattern; // either the character array for the pattern - private String pat; // or the pattern string + private final int m; // length of pattern + private int[][] dfa; // the KMP automaton /** * Preprocesses the pattern string. @@ -59,19 +57,18 @@ public class KMP { */ public KMP(String pat) { this.R = 256; - this.pat = pat; + this.m = pat.length(); // build DFA from pattern - int m = pat.length(); - dfa = new int[R][m]; - dfa[pat.charAt(0)][0] = 1; + dfa = new int[R][m]; + dfa[pat.charAt(0)][0] = 1; for (int x = 0, j = 1; j < m; j++) { - for (int c = 0; c < R; c++) - dfa[c][j] = dfa[c][x]; // Copy mismatch cases. - dfa[pat.charAt(j)][j] = j+1; // Set match case. - x = dfa[pat.charAt(j)][x]; // Update restart state. - } - } + for (int c = 0; c < R; c++) + dfa[c][j] = dfa[c][x]; // Copy mismatch cases. + dfa[pat.charAt(j)][j] = j+1; // Set match case. + x = dfa[pat.charAt(j)][x]; // Update restart state. + } + } /** * Preprocesses the pattern string. @@ -81,24 +78,22 @@ public KMP(String pat) { */ public KMP(char[] pattern, int R) { this.R = R; - this.pattern = new char[pattern.length]; - for (int j = 0; j < pattern.length; j++) - this.pattern[j] = pattern[j]; + this.m = pattern.length; // build DFA from pattern int m = pattern.length; - dfa = new int[R][m]; - dfa[pattern[0]][0] = 1; + dfa = new int[R][m]; + dfa[pattern[0]][0] = 1; for (int x = 0, j = 1; j < m; j++) { - for (int c = 0; c < R; c++) - dfa[c][j] = dfa[c][x]; // Copy mismatch cases. - dfa[pattern[j]][j] = j+1; // Set match case. - x = dfa[pattern[j]][x]; // Update restart state. - } - } + for (int c = 0; c < R; c++) + dfa[c][j] = dfa[c][x]; // Copy mismatch cases. + dfa[pattern[j]][j] = j+1; // Set match case. + x = dfa[pattern[j]][x]; // Update restart state. + } + } /** - * Returns the index of the first occurrrence of the pattern string + * Returns the index of the first occurrence of the pattern string * in the text string. * * @param txt the text string @@ -108,7 +103,6 @@ public KMP(char[] pattern, int R) { public int search(String txt) { // simulate operation of DFA on text - int m = pat.length(); int n = txt.length(); int i, j; for (i = 0, j = 0; i < n && j < m; i++) { @@ -119,7 +113,7 @@ public int search(String txt) { } /** - * Returns the index of the first occurrrence of the pattern string + * Returns the index of the first occurrence of the pattern string * in the text string. * * @param text the text string @@ -129,7 +123,6 @@ public int search(String txt) { public int search(char[] text) { // simulate operation of DFA on text - int m = pattern.length; int n = text.length; int i, j; for (i = 0, j = 0; i < n && j < m; i++) { @@ -140,7 +133,7 @@ public int search(char[] text) { } - /** + /** * Takes a pattern string and an input string as command-line arguments; * searches for the pattern string in the text string; and prints * the first occurrence of the pattern string in the text string. @@ -175,7 +168,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/KWIK.java b/src/main/java/edu/princeton/cs/algs4/KWIK.java index fdeeaa0d3..3713460bc 100644 --- a/src/main/java/edu/princeton/cs/algs4/KWIK.java +++ b/src/main/java/edu/princeton/cs/algs4/KWIK.java @@ -3,7 +3,7 @@ * Execution: java KWIK file.txt * Dependencies: StdIn.java StdOut.java In.java SuffixArray.java * Data files: https://algs4.cs.princeton.edu/63suffix/tale.txt - * https://algs4.cs.princeton.edu/63suffix/mobydick.txt + * https://algs4.cs.princeton.edu/63suffix/mobydick.txt * * Keyword-in-context search. * @@ -11,7 +11,7 @@ * majesty * most gracious majesty king george th * rnkeys and the majesty of the law fir - * on against the majesty of the people + * on against the majesty of the people * se them to his majestys chief secreta * h lists of his majestys forces and of * @@ -21,17 +21,17 @@ * for in case of the worst is a friend in * e roomdoor and the worst is over then a * pect mr darnay the worst its the wisest - * is his brother the worst of a bad race - * ss in them for the worst of health for + * is his brother the worst of a bad race + * ss in them for the worst of health for * you have seen the worst of her agitati * cumwented into the worst of luck buuust * n your brother the worst of the bad rac * full share in the worst of the day pla - * mes to himself the worst of the strife + * mes to himself the worst of the strife * f times it was the worst of times it wa - * ould hope that the worst was over well + * ould hope that the worst was over well * urage business the worst will be over i - * clesiastics of the worst world worldly + * clesiastics of the worst world worldly * ******************************************************************************/ @@ -88,11 +88,11 @@ public static void main(String[] args) { } StdOut.println(); } - } -} + } +} /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Knuth.java b/src/main/java/edu/princeton/cs/algs4/Knuth.java index 863c5260d..4d4fbcf98 100644 --- a/src/main/java/edu/princeton/cs/algs4/Knuth.java +++ b/src/main/java/edu/princeton/cs/algs4/Knuth.java @@ -4,7 +4,7 @@ * Dependencies: StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/11model/cards.txt * https://algs4.cs.princeton.edu/11model/cardsUnicode.txt - * + * * Reads in a list of strings and prints them in random order. * The Knuth (or Fisher-Yates) shuffling algorithm guarantees * to rearrange the elements in uniformly random order, under @@ -27,11 +27,11 @@ * KH * * % more cardsUnicode.txt - * 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ Q♣ K♣ A♣ - * 2♦ 3♦ 4♦ 5♦ 6♦ 7♦ 8♦ 9♦ 10♦ J♦ Q♦ K♦ A♦ - * 2♥ 3♥ 4♥ 5♥ 6♥ 7♥ 8♥ 9♥ 10♥ J♥ Q♥ K♥ A♥ - * 2♠ 3♠ 4♠ 5♠ 6♠ 7♠ 8♠ 9♠ 10♠ J♠ Q♠ K♠ A♠ - * + * 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ Q♣ K♣ A♣ + * 2♦ 3♦ 4♦ 5♦ 6♦ 7♦ 8♦ 9♦ 10♦ J♦ Q♦ K♦ A♦ + * 2♥ 3♥ 4♥ 5♥ 6♥ 7♥ 8♥ 9♥ 10♥ J♥ Q♥ K♥ A♥ + * 2♠ 3♠ 4♠ 5♠ 6♠ 7♠ 8♠ 9♠ 10♠ J♠ Q♠ K♠ A♠ + * * % java Knuth < cardsUnicode.txt * 2♠ * K♥ @@ -46,7 +46,7 @@ package edu.princeton.cs.algs4; /** - * The {@code Knuth} class provides a client for reading in a + * The {@code Knuth} class provides a client for reading in a * sequence of strings and shuffling them using the Knuth (or Fisher-Yates) * shuffling algorithm. This algorithm guarantees to rearrange the * elements in uniformly random order, under @@ -62,7 +62,7 @@ * @author Robert Sedgewick * @author Kevin Wayne */ -public class Knuth { +public class Knuth { // this class should not be instantiated private Knuth() { } @@ -122,7 +122,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/KosarajuSharirSCC.java b/src/main/java/edu/princeton/cs/algs4/KosarajuSharirSCC.java index 7bd7b0747..65df0a89f 100644 --- a/src/main/java/edu/princeton/cs/algs4/KosarajuSharirSCC.java +++ b/src/main/java/edu/princeton/cs/algs4/KosarajuSharirSCC.java @@ -13,26 +13,26 @@ * * % java KosarajuSharirSCC tinyDG.txt * 5 strong components - * 1 - * 0 2 3 4 5 - * 9 10 11 12 - * 6 8 + * 1 + * 0 2 3 4 5 + * 9 10 11 12 + * 6 8 * 7 * - * % java KosarajuSharirSCC mediumDG.txt + * % java KosarajuSharirSCC mediumDG.txt * 10 strong components - * 21 - * 2 5 6 8 9 11 12 13 15 16 18 19 22 23 25 26 28 29 30 31 32 33 34 35 37 38 39 40 42 43 44 46 47 48 49 - * 14 - * 3 4 17 20 24 27 36 - * 41 - * 7 - * 45 - * 1 - * 0 - * 10 + * 21 + * 2 5 6 8 9 11 12 13 15 16 18 19 22 23 25 26 28 29 30 31 32 33 34 35 37 38 39 40 42 43 44 46 47 48 49 + * 14 + * 3 4 17 20 24 27 36 + * 41 + * 7 + * 45 + * 1 + * 0 + * 10 * - * % java -Xss50m KosarajuSharirSCC mediumDG.txt + * % java -Xss50m KosarajuSharirSCC mediumDG.txt * 25 strong components * 7 11 32 36 61 84 95 116 121 128 230 ... * 28 73 80 104 115 143 149 164 184 185 ... @@ -65,30 +65,30 @@ package edu.princeton.cs.algs4; /** - * The {@code KosarajuSharirSCC} class represents a data type for + * The {@code KosarajuSharirSCC} class represents a data type for * determining the strong components in a digraph. * The id operation determines in which strong component * a given vertex lies; the areStronglyConnected operation * determines whether two vertices are in the same strong component; * and the count operation determines the number of strong * components. - - * The component identifier of a component is one of the - * vertices in the strong component: two vertices have the same component - * identifier if and only if they are in the same strong component. - + *

+ * The component identifier of a vertex is an integer between + * 0 and k–1, where k is the number of strong components. + * Two vertices have the same component identifier if and only if they + * are in the same strong component. *

* This implementation uses the Kosaraju-Sharir algorithm. - * The constructor takes time proportional to V + E - * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * Afterwards, the id, count, and areStronglyConnected - * operations take constant time. - * For alternate implementations of the same API, see + * The constructor takes Θ(V + E) time, + * where V is the number of vertices and E + * is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the digraph). + * For alternative implementations of the same API, see * {@link TarjanSCC} and {@link GabowSCC}. *

- * For additional documentation, - * see Section 4.2 of + * For additional documentation, see + * Section 4.2 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -123,7 +123,7 @@ public KosarajuSharirSCC(Digraph G) { } // DFS on graph G - private void dfs(Digraph G, int v) { + private void dfs(Digraph G, int v) { marked[v] = true; id[v] = count; for (int w : G.adj(v)) { @@ -220,7 +220,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/KruskalMST.java b/src/main/java/edu/princeton/cs/algs4/KruskalMST.java index ae710d4a3..bb80cb057 100644 --- a/src/main/java/edu/princeton/cs/algs4/KruskalMST.java +++ b/src/main/java/edu/princeton/cs/algs4/KruskalMST.java @@ -1,7 +1,7 @@ /****************************************************************************** * Compilation: javac KruskalMST.java * Execution: java KruskalMST filename.txt - * Dependencies: EdgeWeightedGraph.java Edge.java Queue.java + * Dependencies: EdgeWeightedGraph.java Edge.java Queue.java MinPQ.java * UF.java In.java StdOut.java * Data files: https://algs4.cs.princeton.edu/43mst/tinyEWG.txt * https://algs4.cs.princeton.edu/43mst/mediumEWG.txt @@ -9,7 +9,7 @@ * * Compute a minimum spanning forest using Kruskal's algorithm. * - * % java KruskalMST tinyEWG.txt + * % java KruskalMST tinyEWG.txt * 0-7 0.16000 * 2-3 0.17000 * 1-7 0.19000 @@ -36,23 +36,30 @@ package edu.princeton.cs.algs4; +import java.util.Arrays; + /** * The {@code KruskalMST} class represents a data type for computing a * minimum spanning tree in an edge-weighted graph. * The edge weights can be positive, zero, or negative and need not * be distinct. If the graph is not connected, it computes a minimum * spanning forest, which is the union of minimum spanning trees - * in each connected component. The {@code weight()} method returns the + * in each connected component. The {@code weight()} method returns the * weight of a minimum spanning tree and the {@code edges()} method * returns its edges. *

- * This implementation uses Krusal's algorithm and the + * This implementation uses Kruskal's algorithm and the * union-find data type. - * The constructor takes time proportional to E log E - * and extra space (not including the graph) proportional to V, - * where V is the number of vertices and E is the number of edges. - * Afterwards, the {@code weight()} method takes constant time - * and the {@code edges()} method takes time proportional to V. + * The constructor takes Θ(E log E) time in + * the worst case. + * Each instance method takes Θ(1) time. + * It uses Θ(E) extra space (not including the graph). + *

+ * This {@code weight()} method correctly computes the weight of the MST + * if all arithmetic performed is without floating-point rounding error + * or arithmetic overflow. + * This is the case if all edge weights are non-negative integers + * and the weight of the MST does not exceed 252. *

* For additional documentation, * see Section 4.3 of @@ -64,7 +71,7 @@ * @author Kevin Wayne */ public class KruskalMST { - private static final double FLOATING_POINT_EPSILON = 1E-12; + private static final double FLOATING_POINT_EPSILON = 1.0E-12; private double weight; // weight of MST private Queue mst = new Queue(); // edges in MST @@ -74,21 +81,26 @@ public class KruskalMST { * @param G the edge-weighted graph */ public KruskalMST(EdgeWeightedGraph G) { - // more efficient to build heap by passing array of edges - MinPQ pq = new MinPQ(); - for (Edge e : G.edges()) { - pq.insert(e); + + // create array of edges, sorted by weight + Edge[] edges = new Edge[G.E()]; + int t = 0; + for (Edge e: G.edges()) { + edges[t++] = e; } + Arrays.sort(edges); // run greedy algorithm UF uf = new UF(G.V()); - while (!pq.isEmpty() && mst.size() < G.V() - 1) { - Edge e = pq.delMin(); + for (int i = 0; i < G.E() && mst.size() < G.V() - 1; i++) { + Edge e = edges[i]; int v = e.either(); int w = e.other(v); - if (!uf.connected(v, w)) { // v-w does not create a cycle - uf.union(v, w); // merge v and w components - mst.enqueue(e); // add edge e to mst + + // v-w does not create a cycle + if (uf.find(v) != uf.find(w)) { + uf.union(v, w); // merge v and w components + mst.enqueue(e); // add edge e to mst weight += e.weight(); } } @@ -113,7 +125,7 @@ public Iterable edges() { public double weight() { return weight; } - + // check optimality conditions (takes time proportional to E V lg* V) private boolean check(EdgeWeightedGraph G) { @@ -131,7 +143,7 @@ private boolean check(EdgeWeightedGraph G) { UF uf = new UF(G.V()); for (Edge e : edges()) { int v = e.either(), w = e.other(v); - if (uf.connected(v, w)) { + if (uf.find(v) == uf.find(w)) { System.err.println("Not a forest"); return false; } @@ -141,7 +153,7 @@ private boolean check(EdgeWeightedGraph G) { // check that it is a spanning forest for (Edge e : G.edges()) { int v = e.either(), w = e.other(v); - if (!uf.connected(v, w)) { + if (uf.find(v) != uf.find(w)) { System.err.println("Not a spanning forest"); return false; } @@ -156,11 +168,11 @@ private boolean check(EdgeWeightedGraph G) { int x = f.either(), y = f.other(x); if (f != e) uf.union(x, y); } - + // check that e is min weight edge in crossing cut for (Edge f : G.edges()) { int x = f.either(), y = f.other(x); - if (!uf.connected(x, y)) { + if (uf.find(x) != uf.find(y)) { if (f.weight() < e.weight()) { System.err.println("Edge " + f + " violates cut optimality conditions"); return false; @@ -193,7 +205,7 @@ public static void main(String[] args) { /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/LSD.java b/src/main/java/edu/princeton/cs/algs4/LSD.java index d0fda4c71..1e3aee4e5 100644 --- a/src/main/java/edu/princeton/cs/algs4/LSD.java +++ b/src/main/java/edu/princeton/cs/algs4/LSD.java @@ -1,14 +1,14 @@ /****************************************************************************** * Compilation: javac LSD.java * Execution: java LSD < input.txt - * Dependencies: StdIn.java StdOut.java + * Dependencies: StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/51radix/words3.txt * * LSD radix sort * * - Sort a String[] array of n extended ASCII strings (R = 256), each of length w. * - * - Sort an int[] array of n 32-bit integers, treating each integer as + * - Sort an int[] array of n 32-bit integers, treating each integer as * a sequence of w = 4 bytes (R = 256). * * Uses extra space proportional to n + R. @@ -46,8 +46,8 @@ public class LSD { // do not instantiate private LSD() { } - /** - * Rearranges the array of W-character strings in ascending order. + /** + * Rearranges the array of w-character strings in ascending order. * * @param a the array to be sorted * @param w the number of characters per string @@ -81,24 +81,24 @@ public static void sort(String[] a, int w) { /** * Rearranges the array of 32-bit integers in ascending order. - * This is about 2-3x faster than Arrays.sort(). + * This is about 2-5x faster than Arrays.sort(). * * @param a the array to be sorted */ public static void sort(int[] a) { - final int BITS = 32; // each int is 32 bits - final int R = 1 << BITS_PER_BYTE; // each bytes is between 0 and 255 + final int BITS = 32; // each int is 32 bits + final int R = 1 << BITS_PER_BYTE; // each byte is between 0 and 255 final int MASK = R - 1; // 0xFF final int w = BITS / BITS_PER_BYTE; // each int is 4 bytes int n = a.length; int[] aux = new int[n]; - for (int d = 0; d < w; d++) { + for (int d = 0; d < w; d++) { // compute frequency counts int[] count = new int[R+1]; - for (int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { int c = (a[i] >> BITS_PER_BYTE*d) & MASK; count[c + 1]++; } @@ -123,12 +123,16 @@ public static void sort(int[] a) { aux[count[c]++] = a[i]; } - // copy back - for (int i = 0; i < n; i++) - a[i] = aux[i]; + // optimization: swap a[] and aux[] references instead of copying + // (since w is even, the argument a[] to sort() will be the array + // with the sorted integers) + int[] temp = a; + a = aux; + aux = temp; } } + /** * Reads in a sequence of fixed-length strings from standard input; * LSD radix sorts them; @@ -155,7 +159,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/LZW.java b/src/main/java/edu/princeton/cs/algs4/LZW.java index d8c8597e9..57469aca8 100644 --- a/src/main/java/edu/princeton/cs/algs4/LZW.java +++ b/src/main/java/edu/princeton/cs/algs4/LZW.java @@ -8,14 +8,7 @@ * * Compress or expand binary input from standard input using LZW. * - * WARNING: STARTING WITH ORACLE JAVA 6, UPDATE 7 the SUBSTRING - * METHOD TAKES TIME AND SPACE LINEAR IN THE SIZE OF THE EXTRACTED - * SUBSTRING (INSTEAD OF CONSTANT SPACE AND TIME AS IN EARLIER - * IMPLEMENTATIONS). - * - * See this article - * for more details. - * + ******************************************************************************/ package edu.princeton.cs.algs4; @@ -25,11 +18,18 @@ * and expanding a binary input using LZW compression over the 8-bit extended * ASCII alphabet with 12-bit codewords. *

+ * WARNING: Starting with Oracle Java 7u6, the substring method takes time and + * space linear in the length of the extracted substring (instead of constant + * time an space as in earlier versions). As a result, compression takes + * quadratic time. TODO: fix. + * See this article + * for more details. + *

* For additional documentation, - * see Section 5.5 of + * see Section 5.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * - * @author Robert Sedgewick + * @author Robert Sedgewick * @author Kevin Wayne */ public class LZW { @@ -45,11 +45,14 @@ private LZW() { } * them using LZW compression with 12-bit codewords; and writes the results * to standard output. */ - public static void compress() { + public static void compress() { String input = BinaryStdIn.readString(); TST st = new TST(); + + // since TST is not balanced, it would be better to insert in a different order for (int i = 0; i < R; i++) st.put("" + (char) i, i); + int code = R+1; // R is codeword for EOF while (input.length() > 0) { @@ -62,7 +65,7 @@ public static void compress() { } BinaryStdOut.write(R, W); BinaryStdOut.close(); - } + } /** * Reads a sequence of bit encoded using LZW compression with @@ -109,7 +112,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/LazyPrimMST.java b/src/main/java/edu/princeton/cs/algs4/LazyPrimMST.java index 890ccc0dc..2ec66407c 100644 --- a/src/main/java/edu/princeton/cs/algs4/LazyPrimMST.java +++ b/src/main/java/edu/princeton/cs/algs4/LazyPrimMST.java @@ -7,10 +7,10 @@ * https://algs4.cs.princeton.edu/43mst/mediumEWG.txt * https://algs4.cs.princeton.edu/43mst/largeEWG.txt * - * Compute a minimum spanning forest using a lazy version of Prim's + * Compute a minimum spanning forest using a lazy version of Prim's * algorithm. * - * % java LazyPrimMST tinyEWG.txt + * % java LazyPrimMST tinyEWG.txt * 0-7 0.16000 * 1-7 0.19000 * 0-2 0.26000 @@ -47,17 +47,18 @@ * The edge weights can be positive, zero, or negative and need not * be distinct. If the graph is not connected, it computes a minimum * spanning forest, which is the union of minimum spanning trees - * in each connected component. The {@code weight()} method returns the + * in each connected component. The {@code weight()} method returns the * weight of a minimum spanning tree and the {@code edges()} method * returns its edges. *

* This implementation uses a lazy version of Prim's algorithm * with a binary heap of edges. - * The constructor takes time proportional to E log E - * and extra space (not including the graph) proportional to E, - * where V is the number of vertices and E is the number of edges. - * Afterwards, the {@code weight()} method takes constant time - * and the {@code edges()} method takes time proportional to V. + * The constructor takes Θ(E log E) time in + * the worst case, where V is the number of vertices and + * E is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(E) extra space in the worst case + * (not including the edge-weighted graph). *

* For additional documentation, * see Section 4.3 of @@ -69,7 +70,7 @@ * @author Kevin Wayne */ public class LazyPrimMST { - private static final double FLOATING_POINT_EPSILON = 1E-12; + private static final double FLOATING_POINT_EPSILON = 1.0E-12; private double weight; // total weight of MST private Queue mst; // edges in the MST @@ -113,7 +114,7 @@ private void scan(EdgeWeightedGraph G, int v) { for (Edge e : G.adj(v)) if (!marked[e.other(v)]) pq.insert(e); } - + /** * Returns the edges in a minimum spanning tree (or forest). * @return the edges in a minimum spanning tree (or forest) as @@ -148,7 +149,7 @@ private boolean check(EdgeWeightedGraph G) { UF uf = new UF(G.V()); for (Edge e : edges()) { int v = e.either(), w = e.other(v); - if (uf.connected(v, w)) { + if (uf.find(v) == uf.find(w)) { System.err.println("Not a forest"); return false; } @@ -158,7 +159,7 @@ private boolean check(EdgeWeightedGraph G) { // check that it is a spanning forest for (Edge e : G.edges()) { int v = e.either(), w = e.other(v); - if (!uf.connected(v, w)) { + if (uf.find(v) != uf.find(w)) { System.err.println("Not a spanning forest"); return false; } @@ -177,7 +178,7 @@ private boolean check(EdgeWeightedGraph G) { // check that e is min weight edge in crossing cut for (Edge f : G.edges()) { int x = f.either(), y = f.other(x); - if (!uf.connected(x, y)) { + if (uf.find(x) != uf.find(y)) { if (f.weight() < e.weight()) { System.err.println("Edge " + f + " violates cut optimality conditions"); return false; @@ -189,8 +190,8 @@ private boolean check(EdgeWeightedGraph G) { return true; } - - + + /** * Unit tests the {@code LazyPrimMST} data type. * @@ -209,7 +210,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/LinearProbingHashST.java b/src/main/java/edu/princeton/cs/algs4/LinearProbingHashST.java index 2e5391e7b..19ef44991 100644 --- a/src/main/java/edu/princeton/cs/algs4/LinearProbingHashST.java +++ b/src/main/java/edu/princeton/cs/algs4/LinearProbingHashST.java @@ -3,7 +3,7 @@ * Execution: java LinearProbingHashST < input.txt * Dependencies: StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/34hash/tinyST.txt - * + * * Symbol-table implementation with linear-probing hash table. * ******************************************************************************/ @@ -41,6 +41,8 @@ * @author Kevin Wayne */ public class LinearProbingHashST { + + // must be a power of 2 private static final int INIT_CAPACITY = 4; private int n; // number of key-value pairs in the symbol table @@ -100,11 +102,19 @@ public boolean contains(Key key) { return get(key) != null; } - // hash function for keys - returns value between 0 and M-1 - private int hash(Key key) { + // hash function for keys - returns value between 0 and m-1 + private int hashTextbook(Key key) { return (key.hashCode() & 0x7fffffff) % m; } + // hash function for keys - returns value between 0 and m-1 (assumes m is a power of 2) + // (from Java 7 implementation, protects against poor quality hashCode() implementations) + private int hash(Key key) { + int h = key.hashCode(); + h ^= (h >>> 20) ^ (h >>> 12) ^ (h >>> 7) ^ (h >>> 4); + return h & (m-1); + } + // resizes the hash table to the given capacity by re-hashing all of the keys private void resize(int capacity) { LinearProbingHashST temp = new LinearProbingHashST(capacity); @@ -119,7 +129,7 @@ private void resize(int capacity) { } /** - * Inserts the specified key-value pair into the symbol table, overwriting the old + * Inserts the specified key-value pair into the symbol table, overwriting the old * value with the new value if the symbol table already contains the specified key. * Deletes the specified key (and its associated value) from this symbol table * if the specified value is {@code null}. @@ -167,8 +177,8 @@ public Value get(Key key) { } /** - * Removes the specified key and its associated value from this symbol table - * (if the key is in this symbol table). + * Removes the specified key and its associated value from this symbol table + * (if the key is in this symbol table). * * @param key the key * @throws IllegalArgumentException if {@code key} is {@code null} @@ -190,7 +200,7 @@ public void delete(Key key) { // rehash all keys in same cluster i = (i + 1) % m; while (keys[i] != null) { - // delete keys[i] an vals[i] and reinsert + // delete keys[i] and vals[i] and reinsert Key keyToRehash = keys[i]; Value valToRehash = vals[i]; keys[i] = null; @@ -223,7 +233,7 @@ public Iterable keys() { } // integrity check - don't check after each put() because - // integrity not maintained during a delete() + // integrity not maintained during a call to delete() private boolean check() { // check that hash table is at most 50% full @@ -249,7 +259,7 @@ else if (get(keys[i]) != vals[i]) { * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { LinearProbingHashST st = new LinearProbingHashST(); for (int i = 0; !StdIn.isEmpty(); i++) { String key = StdIn.readString(); @@ -257,13 +267,13 @@ public static void main(String[] args) { } // print keys - for (String s : st.keys()) - StdOut.println(s + " " + st.get(s)); + for (String s : st.keys()) + StdOut.println(s + " " + st.get(s)); } } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/LinearProgramming.java b/src/main/java/edu/princeton/cs/algs4/LinearProgramming.java index 525b5020c..45088d2c3 100644 --- a/src/main/java/edu/princeton/cs/algs4/LinearProgramming.java +++ b/src/main/java/edu/princeton/cs/algs4/LinearProgramming.java @@ -7,7 +7,7 @@ * n-length vector c, solve the LP { max cx : Ax <= b, x >= 0 }. * Assumes that b >= 0 so that x = 0 is a basic feasible solution. * - * Creates an (m+1)-by-(n+m+1) simplex tableaux with the + * Creates an (m+1)-by-(n+m+1) simplex tableaux with the * RHS in column m+n, the objective function in row m, and * slack variables in columns m through m+n-1. * @@ -17,18 +17,23 @@ /** * The {@code LinearProgramming} class represents a data type for solving a - * linear program of the form { max cx : Ax ≤ b, x ≥ 0 }, where A is a m-by-n - * matrix, b is an m-length vector, and c is an n-length vector. For simplicity, - * we assume that A is of full rank and that b ≥ 0 so that x = 0 is a basic - * feasible soution. + * linear program of the form { max cx : Ax ≤ b, x ≥ 0 }, where A is an + * m-by-n matrix, b is an m-length vector, and c is an n-length vector. + * For simplicity, we assume that A is of full rank and that b ≥ 0 + * so that x = 0 is a basic feasible solution. *

* The data type supplies methods for determining the optimal primal and * dual solutions. *

* This is a bare-bones implementation of the simplex algorithm. - * It uses Bland's rule to determing the entering and leaving variables. - * It is not suitable for use on large inputs. It is also not robust - * in the presence of floating-point roundoff error. + * It uses Bland's rule to determine the entering and leaving variables. + * It is not suitable for use on large inputs. + *

+ * This computes correct results if all arithmetic performed is + * without floating-point rounding error or arithmetic overflow. + * In practice, there will be floating-point rounding error + * and this implementation is not robust in the presence of + * such errors. *

* For additional documentation, see * Section 6.5 @@ -48,7 +53,7 @@ public class LinearProgramming { /** * Determines an optimal solution to the linear program - * { max cx : Ax ≤ b, x ≥ 0 }, where A is a m-by-n + * { max cx : Ax ≤ b, x ≥ 0 }, where A is an m-by-n * matrix, b is an m-length vector, and c is an n-length vector. * * @param A the m-by-b matrix @@ -56,7 +61,7 @@ public class LinearProgramming { * @param c the n-length cost vector * @throws IllegalArgumentException unless {@code b[i] >= 0} for each {@code i} * @throws ArithmeticException if the linear program is unbounded - */ + */ public LinearProgramming(double[][] A, double[] b, double[] c) { m = b.length; n = c.length; @@ -140,7 +145,7 @@ private void pivot(int p, int q) { // everything but row p and column q for (int i = 0; i <= m; i++) for (int j = 0; j <= m+n; j++) - if (i != p && j != q) a[i][j] -= a[p][j] * a[i][q] / a[p][q]; + if (i != p && j != q) a[i][j] -= a[p][j] * (a[i][q] / a[p][q]); // zero out column q for (int i = 0; i <= m; i++) @@ -181,8 +186,10 @@ public double[] primal() { */ public double[] dual() { double[] y = new double[m]; - for (int i = 0; i < m; i++) + for (int i = 0; i < m; i++) { y[i] = -a[m][n+i]; + if (y[i] == -0.0) y[i] = 0.0; + } return y; } @@ -193,7 +200,7 @@ private boolean isPrimalFeasible(double[][] A, double[] b) { // check that x >= 0 for (int j = 0; j < x.length; j++) { - if (x[j] < 0.0) { + if (x[j] < -EPSILON) { StdOut.println("x[" + j + "] = " + x[j] + " is negative"); return false; } @@ -220,7 +227,7 @@ private boolean isDualFeasible(double[][] A, double[] c) { // check that y >= 0 for (int i = 0; i < y.length; i++) { - if (y[i] < 0.0) { + if (y[i] < -EPSILON) { StdOut.println("y[" + i + "] = " + y[i] + " is negative"); return false; } @@ -383,12 +390,12 @@ public static void main(String[] args) { double[] b = new double[m]; double[][] A = new double[m][n]; for (int j = 0; j < n; j++) - c[j] = StdRandom.uniform(1000); + c[j] = StdRandom.uniformInt(1000); for (int i = 0; i < m; i++) - b[i] = StdRandom.uniform(1000); + b[i] = StdRandom.uniformInt(1000); for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) - A[i][j] = StdRandom.uniform(100); + A[i][j] = StdRandom.uniformInt(100); LinearProgramming lp = new LinearProgramming(A, b, c); test(A, b, c); } @@ -396,7 +403,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/LinearRegression.java b/src/main/java/edu/princeton/cs/algs4/LinearRegression.java index 9e2157f4c..90f516bb2 100644 --- a/src/main/java/edu/princeton/cs/algs4/LinearRegression.java +++ b/src/main/java/edu/princeton/cs/algs4/LinearRegression.java @@ -2,7 +2,7 @@ * Compilation: javac LinearRegression.java * Execution: java LinearRegression * Dependencies: none - * + * * Compute least squares solution to y = beta * x + alpha. * Simple linear regression. * @@ -154,7 +154,7 @@ public String toString() { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/LinkedBag.java b/src/main/java/edu/princeton/cs/algs4/LinkedBag.java index f1b54e87e..971466a51 100644 --- a/src/main/java/edu/princeton/cs/algs4/LinkedBag.java +++ b/src/main/java/edu/princeton/cs/algs4/LinkedBag.java @@ -5,7 +5,7 @@ * * A generic bag or multiset, implemented using a singly linked list. * - * % more tobe.txt + * % more tobe.txt * to be or not to - be - - that - - - is * * % java LinkedBag < tobe.txt @@ -33,8 +33,8 @@ import java.util.NoSuchElementException; /** - * The {@code LinkedBag} class represents a bag (or multiset) of - * generic items. It supports insertion and iterating over the + * The {@code LinkedBag} class represents a bag (or multiset) of + * generic items. It supports insertion and iterating over the * items in arbitrary order. *

* This implementation uses a singly linked list with a non-static nested class Node. @@ -99,15 +99,15 @@ public void add(Item item) { * Returns an iterator that iterates over the items in the bag. */ public Iterator iterator() { - return new ListIterator(); + return new LinkedIterator(); } // an iterator over a linked list - private class ListIterator implements Iterator { + private class LinkedIterator implements Iterator { private Node current; // creates a new iterator - public ListIterator() { + public LinkedIterator() { current = first; } @@ -116,16 +116,11 @@ public boolean hasNext() { return current != null; } - // this method is optional in Iterator interface - public void remove() { - throw new UnsupportedOperationException(); - } - // returns the next item in the iterator (and advances the iterator) public Item next() { if (!hasNext()) throw new NoSuchElementException(); Item item = current.item; - current = current.next; + current = current.next; return item; } } @@ -152,7 +147,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/LinkedQueue.java b/src/main/java/edu/princeton/cs/algs4/LinkedQueue.java index d46e18676..37b7646bf 100644 --- a/src/main/java/edu/princeton/cs/algs4/LinkedQueue.java +++ b/src/main/java/edu/princeton/cs/algs4/LinkedQueue.java @@ -2,11 +2,11 @@ * Compilation: javac LinkedQueue.java * Execution: java LinkedQueue < input.txt * Dependencies: StdIn.java StdOut.java - * Data files: https://algs4.cs.princeton.edu/13stacks/tobe.txt + * Data files: https://algs4.cs.princeton.edu/13stacks/tobe.txt * * A generic queue, implemented using a singly linked list. * - * % java Queue < tobe.txt + * % java Queue < tobe.txt * to be or not to be (2 left on queue) * ******************************************************************************/ @@ -24,7 +24,7 @@ * testing if the queue is empty, and iterating through * the items in FIFO order. *

- * This implementation uses a singly linked list with a non-static nested class + * This implementation uses a singly linked list with a non-static nested class * for linked-list nodes. See {@link Queue} for a version that uses a static nested class. * The enqueue, dequeue, peek, size, and is-empty * operations all take constant time in the worst case. @@ -69,7 +69,7 @@ public boolean isEmpty() { * @return the number of items in this queue */ public int size() { - return n; + return n; } /** @@ -121,7 +121,7 @@ public String toString() { for (Item item : this) s.append(item + " "); return s.toString(); - } + } // check internal invariants private boolean check() { @@ -159,28 +159,29 @@ else if (n == 1) { } return true; - } - + } + /** * Returns an iterator that iterates over the items in this queue in FIFO order. * @return an iterator that iterates over the items in this queue in FIFO order */ public Iterator iterator() { - return new ListIterator(); + return new LinkedIterator(); } - // an iterator, doesn't implement remove() since it's optional - private class ListIterator implements Iterator { + // a linked-list iterator + private class LinkedIterator implements Iterator { private Node current = first; - public boolean hasNext() { return current != null; } - public void remove() { throw new UnsupportedOperationException(); } + public boolean hasNext() { + return current != null; + } public Item next() { if (!hasNext()) throw new NoSuchElementException(); Item item = current.item; - current = current.next; + current = current.next; return item; } } @@ -205,7 +206,7 @@ else if (!queue.isEmpty()) } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/LinkedStack.java b/src/main/java/edu/princeton/cs/algs4/LinkedStack.java index 116698e88..821aa43b9 100644 --- a/src/main/java/edu/princeton/cs/algs4/LinkedStack.java +++ b/src/main/java/edu/princeton/cs/algs4/LinkedStack.java @@ -6,8 +6,8 @@ * * A generic stack, implemented using a linked list. Each stack * element is of type Item. - * - * % more tobe.txt + * + * % more tobe.txt * to be or not to - be - - that - - - is * * % java LinkedStack < tobe.txt @@ -28,7 +28,7 @@ * for peeking at the top item, testing if the stack is empty, and iterating through * the items in LIFO order. *

- * This implementation uses a singly linked list with a non-static nested class for + * This implementation uses a singly linked list with a non-static nested class for * linked-list nodes. See {@link Stack} for a version that uses a static nested class. * The push, pop, peek, size, and is-empty * operations all take constant time in the worst case. @@ -123,25 +123,27 @@ public String toString() { s.append(item + " "); return s.toString(); } - + /** * Returns an iterator to this stack that iterates through the items in LIFO order. * @return an iterator to this stack that iterates through the items in LIFO order. */ public Iterator iterator() { - return new ListIterator(); + return new LinkedIterator(); } - // an iterator, doesn't implement remove() since it's optional - private class ListIterator implements Iterator { + // a linked-list iterator + private class LinkedIterator implements Iterator { private Node current = first; - public boolean hasNext() { return current != null; } - public void remove() { throw new UnsupportedOperationException(); } + + public boolean hasNext() { + return current != null; + } public Item next() { if (!hasNext()) throw new NoSuchElementException(); Item item = current.item; - current = current.next; + current = current.next; return item; } } @@ -196,7 +198,7 @@ else if (!stack.isEmpty()) /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/LongestCommonSubstring.java b/src/main/java/edu/princeton/cs/algs4/LongestCommonSubstring.java index 05931c596..6974cf5a0 100644 --- a/src/main/java/edu/princeton/cs/algs4/LongestCommonSubstring.java +++ b/src/main/java/edu/princeton/cs/algs4/LongestCommonSubstring.java @@ -4,10 +4,10 @@ * Dependencies: SuffixArray.java In.java StdOut.java * Data files: https://algs4.cs.princeton.edu/63suffix/tale.txt * https://algs4.cs.princeton.edu/63suffix/mobydick.txt - * + * * Read in two text files and find the longest substring that * appears in both texts. - * + * * % java LongestCommonSubstring tale.txt mobydick.txt * ' seemed on the point of being ' * @@ -29,7 +29,7 @@ * see Section 6.3 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. *

- * + * * @author Robert Sedgewick * @author Kevin Wayne */ @@ -73,7 +73,7 @@ public static String lcs(String s, String t) { SuffixArray suffix1 = new SuffixArray(s); SuffixArray suffix2 = new SuffixArray(t); - // find longest common substring by "merging" sorted suffixes + // find longest common substring by "merging" sorted suffixes String lcs = ""; int i = 0, j = 0; while (i < s.length() && j < t.length()) { @@ -106,7 +106,7 @@ public static void main(String[] args) { /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/LongestRepeatedSubstring.java b/src/main/java/edu/princeton/cs/algs4/LongestRepeatedSubstring.java index c47150000..2c32a7608 100644 --- a/src/main/java/edu/princeton/cs/algs4/LongestRepeatedSubstring.java +++ b/src/main/java/edu/princeton/cs/algs4/LongestRepeatedSubstring.java @@ -5,17 +5,17 @@ * Data files: https://algs4.cs.princeton.edu/63suffix/tale.txt * https://algs4.cs.princeton.edu/63suffix/tinyTale.txt * https://algs4.cs.princeton.edu/63suffix/mobydick.txt - * - * Reads a text string from stdin, replaces all consecutive blocks of - * whitespace with a single space, and then computes the longest + * + * Reads a text string from standard input, replaces all consecutive blocks + * of whitespace with a single space, and then computes the longest * repeated substring in that text using a suffix array. - * - * % java LongestRepeatedSubstring < tinyTale.txt + * + * % java LongestRepeatedSubstring < tinyTale.txt * 'st of times it was the ' * * % java LongestRepeatedSubstring < mobydick.txt * ',- Such a funny, sporty, gamy, jesty, joky, hoky-poky lad, is the Ocean, oh! Th' - * + * * % java LongestRepeatedSubstring * aaaaaaaaa * 'aaaaaaaa' @@ -89,7 +89,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/LookupCSV.java b/src/main/java/edu/princeton/cs/algs4/LookupCSV.java index 190614925..15b1477a7 100644 --- a/src/main/java/edu/princeton/cs/algs4/LookupCSV.java +++ b/src/main/java/edu/princeton/cs/algs4/LookupCSV.java @@ -8,23 +8,23 @@ * https://algs4.cs.princeton.edu/35applications/elements.csv * https://algs4.cs.princeton.edu/35applications/ip.csv * https://algs4.cs.princeton.edu/35applications/morse.csv - * + * * Reads in a set of key-value pairs from a two-column CSV file * specified on the command line; then, reads in keys from standard * input and prints out corresponding values. - * - * % java LookupCSV amino.csv 0 3 % java LookupCSV ip.csv 0 1 - * TTA www.google.com - * Leucine 216.239.41.99 - * ABC - * Not found % java LookupCSV ip.csv 1 0 - * TCT 216.239.41.99 - * Serine www.google.com - * - * % java LookupCSV amino.csv 3 0 % java LookupCSV DJIA.csv 0 1 - * Glycine 29-Oct-29 - * GGG 252.38 - * 20-Oct-87 + * + * % java LookupCSV amino.csv 0 3 % java LookupCSV ip.csv 0 1 + * TTA www.google.com + * Leucine 216.239.41.99 + * ABC + * Not found % java LookupCSV ip.csv 1 0 + * TCT 216.239.41.99 + * Serine www.google.com + * + * % java LookupCSV amino.csv 3 0 % java LookupCSV DJIA.csv 0 1 + * Glycine 29-Oct-29 + * GGG 252.38 + * 20-Oct-87 * 1738.74 * * @@ -40,7 +40,7 @@ *

* For additional documentation, see Section 3.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. - * + * * @author Robert Sedgewick * @author Kevin Wayne */ @@ -75,7 +75,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/LookupIndex.java b/src/main/java/edu/princeton/cs/algs4/LookupIndex.java index bcda021e4..86180e45d 100644 --- a/src/main/java/edu/princeton/cs/algs4/LookupIndex.java +++ b/src/main/java/edu/princeton/cs/algs4/LookupIndex.java @@ -44,11 +44,11 @@ *

* For additional documentation, see Section 3.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. - * + * * @author Robert Sedgewick * @author Kevin Wayne */ -public class LookupIndex { +public class LookupIndex { // Do not instantiate. private LookupIndex() { } @@ -79,10 +79,10 @@ public static void main(String[] args) { // read queries from standard input, one per line while (!StdIn.isEmpty()) { String query = StdIn.readLine(); - if (st.contains(query)) + if (st.contains(query)) for (String vals : st.get(query)) StdOut.println(" " + vals); - if (ts.contains(query)) + if (ts.contains(query)) for (String keys : ts.get(query)) StdOut.println(" " + keys); } @@ -92,7 +92,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/MSD.java b/src/main/java/edu/princeton/cs/algs4/MSD.java index f0603aec5..9fd1c2cf6 100644 --- a/src/main/java/edu/princeton/cs/algs4/MSD.java +++ b/src/main/java/edu/princeton/cs/algs4/MSD.java @@ -1,13 +1,13 @@ /****************************************************************************** * Compilation: javac MSD.java * Execution: java MSD < input.txt - * Dependencies: StdIn.java StdOut.java + * Dependencies: StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/51radix/words3.txt * https://algs4.cs.princeton.edu/51radix/shells.txt * * Sort an array of strings or integers using MSD radix sort. * - * % java MSD < shells.txt + * % java MSD < shells.txt * are * by * sea @@ -40,12 +40,12 @@ */ public class MSD { private static final int BITS_PER_BYTE = 8; - private static final int BITS_PER_INT = 32; // each Java int is 32 bits + private static final int BITS_PER_INT = 32; // each Java int is 32 bits private static final int R = 256; // extended ASCII alphabet size private static final int CUTOFF = 15; // cutoff to insertion sort // do not instantiate - private MSD() { } + private MSD() { } /** * Rearranges the array of extended ASCII strings in ascending order. @@ -81,7 +81,7 @@ private static void sort(String[] a, int lo, int hi, int d, String[] aux) { count[c+2]++; } - // transform counts to indicies + // transform counts to indices for (int r = 0; r < R+1; r++) count[r+1] += count[r]; @@ -92,7 +92,7 @@ private static void sort(String[] a, int lo, int hi, int d, String[] aux) { } // copy back - for (int i = lo; i <= hi; i++) + for (int i = lo; i <= hi; i++) a[i] = aux[i - lo]; @@ -144,7 +144,7 @@ private static void sort(int[] a, int lo, int hi, int d, int[] aux) { // cutoff to insertion sort for small subarrays if (hi <= lo + CUTOFF) { - insertion(a, lo, hi, d); + insertion(a, lo, hi); return; } @@ -157,21 +157,21 @@ private static void sort(int[] a, int lo, int hi, int d, int[] aux) { count[c + 1]++; } - // transform counts to indicies + // transform counts to indices for (int r = 0; r < R; r++) count[r+1] += count[r]; -/************* BUGGGY CODE. // for most significant byte, 0x80-0xFF comes before 0x00-0x7F if (d == 0) { int shift1 = count[R] - count[R/2]; int shift2 = count[R/2]; + count[R] = shift1 + count[1]; // to simplify recursive calls later for (int r = 0; r < R/2; r++) count[r] += shift1; for (int r = R/2; r < R; r++) count[r] -= shift2; } -************************************/ + // distribute for (int i = lo; i <= hi; i++) { int c = (a[i] >> shift) & mask; @@ -179,22 +179,29 @@ private static void sort(int[] a, int lo, int hi, int d, int[] aux) { } // copy back - for (int i = lo; i <= hi; i++) + for (int i = lo; i <= hi; i++) a[i] = aux[i - lo]; // no more bits - if (d == 4) return; + if (d == 3) return; - // recursively sort for each character - if (count[0] > 0) + // special case for most significant byte + if (d == 0 && count[R/2] > 0) + sort(a, lo, lo + count[R/2] - 1, d+1, aux); + + // special case for other bytes + if (d != 0 && count[0] > 0) sort(a, lo, lo + count[0] - 1, d+1, aux); + + // recursively sort for each character + // (could skip r = R/2 for d = 0 and skip r = R for d > 0) for (int r = 0; r < R; r++) if (count[r+1] > count[r]) sort(a, lo + count[r], lo + count[r+1] - 1, d+1, aux); } - // TODO: insertion sort a[lo..hi], starting at dth character - private static void insertion(int[] a, int lo, int hi, int d) { + // insertion sort a[lo..hi] + private static void insertion(int[] a, int lo, int hi) { for (int i = lo; i <= hi; i++) for (int j = i; j > lo && a[j] < a[j-1]; j--) exch(a, j, j-1); @@ -225,7 +232,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/MaxPQ.java b/src/main/java/edu/princeton/cs/algs4/MaxPQ.java index fe70deab1..ccba82f7f 100644 --- a/src/main/java/edu/princeton/cs/algs4/MaxPQ.java +++ b/src/main/java/edu/princeton/cs/algs4/MaxPQ.java @@ -3,12 +3,12 @@ * Execution: java MaxPQ < input.txt * Dependencies: StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/24pq/tinyPQ.txt - * + * * Generic max priority queue implementation with a binary heap. * Can be used with a comparator instead of the natural order, * but the generic Key type must still be Comparable. * - * % java MaxPQ < tinyPQ.txt + * % java MaxPQ < tinyPQ.txt * Q X P (6 left on pq) * * We use a one-based array to simplify parent and child calculations. @@ -31,14 +31,18 @@ * testing if the priority queue is empty, and iterating through * the keys. *

- * This implementation uses a binary heap. + * This implementation uses a binary heap. * The insert and delete-the-maximum operations take - * logarithmic amortized time. - * The max, size, and is-empty operations take constant time. - * Construction takes time proportional to the specified capacity or the number of - * items used to initialize the data structure. + * Θ(log n) amortized time, where n is the number + * of elements in the priority queue. This is an amortized bound + * (and not a worst-case bound) because of array resizing operations. + * The min, size, and is-empty operations take + * Θ(1) time in the worst case. + * Construction takes time proportional to the specified capacity or the + * number of items used to initialize the data structure. *

- * For additional documentation, see Section 2.4 of + * For additional documentation, see + * Section 2.4 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -106,7 +110,7 @@ public MaxPQ(Key[] keys) { sink(k); assert isMaxHeap(); } - + /** @@ -139,7 +143,7 @@ public Key max() { return pq[1]; } - // helper function to double the size of the heap array + // resize the underlying array to have the given capacity private void resize(int capacity) { assert capacity > n; Key[] temp = (Key[]) new Object[capacity]; @@ -177,7 +181,7 @@ public Key delMax() { Key max = pq[1]; exch(1, n--); sink(1); - pq[n+1] = null; // to avoid loiterig and help with garbage collection + pq[n+1] = null; // to avoid loitering and help with garbage collection if ((n > 0) && (n == (pq.length - 1) / 4)) resize(pq.length / 2); assert isMaxHeap(); return max; @@ -190,7 +194,7 @@ public Key delMax() { private void swim(int k) { while (k > 1 && less(k/2, k)) { - exch(k, k/2); + exch(k/2, k); k = k/2; } } @@ -223,19 +227,26 @@ private void exch(int i, int j) { pq[j] = swap; } - // is pq[1..N] a max heap? + // is pq[1..n] a max heap? private boolean isMaxHeap() { - return isMaxHeap(1); + for (int i = 1; i <= n; i++) { + if (pq[i] == null) return false; + } + for (int i = n+1; i < pq.length; i++) { + if (pq[i] != null) return false; + } + if (pq[0] != null) return false; + return isMaxHeapOrdered(1); } // is subtree of pq[1..n] rooted at k a max heap? - private boolean isMaxHeap(int k) { + private boolean isMaxHeapOrdered(int k) { if (k > n) return true; int left = 2*k; int right = 2*k + 1; if (left <= n && less(k, left)) return false; if (right <= n && less(k, right)) return false; - return isMaxHeap(left) && isMaxHeap(right); + return isMaxHeapOrdered(left) && isMaxHeapOrdered(right); } @@ -295,7 +306,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Merge.java b/src/main/java/edu/princeton/cs/algs4/Merge.java index beb6ee8a4..af519bcc1 100644 --- a/src/main/java/edu/princeton/cs/algs4/Merge.java +++ b/src/main/java/edu/princeton/cs/algs4/Merge.java @@ -4,30 +4,40 @@ * Dependencies: StdOut.java StdIn.java * Data files: https://algs4.cs.princeton.edu/22mergesort/tiny.txt * https://algs4.cs.princeton.edu/22mergesort/words3.txt - * + * * Sorts a sequence of strings from standard input using mergesort. - * + * * % more tiny.txt * S O R T E X A M P L E * * % java Merge < tiny.txt * A E E L M O P R S T X [ one string per line ] - * + * * % more words3.txt * bed bug dad yes zoo ... all bad yet - * + * * % java Merge < words3.txt * all bad bed bug dad ... yes yet zoo [ one string per line ] - * + * ******************************************************************************/ package edu.princeton.cs.algs4; /** * The {@code Merge} class provides static methods for sorting an - * array using mergesort. + * array using a top-down, recursive version of mergesort. *

- * For additional documentation, see Section 2.2 of + * This implementation takes Θ(n log n) time + * to sort any array of length n (assuming comparisons + * take constant time). It makes between + * ~ ½ n log2 n and + * ~ 1 n log2 n compares. + *

+ * This sorting algorithm is stable. + * It uses Θ(n) extra memory (not including the input array). + *

+ * For additional documentation, see + * Section 2.2 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * For an optimized version, see {@link MergeX}. * @@ -47,7 +57,7 @@ private static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int // copy to aux[] for (int k = lo; k <= hi; k++) { - aux[k] = a[k]; + aux[k] = a[k]; } // merge back to a[] @@ -86,12 +96,12 @@ public static void sort(Comparable[] a) { /*************************************************************************** * Helper sorting function. ***************************************************************************/ - + // is v < w ? private static boolean less(Comparable v, Comparable w) { return v.compareTo(w) < 0; } - + /*************************************************************************** * Check if array is sorted - useful for debugging. ***************************************************************************/ @@ -114,7 +124,7 @@ private static void merge(Comparable[] a, int[] index, int[] aux, int lo, int mi // copy to aux[] for (int k = lo; k <= hi; k++) { - aux[k] = index[k]; + aux[k] = index[k]; } // merge back to a[] @@ -131,7 +141,7 @@ private static void merge(Comparable[] a, int[] index, int[] aux, int lo, int mi * Returns a permutation that gives the elements in the array in ascending order. * @param a the array * @return a permutation {@code p[]} such that {@code a[p[0]]}, {@code a[p[1]]}, - * ..., {@code a[p[N-1]]} are in ascending order + * ..., {@code a[p[n-1]]} are in ascending order */ public static int[] indexSort(Comparable[] a) { int n = a.length; @@ -161,8 +171,8 @@ private static void show(Comparable[] a) { } /** - * Reads in a sequence of strings from standard input; mergesorts them; - * and prints them to standard output in ascending order. + * Reads in a sequence of strings from standard input; mergesorts them; + * and prints them to standard output in ascending order. * * @param args the command-line arguments */ @@ -174,7 +184,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/MergeBU.java b/src/main/java/edu/princeton/cs/algs4/MergeBU.java index e87b72895..711aa7fa0 100644 --- a/src/main/java/edu/princeton/cs/algs4/MergeBU.java +++ b/src/main/java/edu/princeton/cs/algs4/MergeBU.java @@ -4,19 +4,19 @@ * Dependencies: StdOut.java StdIn.java * Data files: https://algs4.cs.princeton.edu/22mergesort/tiny.txt * https://algs4.cs.princeton.edu/22mergesort/words3.txt - * + * * Sorts a sequence of strings from standard input using * bottom-up mergesort. - * + * * % more tiny.txt * S O R T E X A M P L E * * % java MergeBU < tiny.txt * A E E L M O P R S T X [ one string per line ] - * + * * % more words3.txt * bed bug dad yes zoo ... all bad yet - * + * * % java MergeBU < words3.txt * all bad bed bug dad ... yes yet zoo [ one string per line ] * @@ -26,9 +26,19 @@ /** * The {@code MergeBU} class provides static methods for sorting an - * array using bottom-up mergesort. + * array using bottom-up mergesort. It is non-recursive. *

- * For additional documentation, see Section 2.1 of + * This implementation takes Θ(n log n) time + * to sort any array of length n (assuming comparisons + * take constant time). It makes between + * ~ ½ n log2 n and + * ~ 1 n log2 n compares. + *

+ * This sorting algorithm is stable. + * It uses Θ(n) extra memory (not including the input array). + *

+ * For additional documentation, see + * Section 2.1 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -44,13 +54,13 @@ private static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int // copy to aux[] for (int k = lo; k <= hi; k++) { - aux[k] = a[k]; + aux[k] = a[k]; } // merge back to a[] int i = lo, j = mid+1; for (int k = lo; k <= hi; k++) { - if (i > mid) a[k] = aux[j++]; // this copying is unneccessary + if (i > mid) a[k] = aux[j++]; // this copying is unnecessary else if (j > hi) a[k] = aux[i++]; else if (less(aux[j], aux[i])) a[k] = aux[j++]; else a[k] = aux[i++]; @@ -78,7 +88,7 @@ public static void sort(Comparable[] a) { /*********************************************************************** * Helper sorting functions. ***************************************************************************/ - + // is v < w ? private static boolean less(Comparable v, Comparable w) { return v.compareTo(w) < 0; @@ -103,7 +113,7 @@ private static void show(Comparable[] a) { /** * Reads in a sequence of strings from standard input; bottom-up - * mergesorts them; and prints them to standard output in ascending order. + * mergesorts them; and prints them to standard output in ascending order. * * @param args the command-line arguments */ @@ -115,7 +125,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/MergeX.java b/src/main/java/edu/princeton/cs/algs4/MergeX.java index 62ae118e9..72f9b23d3 100644 --- a/src/main/java/edu/princeton/cs/algs4/MergeX.java +++ b/src/main/java/edu/princeton/cs/algs4/MergeX.java @@ -4,19 +4,19 @@ * Dependencies: StdOut.java StdIn.java * Data files: https://algs4.cs.princeton.edu/22mergesort/tiny.txt * https://algs4.cs.princeton.edu/22mergesort/words3.txt - * + * * Sorts a sequence of strings from standard input using an * optimized version of mergesort. - * + * * % more tiny.txt * S O R T E X A M P L E * * % java MergeX < tiny.txt * A E E L M O P R S T X [ one string per line ] - * + * * % more words3.txt * bed bug dad yes zoo ... all bad yet - * + * * % java MergeX < words3.txt * all bad bed bug dad ... yes yet zoo [ one string per line ] * @@ -30,7 +30,15 @@ * The {@code MergeX} class provides static methods for sorting an * array using an optimized version of mergesort. *

- * For additional documentation, see Section 2.2 of + * In the worst case, this implementation takes + * Θ(n log n) time to sort an array of + * length n (assuming comparisons take constant time). + *

+ * This sorting algorithm is stable. + * It uses Θ(n) extra memory (not including the input array). + *

+ * For additional documentation, see + * Section 2.2 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -62,7 +70,7 @@ private static void merge(Comparable[] src, Comparable[] dst, int lo, int mid, i private static void sort(Comparable[] src, Comparable[] dst, int lo, int hi) { // if (hi <= lo) return; - if (hi <= lo + CUTOFF) { + if (hi <= lo + CUTOFF) { insertionSort(dst, lo, hi); return; } @@ -90,7 +98,7 @@ private static void sort(Comparable[] src, Comparable[] dst, int lo, int hi) { */ public static void sort(Comparable[] a) { Comparable[] aux = a.clone(); - sort(aux, a, 0, a.length-1); + sort(aux, a, 0, a.length-1); assert isSorted(a); } @@ -161,7 +169,7 @@ private static void merge(Object[] src, Object[] dst, int lo, int mid, int hi, C private static void sort(Object[] src, Object[] dst, int lo, int hi, Comparator comparator) { // if (hi <= lo) return; - if (hi <= lo + CUTOFF) { + if (hi <= lo + CUTOFF) { insertionSort(dst, lo, hi, comparator); return; } @@ -218,8 +226,8 @@ private static void show(Object[] a) { /** * Reads in a sequence of strings from standard input; mergesorts them - * (using an optimized version of mergesort); - * and prints them to standard output in ascending order. + * (using an optimized version of mergesort); + * and prints them to standard output in ascending order. * * @param args the command-line arguments */ @@ -231,7 +239,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/MinPQ.java b/src/main/java/edu/princeton/cs/algs4/MinPQ.java index 29005daab..5d75e578e 100644 --- a/src/main/java/edu/princeton/cs/algs4/MinPQ.java +++ b/src/main/java/edu/princeton/cs/algs4/MinPQ.java @@ -3,7 +3,7 @@ * Execution: java MinPQ < input.txt * Dependencies: StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/24pq/tinyPQ.txt - * + * * Generic min priority queue implementation with a binary heap. * Can be used with a comparator instead of the natural order. * @@ -30,14 +30,18 @@ * testing if the priority queue is empty, and iterating through * the keys. *

- * This implementation uses a binary heap. + * This implementation uses a binary heap. * The insert and delete-the-minimum operations take - * logarithmic amortized time. - * The min, size, and is-empty operations take constant time. - * Construction takes time proportional to the specified capacity or the number of - * items used to initialize the data structure. + * Θ(log n) amortized time, where n is the number + * of elements in the priority queue. This is an amortized bound + * (and not a worst-case bound) because of array resizing operations. + * The min, size, and is-empty operations take + * Θ(1) time in the worst case. + * Construction takes time proportional to the specified capacity or the + * number of items used to initialize the data structure. *

- * For additional documentation, see Section 2.4 of + * For additional documentation, see + * Section 2.4 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -136,7 +140,7 @@ public Key min() { return pq[1]; } - // helper function to double the size of the heap array + // resize the underlying array to have the given capacity private void resize(int capacity) { assert capacity > n; Key[] temp = (Key[]) new Object[capacity]; @@ -172,7 +176,7 @@ public Key delMin() { Key min = pq[1]; exch(1, n--); sink(1); - pq[n+1] = null; // to avoid loiterig and help with garbage collection + pq[n+1] = null; // to avoid loitering and help with garbage collection if ((n > 0) && (n == (pq.length - 1) / 4)) resize(pq.length / 2); assert isMinHeap(); return min; @@ -185,7 +189,7 @@ public Key delMin() { private void swim(int k) { while (k > 1 && greater(k/2, k)) { - exch(k, k/2); + exch(k/2, k); k = k/2; } } @@ -218,19 +222,26 @@ private void exch(int i, int j) { pq[j] = swap; } - // is pq[1..N] a min heap? + // is pq[1..n] a min heap? private boolean isMinHeap() { - return isMinHeap(1); + for (int i = 1; i <= n; i++) { + if (pq[i] == null) return false; + } + for (int i = n+1; i < pq.length; i++) { + if (pq[i] != null) return false; + } + if (pq[0] != null) return false; + return isMinHeapOrdered(1); } // is subtree of pq[1..n] rooted at k a min heap? - private boolean isMinHeap(int k) { + private boolean isMinHeapOrdered(int k) { if (k > n) return true; int left = 2*k; int right = 2*k + 1; if (left <= n && greater(k, left)) return false; if (right <= n && greater(k, right)) return false; - return isMinHeap(left) && isMinHeap(right); + return isMinHeapOrdered(left) && isMinHeapOrdered(right); } @@ -286,7 +297,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Multiway.java b/src/main/java/edu/princeton/cs/algs4/Multiway.java index dff2ff12a..6d64e5458 100644 --- a/src/main/java/edu/princeton/cs/algs4/Multiway.java +++ b/src/main/java/edu/princeton/cs/algs4/Multiway.java @@ -5,21 +5,21 @@ * Data files: https://algs4.cs.princeton.edu/24pq/m1.txt * https://algs4.cs.princeton.edu/24pq/m2.txt * https://algs4.cs.princeton.edu/24pq/m3.txt - * + * * Merges together the sorted input stream given as command-line arguments * into a single sorted output stream on standard output. * - * % more m1.txt + * % more m1.txt * A B C F G I I Z * - * % more m2.txt + * % more m2.txt * B D H P Q Q - * - * % more m3.txt + * + * % more m3.txt * A B E F J N * - * % java Multiway m1.txt m2.txt m3.txt - * A A B B B C D E F F G H I I J N P Q Q Z + * % java Multiway m1.txt m2.txt m3.txt + * A A B B B C D E F F G H I I J N P Q Q Z * ******************************************************************************/ @@ -30,7 +30,7 @@ * sorted text files and merging them together into a single sorted * text stream. * This implementation uses a {@link IndexMinPQ} to perform the multiway - * merge. + * merge. *

* For additional documentation, see Section 2.4 * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. @@ -39,7 +39,7 @@ * @author Kevin Wayne */ -public class Multiway { +public class Multiway { // This class should not be instantiated. private Multiway() { } @@ -52,7 +52,7 @@ private static void merge(In[] streams) { if (!streams[i].isEmpty()) pq.insert(i, streams[i].readString()); - // Extract and print min and read next from its stream. + // Extract and print min and read next from its stream. while (!pq.isEmpty()) { StdOut.print(pq.minKey() + " "); int i = pq.delMin(); @@ -81,7 +81,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/MultiwayMinPQ.java b/src/main/java/edu/princeton/cs/algs4/MultiwayMinPQ.java index 65d3f63d4..fae2520d2 100644 --- a/src/main/java/edu/princeton/cs/algs4/MultiwayMinPQ.java +++ b/src/main/java/edu/princeton/cs/algs4/MultiwayMinPQ.java @@ -305,7 +305,7 @@ public int compare(Key key1, Key key2) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/NFA.java b/src/main/java/edu/princeton/cs/algs4/NFA.java index 2686af151..575d62a38 100644 --- a/src/main/java/edu/princeton/cs/algs4/NFA.java +++ b/src/main/java/edu/princeton/cs/algs4/NFA.java @@ -36,7 +36,7 @@ * closure, binary or, and parentheses. * It does not support mutiway or, character classes, * metacharacters (either in the text or pattern), - * capturing capabilities, greedy or relucantant + * capturing capabilities, greedy or reluctant * modifiers, and other features in industrial-strength implementations * such as {@link java.util.regex.Pattern} and {@link java.util.regex.Matcher}. *

@@ -54,7 +54,7 @@ * @author Robert Sedgewick * @author Kevin Wayne */ -public class NFA { +public class NFA { private Digraph graph; // digraph of epsilon transitions private String regexp; // regular expression @@ -68,17 +68,17 @@ public class NFA { public NFA(String regexp) { this.regexp = regexp; m = regexp.length(); - Stack ops = new Stack(); - graph = new Digraph(m+1); - for (int i = 0; i < m; i++) { - int lp = i; - if (regexp.charAt(i) == '(' || regexp.charAt(i) == '|') - ops.push(i); + Stack ops = new Stack(); + graph = new Digraph(m+1); + for (int i = 0; i < m; i++) { + int lp = i; + if (regexp.charAt(i) == '(' || regexp.charAt(i) == '|') + ops.push(i); else if (regexp.charAt(i) == ')') { - int or = ops.pop(); + int or = ops.pop(); // 2-way or operator - if (regexp.charAt(or) == '|') { + if (regexp.charAt(or) == '|') { lp = ops.pop(); graph.addEdge(lp, or+1); graph.addEdge(or, i); @@ -86,23 +86,23 @@ else if (regexp.charAt(i) == ')') { else if (regexp.charAt(or) == '(') lp = or; else assert false; - } + } // closure operator (uses 1-character lookahead) - if (i < m-1 && regexp.charAt(i+1) == '*') { - graph.addEdge(lp, i+1); - graph.addEdge(i+1, lp); - } - if (regexp.charAt(i) == '(' || regexp.charAt(i) == '*' || regexp.charAt(i) == ')') + if (i < m-1 && regexp.charAt(i+1) == '*') { + graph.addEdge(lp, i+1); + graph.addEdge(i+1, lp); + } + if (regexp.charAt(i) == '(' || regexp.charAt(i) == '*' || regexp.charAt(i) == ')') graph.addEdge(i, i+1); } if (ops.size() != 0) throw new IllegalArgumentException("Invalid regular expression"); - } + } /** * Returns true if the text is matched by the regular expression. - * + * * @param txt the text * @return {@code true} if the text is matched by the regular expression, * {@code false} otherwise @@ -122,9 +122,11 @@ public boolean recognizes(String txt) { for (int v : pc) { if (v == m) continue; if ((regexp.charAt(v) == txt.charAt(i)) || regexp.charAt(v) == '.') - match.add(v+1); + match.add(v+1); } - dfs = new DirectedDFS(graph, match); + if (match.isEmpty()) continue; + + dfs = new DirectedDFS(graph, match); pc = new Bag(); for (int v = 0; v < graph.V(); v++) if (dfs.marked(v)) pc.add(v); @@ -151,10 +153,10 @@ public static void main(String[] args) { StdOut.println(nfa.recognizes(txt)); } -} +} /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/NonrecursiveDFS.java b/src/main/java/edu/princeton/cs/algs4/NonrecursiveDFS.java index 1a16956ef..c1db52540 100644 --- a/src/main/java/edu/princeton/cs/algs4/NonrecursiveDFS.java +++ b/src/main/java/edu/princeton/cs/algs4/NonrecursiveDFS.java @@ -7,26 +7,31 @@ * https://algs4.cs.princeton.edu/41graph/mediumG.txt * * Run nonrecurisve depth-first search on an undirected graph. - * Runs in O(E + V) time. + * Runs in O(E + V) time using O(V) extra space. * * Explores the vertices in exactly the same order as DepthFirstSearch.java. * - * % java Graph tinyCG.txt - * 6 8 - * 0: 2 1 5 - * 1: 0 2 - * 2: 0 1 3 4 - * 3: 5 4 2 - * 4: 3 2 - * 5: 3 0 + * % java Graph tinyG.txt + * 13 vertices, 13 edges + * 0: 6 2 1 5 + * 1: 0 + * 2: 0 + * 3: 5 4 + * 4: 5 6 3 + * 5: 3 4 0 + * 6: 0 4 + * 7: 8 + * 8: 7 + * 9: 11 10 12 + * 10: 9 + * 11: 9 12 + * 12: 11 9 * - * % java NonrecursiveDFS tinyCG.txt 0 - * 0 to 0 (0): 0 - * 0 to 1 (1): 0-1 - * 0 to 2 (1): 0-2 - * 0 to 3 (2): 0-2-3 - * 0 to 4 (2): 0-2-4 - * 0 to 5 (1): 0-5 + * % java NonrecursiveDFS tinyG.txt 0 + * 0 1 2 3 4 5 6 + * + * % java NonrecursiveDFS tinyG.txt 9 + * 9 10 11 12 * ******************************************************************************/ @@ -41,11 +46,15 @@ *

* This implementation uses a nonrecursive version of depth-first search * with an explicit stack. - * The constructor takes time proportional to V + E, - * where V is the number of vertices and E is the number of edges. - * It uses extra space (not including the graph) proportional to V. + * See {@link DepthFirstSearch} for the classic recursive version. + * The constructor takes Θ(V + E) time in the worst + * case, where V is the number of vertices and E is the + * number of edges. + * The {@link #marked(int)} instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the graph). *

- * For additional documentation, see Section 4.1 + * For additional documentation, + * see Section 4.1 * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -133,7 +142,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/NonrecursiveDirectedDFS.java b/src/main/java/edu/princeton/cs/algs4/NonrecursiveDirectedDFS.java index 58773e7ad..52f6c46f9 100644 --- a/src/main/java/edu/princeton/cs/algs4/NonrecursiveDirectedDFS.java +++ b/src/main/java/edu/princeton/cs/algs4/NonrecursiveDirectedDFS.java @@ -6,7 +6,7 @@ * https://algs4.cs.princeton.edu/42digraph/mediumDG.txt * https://algs4.cs.princeton.edu/42digraph/largeDG.txt * - * Run nonrecurisve depth-first search on an directed graph. + * Run nonrecurisve depth-first search on a directed graph. * Runs in O(E + V) time. * * Explores the vertices in exactly the same order as DirectedDFS.java. @@ -30,9 +30,11 @@ *

* This implementation uses a nonrecursive version of depth-first search * with an explicit stack. - * The constructor takes time proportional to V + E, - * where V is the number of vertices and E is the number of edges. - * It uses extra space (not including the digraph) proportional to V. + * The constructor takes Θ(V + E) time in the + * worst case, where V is the number of vertices and E + * is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the digraph). *

* For additional documentation, * see Section 4.2 of @@ -43,6 +45,7 @@ */ public class NonrecursiveDirectedDFS { private boolean[] marked; // marked[v] = is there an s->v path? + /** * Computes the vertices reachable from the source vertex {@code s} in the digraph {@code G}. * @param G the digraph @@ -121,7 +124,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Out.java b/src/main/java/edu/princeton/cs/algs4/Out.java index 1ad379ee3..8c1284d66 100644 --- a/src/main/java/edu/princeton/cs/algs4/Out.java +++ b/src/main/java/edu/princeton/cs/algs4/Out.java @@ -16,11 +16,13 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Locale; /** - * This class provides methods for writing strings and numbers to - * various output streams, including standard output, file, and sockets. + * The Out data type provides methods for writing strings and + * numbers to various output streams, including standard output, file, and sockets. *

* For additional documentation, see * Section 3.1 of @@ -33,7 +35,7 @@ public class Out { // force Unicode UTF-8 encoding; otherwise it's system dependent - private static final String CHARSET_NAME = "UTF-8"; + private static final Charset CHARSET = StandardCharsets.UTF_8; // assume language = English, country = US for consistency with In private static final Locale LOCALE = Locale.US; @@ -46,13 +48,8 @@ public class Out { * @param os the {@code OutputStream} */ public Out(OutputStream os) { - try { - OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET_NAME); - out = new PrintWriter(osw, true); - } - catch (IOException e) { - e.printStackTrace(); - } + OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET); + out = new PrintWriter(osw, true); } /** @@ -66,15 +63,20 @@ public Out() { * Initializes an output stream from a socket. * * @param socket the socket + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if cannot create output stream from socket */ public Out(Socket socket) { + if (socket == null) { + throw new IllegalArgumentException("socket argument is null"); + } try { OutputStream os = socket.getOutputStream(); - OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET_NAME); + OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET); out = new PrintWriter(osw, true); } catch (IOException e) { - e.printStackTrace(); + throw new IllegalArgumentException("could not create output stream from socket", e); } } @@ -82,15 +84,26 @@ public Out(Socket socket) { * Initializes an output stream from a file. * * @param filename the name of the file + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if {@code filename} is the empty string + * @throws IllegalArgumentException if cannot write the file {@code filename} */ public Out(String filename) { + if (filename == null) { + throw new IllegalArgumentException("filename argument is null"); + } + + if (filename.length() == 0) { + throw new IllegalArgumentException("filename argument is the empty string"); + } + try { OutputStream os = new FileOutputStream(filename); - OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET_NAME); + OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET); out = new PrintWriter(osw, true); } catch (IOException e) { - e.printStackTrace(); + throw new IllegalArgumentException("could not create file '" + filename + "' for writing", e); } } @@ -193,7 +206,7 @@ public void print() { /** * Prints an object to this output stream and flushes this output stream. - * + * * @param x the object to print */ public void print(Object x) { @@ -203,7 +216,7 @@ public void print(Object x) { /** * Prints a boolean to this output stream and flushes this output stream. - * + * * @param x the boolean to print */ public void print(boolean x) { @@ -213,7 +226,7 @@ public void print(boolean x) { /** * Prints a character to this output stream and flushes this output stream. - * + * * @param x the character to print */ public void print(char x) { @@ -223,7 +236,7 @@ public void print(char x) { /** * Prints a double to this output stream and flushes this output stream. - * + * * @param x the double to print */ public void print(double x) { @@ -233,7 +246,7 @@ public void print(double x) { /** * Prints a float to this output stream and flushes this output stream. - * + * * @param x the float to print */ public void print(float x) { @@ -243,7 +256,7 @@ public void print(float x) { /** * Prints an integer to this output stream and flushes this output stream. - * + * * @param x the integer to print */ public void print(int x) { @@ -253,7 +266,7 @@ public void print(int x) { /** * Prints a long integer to this output stream and flushes this output stream. - * + * * @param x the long integer to print */ public void print(long x) { @@ -263,7 +276,7 @@ public void print(long x) { /** * Prints a byte to this output stream and flushes this output stream. - * + * * @param x the byte to print */ public void print(byte x) { @@ -319,7 +332,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Particle.java b/src/main/java/edu/princeton/cs/algs4/Particle.java index a86356b0f..6568e0a31 100644 --- a/src/main/java/edu/princeton/cs/algs4/Particle.java +++ b/src/main/java/edu/princeton/cs/algs4/Particle.java @@ -2,7 +2,7 @@ * Compilation: javac Particle.java * Execution: none * Dependencies: StdDraw.java - * + * * A particle moving in the unit box with a given position, velocity, * radius, and mass. * @@ -19,9 +19,9 @@ * collisions with vertical walls, horizontal walls, and other particles. * This data type is mutable because the position and velocity change. *

- * For additional documentation, - * see Section 6.1 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 6.1 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -49,6 +49,10 @@ public class Particle { * @param color the color */ public Particle(double rx, double ry, double vx, double vy, double radius, double mass, Color color) { + if (!(radius > 0.0)) throw new IllegalArgumentException("radius must be positive"); + if (!(mass > 0.0)) throw new IllegalArgumentException("mass must be positive"); + if (rx - radius < -1.0 || rx + radius > 1.0) throw new IllegalArgumentException("out-of-bounds rx"); + if (ry - radius < -1.0 || ry + radius > 1.0) throw new IllegalArgumentException("out-of-bounds ry"); this.vx = vx; this.vy = vy; this.rx = rx; @@ -57,18 +61,18 @@ public Particle(double rx, double ry, double vx, double vy, double radius, doubl this.mass = mass; this.color = color; } - + /** * Initializes a particle with a random position and velocity. * The position is uniform in the unit box; the velocity in * either direciton is chosen uniformly at random. */ public Particle() { - rx = StdRandom.uniform(0.0, 1.0); - ry = StdRandom.uniform(0.0, 1.0); - vx = StdRandom.uniform(-0.005, 0.005); - vy = StdRandom.uniform(-0.005, 0.005); - radius = 0.01; + rx = StdRandom.uniformDouble(0.0, 1.0); + ry = StdRandom.uniformDouble(0.0, 1.0); + vx = StdRandom.uniformDouble(-0.005, 0.005); + vy = StdRandom.uniformDouble(-0.005, 0.005); + radius = 0.02; mass = 0.5; color = Color.BLACK; } @@ -112,7 +116,7 @@ public int count() { * * @param that the other particle * @return the amount of time for this particle to collide with the specified - * particle, assuming no interening collisions; + * particle, assuming no interening collisions; * {@code Double.POSITIVE_INFINITY} if the particles will not collide */ public double timeToHit(Particle that) { @@ -124,12 +128,19 @@ public double timeToHit(Particle that) { double dvdr = dx*dvx + dy*dvy; if (dvdr > 0) return INFINITY; double dvdv = dvx*dvx + dvy*dvy; + if (dvdv == 0) return INFINITY; double drdr = dx*dx + dy*dy; double sigma = this.radius + that.radius; - double d = (dvdr*dvdr) - dvdv * (drdr - sigma*sigma); + double d = (dvdr*dvdr) - dvdv * (drdr - sigma*sigma); // if (drdr < sigma*sigma) StdOut.println("overlapping particles"); if (d < 0) return INFINITY; - return -(dvdr + Math.sqrt(d)) / dvdv; + double t = -(dvdr + Math.sqrt(d)) / dvdv; + + // should't happen, but seems to be needed for some extreme inputs + // (floating-point precision when dvdv is close to 0, I think) + if (t <= 0) return INFINITY; + + return t; } /** @@ -137,13 +148,13 @@ public double timeToHit(Particle that) { * wall, assuming no interening collisions. * * @return the amount of time for this particle to collide with a vertical wall, - * assuming no interening collisions; + * assuming no interening collisions; * {@code Double.POSITIVE_INFINITY} if the particle will not collide * with a vertical wall */ public double timeToHitVerticalWall() { if (vx > 0) return (1.0 - rx - radius) / vx; - else if (vx < 0) return (radius - rx) / vx; + else if (vx < 0) return (radius - rx) / vx; else return INFINITY; } @@ -152,7 +163,7 @@ public double timeToHitVerticalWall() { * wall, assuming no interening collisions. * * @return the amount of time for this particle to collide with a horizontal wall, - * assuming no interening collisions; + * assuming no interening collisions; * {@code Double.POSITIVE_INFINITY} if the particle will not collide * with a horizontal wall */ @@ -228,7 +239,7 @@ public double kineticEnergy() { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/PatriciaSET.java b/src/main/java/edu/princeton/cs/algs4/PatriciaSET.java index e76ca2011..25ddbecdc 100644 --- a/src/main/java/edu/princeton/cs/algs4/PatriciaSET.java +++ b/src/main/java/edu/princeton/cs/algs4/PatriciaSET.java @@ -81,7 +81,7 @@ * implementation performs well, the source code was written with an emphasis * on clarity, and not performance. PATRICIA performs admirably when its * bit-testing loops are well tuned. Consider using the source code as a guide, - * should you need to produce an optimized implementation, for anther key type, + * should you need to produce an optimized implementation, for another key type, * or in another programming language. *

* Other resources for PATRICIA:
@@ -434,7 +434,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/PatriciaST.java b/src/main/java/edu/princeton/cs/algs4/PatriciaST.java index b1d17cb4a..6ef3e6af4 100644 --- a/src/main/java/edu/princeton/cs/algs4/PatriciaST.java +++ b/src/main/java/edu/princeton/cs/algs4/PatriciaST.java @@ -84,7 +84,7 @@ * implementation performs well, the source code was written with an emphasis * on clarity, and not performance. PATRICIA performs admirably when its * bit-testing loops are well tuned. Consider using the source code as a guide, - * should you need to produce an optimized implementation, for anther key type, + * should you need to produce an optimized implementation, for another key type, * or in another programming language. *

* Other resources for PATRICIA:
@@ -453,7 +453,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Picture.java b/src/main/java/edu/princeton/cs/algs4/Picture.java index 398ed2469..e1c645069 100644 --- a/src/main/java/edu/princeton/cs/algs4/Picture.java +++ b/src/main/java/edu/princeton/cs/algs4/Picture.java @@ -1,21 +1,8 @@ /****************************************************************************** * Compilation: javac Picture.java - * Execution: java Picture imagename + * Execution: java Picture filename.jpg * Dependencies: none * - * Data type for manipulating individual pixels of an image. The original - * image can be read from a file in jpg, gif, or png format, or the - * user can create a blank image of a given dimension. Includes methods for - * displaying the image in a window on the screen or saving to a file. - * - * % java Picture mandrill.jpg - * - * Remarks - * ------- - * - pixel (x, y) is column x and row y, where (0, 0) is upper left - * - * - see also GrayPicture.java for a grayscale version - * ******************************************************************************/ package edu.princeton.cs.algs4; @@ -23,14 +10,24 @@ import java.awt.Color; import java.awt.FileDialog; import java.awt.Toolkit; + import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + import java.awt.image.BufferedImage; + import java.io.File; import java.io.IOException; + +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; + import javax.imageio.ImageIO; + import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; @@ -42,41 +39,193 @@ /** - * This class provides methods for manipulating individual pixels of - * an image using the RGB color format. The alpha component (for transparency) - * is not currently supported. - * The original image can be read from a {@code PNG}, {@code GIF}, - * or {@code JPEG} file or the user can create a blank image of a given dimension. - * This class includes methods for displaying the image in a window on - * the screen or saving it to a file. + * The {@code Picture} data type provides a basic capability for manipulating + * the individual pixels of an image. + * You can either create a blank image (of a given dimension) or read an + * image in a supported file format (typically JPEG, PNG, GIF, TIFF, and BMP). + * This class also includes methods for displaying the image in a window + * and saving it to a file. + * *

- * Pixel (col, row) is column col and row row. - * By default, the origin (0, 0) is the pixel in the top-left corner, - * which is a common convention in image processing. - * The method {@link #setOriginLowerLeft()} change the origin to the lower left. + * Use in the curriculum. + * The {@code Picture} class is intended for use in the + * curriculum once objects are introduced. + * The {@link StdPicture} class is intended for earlier use in + * the curriculum, before objects (but it can support only one + * picture at a time). + * See {@link GrayscalePicture} for a version that supports + * grayscale images. + * + *

+ * Getting started. + * To use this class, you must have {@code Picture} in your Java classpath. + * Here are three possible ways to do this: + *

    + *
  • If you ran our autoinstaller, use the commands + * {@code javac-introcs} and {@code java-introcs} (or {@code javac-algs4} + * and {@code java-algs4}) when compiling and executing. These commands + * add {@code stdlib.jar} (or {@code algs4.jar}) to the Java classpath, which + * provides access to {@code Picture}. + * + *
  • Download stdlib.jar + * (or algs4.jar) + * and add it to the Java classpath. + * + *
  • Download StdPicture.java + * and + * Picture.java + * and put them in the working directory. + *
+ * + *

+ * As a test, cut-and-paste the following short program into your editor: + *

+ *   public class TestPicture {
+ *       public static void main(String[] args) {
+ *           Picture picture = new Picture("https://introcs.cs.princeton.edu/java/stdlib/mandrill.jpg");
+ *           picture.show();
+ *       }
+ *   }
+ *  
+ *

+ * If you compile and execute the program, you should see a picture of a mandrill + * (a colorful monkey native to west-central Africa) in a window. + * + *

+ * Anatomy of an image. + * An image is a width-by-height grid of pixels, with pixel (0, 0) + * in the upper-left corner. + * Each pixel has a color that is represented using the RGB color model, + * which specifies the levels of red (R), green (G), and blue (B) + * on an integer scale from 0 to 255. + * + *

+ * anatomy of an image + *
+ * + *

+ * Creating pictures. + * You can use the following constructors to create new {@code Picture} objects: + *

    + *
  • {@link #Picture(String filename)} + *
  • {@link #Picture(int width, int height)} + *
*

- * The {@code get()} and {@code set()} methods use {@link Color} objects to get - * or set the color of the specified pixel. - * The {@code getRGB()} and {@code setRGB()} methods use a 32-bit {@code int} - * to encode the color, thereby avoiding the need to create temporary - * {@code Color} objects. The red (R), green (G), and blue (B) components - * are encoded using the least significant 24 bits. + * The first constructor read an image in a supported file format + * (typically JPEG, PNG, GIF, TIFF, and BMP) + * and initializes the picture to that image. + * The second constructor creates a width-by-height picture, + * with each pixel black. + * + *

+ * Getting and setting the colors of the individual pixels. + * You can use the following methods to get and set the color of a + * specified pixel: + *

    + *
  • {@link #get(int col, int row)} + *
  • {@link #set(int col, int row, Color color)} + *
+ *

+ * The first method returns the color of pixel (col, row) + * as a {@code Color} object. + * The second method sets the color of pixel (col, row) to + * the specified color. + * + *

Iterating over the pixels. + * A common operation in image processing is to iterate over and process + * all of the pixels in an image. + * Here is a prototypical example that creates a grayscale version of a color image, + * using the NTSC formula + * Y = 0.299r + 0.587g + 0.114b. + * Note that if the red, green, and blue components of an RGB color + * are all equal, the color is a shade of gray. + *

+ *  Picture picture   = new Picture("https://introcs.cs.princeton.edu/java/stdlib/mandrill.jpg");
+ *  Picture grayscale = new Picture(picture.width(), picture.height());
+ *  for (int col = 0; col < picture.width(); col++) {
+ *      for (int row = 0; row < picture.height(); row++) {
+ *          Color color = picture.get(col, row);
+ *          int r = color.getRed();
+ *          int g = color.getGreen();
+ *          int b = color.getBlue();
+ *          int y = (int) (Math.round(0.299*r + 0.587*g + 0.114*b));
+ *          Color gray = new Color(y, y, y);
+ *          grayscale.set(col, row, gray);
+ *      }
+ *  }
+ *  picture.show();
+ *  grayscale.show();
+ *  
+ * + *

Transparency. + * Both the {@link Color} and {@code Picture} classes support + * transparency, using the alpha channel. + * The alpha value defines the transparency of a color, with 0 corresponding to + * completely transparent and 255 to completely opaque. If transparency is not + * explicitly used, the alpha values is 255. + * + *

32-bit color. + * Sometimes it is more convenient (or efficient) to manipulate the + * color of a pixel as a single 32-bit integers instead of four 8-bit components. + * The following methods support this: + *

    + *
  • {@link #getARGB(int col, int row)} + *
  • {@link #setARGB(int col, int row, int rgb)} + *
+ *

+ * The alpha (A), red (R), green (G), and blue (B) components + * are encoded as a single 32-bit integer. * Given a 32-bit {@code int} encoding the color, the following code extracts - * the RGB components: + * the ARGB components: *

- *  int r = (rgb >> 16) & 0xFF;
- *  int g = (rgb >>  8) & 0xFF;
- *  int b = (rgb >>  0) & 0xFF;
- *  
- * Given the RGB components (8-bits each) of a color, + * int a = (rgb >> 24) & 0xFF; + * int r = (rgb >> 16) & 0xFF; + * int g = (rgb >> 8) & 0xFF; + * int b = (rgb >> 0) & 0xFF; + * + * Given the ARGB components (8-bits each) of a color, * the following statement packs it into a 32-bit {@code int}: - *
- *  int rgb = (r << 16) + (g << 8) + (b << 0);
- * 
- *

- * A W-by-H picture uses ~ 4 W H bytes of memory, + *

+ *  int argb = (a << 24) | (r << 16) | (g << 8) | (b << 0);
+ *  
+ * + *

Coordinates. + * Pixel (col, row) is column col and row row. + * By default, the origin (0, 0) is the pixel in the upper-left corner. + * These are common conventions in image processing and consistent with Java's + * {@link java.awt.image.BufferedImage} data type. The following + * two methods allow you to change this convention: + *

    + *
  • {@link #setOriginLowerLeft()} + *
  • {@link #setOriginUpperLeft()} + *
+ * + *

Saving files. + * The {@code Picture} class supports writing images to a supported + * file format (typically JPEG, PNG, GIF, TIFF, and BMP). + * You can save the picture to a file using these two methods: + *

    + *
  • {@link #save(String filename)} + *
  • {@link #save(File file)} + *
+ * + *

Alternatively, you can save the picture interactively + * by using the menu option File → Save from the picture window. + * + *

File formats. + * The {@code Picture} class supports reading and writing images to any of the + * file formats supported by {@link javax.imageio} (typically JPEG, PNG, + * GIF, TIFF, and BMP). + * The file extensions corresponding to JPEG, PNG, GIF, TIFF, and BMP, + * are {@code .jpg}, {@code .png}, {@code .gif}, {@code .tif}, + * and {@code .bmp}, respectively. + * The file formats JPEG and BMP do not support transparency. + * + *

Memory usage. + * A W-by-H picture uses ~ 4 W H bytes of memory, * since the color of each pixel is encoded as a 32-bit int. - *

+ * + *

Additional documentation. * For additional documentation, see * Section 3.1 of * Computer Science: An Interdisciplinary Approach @@ -87,9 +236,11 @@ */ public final class Picture implements ActionListener { private BufferedImage image; // the rasterized image - private JFrame frame; // on-screen view - private String filename; // name of file + private JFrame jframe; // on-screen view + private String title; // window title (typically the name of the file) private boolean isOriginUpperLeft = true; // location of origin + private boolean isVisible = false; // is the frame visible? + private boolean isDisposed = false; // has the window been disposed? private final int width, height; // width and height /** @@ -98,16 +249,16 @@ public final class Picture implements ActionListener { * * @param width the width of the picture * @param height the height of the picture - * @throws IllegalArgumentException if {@code width} is negative - * @throws IllegalArgumentException if {@code height} is negative + * @throws IllegalArgumentException if {@code width} is negative or zero + * @throws IllegalArgumentException if {@code height} is negative or zero */ public Picture(int width, int height) { - if (width < 0) throw new IllegalArgumentException("width must be non-negative"); - if (height < 0) throw new IllegalArgumentException("height must be non-negative"); + if (width <= 0) throw new IllegalArgumentException("width must be positive"); + if (height <= 0) throw new IllegalArgumentException("height must be positive"); this.width = width; this.height = height; - image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - // set to TYPE_INT_ARGB here and in next constructor to support transparency + this.title = width + "-by-" + height; + image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); } /** @@ -121,8 +272,8 @@ public Picture(Picture picture) { width = picture.width(); height = picture.height(); - image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - filename = picture.filename; + image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + title = picture.title; isOriginUpperLeft = picture.isOriginUpperLeft; for (int col = 0; col < width(); col++) for (int row = 0; row < height(); row++) @@ -130,46 +281,72 @@ public Picture(Picture picture) { } /** - * Creates a picture by reading an image from a file or URL. + * Creates a picture by reading a JPEG, PNG, GIF , BMP, or TIFF image + * from a file or URL. + * The filetype extension must be {@code .jpg}, {@code .png}, {@code .gif}, + * {@code .bmp}, or {@code .tif}. * - * @param filename the name of the file (.png, .gif, or .jpg) or URL. - * @throws IllegalArgumentException if cannot read image + * @param filename the name of the file or URL * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if cannot read image from file or URL */ public Picture(String filename) { if (filename == null) throw new IllegalArgumentException("constructor argument is null"); + if (filename.length() == 0) throw new IllegalArgumentException("constructor argument is the empty string"); - this.filename = filename; + title = filename; try { // try to read from file in working directory File file = new File(filename); if (file.isFile()) { + title = file.getName(); image = ImageIO.read(file); } - // now try to read from file in same directory as this .class file else { + + // resource relative to .class file URL url = getClass().getResource(filename); + + // resource relative to classloader root + if (url == null) { + url = getClass().getClassLoader().getResource(filename); + } + + // or URL from web or jar if (url == null) { - url = new URL(filename); + URI uri = new URI(filename); + if (uri.isAbsolute()) url = uri.toURL(); + else throw new IllegalArgumentException("could not read image: '" + filename + "'"); } + image = ImageIO.read(url); } if (image == null) { - throw new IllegalArgumentException("could not read image file: " + filename); + throw new IllegalArgumentException("could not read image: '" + filename + "'"); } width = image.getWidth(null); height = image.getHeight(null); + + // convert to ARGB if necessary + if (image.getType() != BufferedImage.TYPE_INT_ARGB) { + BufferedImage imageARGB = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + // the next line causes JVM app icon to previous display in Dock on OS X + imageARGB.createGraphics().drawImage(image, 0, 0, null); + image = imageARGB; + } } - catch (IOException ioe) { - throw new IllegalArgumentException("could not open image file: " + filename, ioe); + catch (IOException | URISyntaxException e) { + throw new IllegalArgumentException("could not open image: " + filename, e); } } /** - * Creates a picture by reading the image from a PNG, GIF, or JPEG file. + * Creates a picture by reading the image from a JPEG, PNG, GIF, BMP, or TIFF file. + * The filetype extension must be {@code .jpg}, {@code .png}, {@code .gif}, + * {@code .bmp}, or {@code .tif}. * * @param file the file * @throws IllegalArgumentException if cannot read image @@ -180,16 +357,54 @@ public Picture(File file) { try { image = ImageIO.read(file); + + width = image.getWidth(null); + height = image.getHeight(null); + title = file.getName(); + + // convert to ARGB + if (image.getType() != BufferedImage.TYPE_INT_RGB) { + BufferedImage imageARGB = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + imageARGB.createGraphics().drawImage(image, 0, 0, null); + image = imageARGB; + } } catch (IOException ioe) { throw new IllegalArgumentException("could not open file: " + file, ioe); } - if (image == null) { - throw new IllegalArgumentException("could not read file: " + file); - } - width = image.getWidth(null); - height = image.getHeight(null); - filename = file.getName(); + } + + // create the GUI for viewing the image if needed + @SuppressWarnings("deprecation") + private JFrame createGUI() { + JFrame frame = new JFrame(); + JMenuBar menuBar = new JMenuBar(); + JMenu menu = new JMenu("File"); + menuBar.add(menu); + JMenuItem menuItem1 = new JMenuItem(" Save... "); + menuItem1.addActionListener(this); + // Java 11: use getMenuShortcutKeyMaskEx() + // Java 8: use getMenuShortcutKeyMask() + menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); + menu.add(menuItem1); + frame.setJMenuBar(menuBar); + + frame.setContentPane(getJLabel()); + // f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.setTitle(title); + frame.setResizable(false); + frame.pack(); + + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent event){ + isVisible = false; + isDisposed = true; + super.windowClosing(event); + } + }); + return frame; } /** @@ -205,52 +420,60 @@ public JLabel getJLabel() { } /** - * Sets the origin to be the upper left pixel. This is the default. + * Sets the origin (0, 0) to be the upper left pixel. This is the default. */ public void setOriginUpperLeft() { isOriginUpperLeft = true; } /** - * Sets the origin to be the lower left pixel. + * Sets the origin (0, 0) to be the lower left pixel. */ public void setOriginLowerLeft() { isOriginUpperLeft = false; } + /** + * Displays the picture in a window on the screen. + */ + + // getMenuShortcutKeyMask() deprecated in Java 10 but its replacement + // getMenuShortcutKeyMaskEx() is not available in Java 8 + @SuppressWarnings("deprecation") /** * Displays the picture in a window on the screen. */ public void show() { + if (jframe == null && !isDisposed) { + jframe = createGUI(); + isVisible = true; + jframe.setVisible(true); + jframe.repaint(); + } - // create the GUI for viewing the image if needed - if (frame == null) { - frame = new JFrame(); - - JMenuBar menuBar = new JMenuBar(); - JMenu menu = new JMenu("File"); - menuBar.add(menu); - JMenuItem menuItem1 = new JMenuItem(" Save... "); - menuItem1.addActionListener(this); - menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - menu.add(menuItem1); - frame.setJMenuBar(menuBar); - - - - frame.setContentPane(getJLabel()); - // f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - if (filename == null) frame.setTitle(width + "-by-" + height); - else frame.setTitle(filename); - frame.setResizable(false); - frame.pack(); - frame.setVisible(true); + if (jframe != null && !isDisposed) { + isVisible = true; + jframe.setVisible(true); + jframe.repaint(); } + } - // draw - frame.repaint(); + /** + * Hides the window containing the picture. + */ + public void hide() { + if (jframe != null) { + isVisible = false; + jframe.setVisible(false); + } + } + + /** + * Is the window containing the picture visible? + * @return {@code true} if the picture is visible, and {@code false} otherwise + */ + public boolean isVisible() { + return isVisible; } /** @@ -273,40 +496,40 @@ public int width() { private void validateRowIndex(int row) { if (row < 0 || row >= height()) - throw new IllegalArgumentException("row index must be between 0 and " + (height() - 1) + ": " + row); + throw new IndexOutOfBoundsException("row index must be between 0 and " + (height() - 1) + ": " + row); } private void validateColumnIndex(int col) { if (col < 0 || col >= width()) - throw new IllegalArgumentException("column index must be between 0 and " + (width() - 1) + ": " + col); + throw new IndexOutOfBoundsException("column index must be between 0 and " + (width() - 1) + ": " + col); } /** - * Returns the color of pixel ({@code col}, {@code row}) as a {@link java.awt.Color}. + * Returns the color of pixel ({@code col}, {@code row}) as a {@link java.awt.Color} object. * * @param col the column index * @param row the row index * @return the color of pixel ({@code col}, {@code row}) - * @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} */ public Color get(int col, int row) { validateColumnIndex(col); validateRowIndex(row); - int rgb = getRGB(col, row); - return new Color(rgb); + int argb = getARGB(col, row); + return new Color(argb, true); } /** - * Returns the color of pixel ({@code col}, {@code row}) as an {@code int}. + * Returns the ARGB color of pixel ({@code col}, {@code row}) as a 32-bit integer. * Using this method can be more efficient than {@link #get(int, int)} because * it does not create a {@code Color} object. * * @param col the column index * @param row the row index - * @return the integer representation of the color of pixel ({@code col}, {@code row}) - * @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + * @return the 32-bit integer representation of the ARGB color of pixel ({@code col}, {@code row}) + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} */ - public int getRGB(int col, int row) { + public int getARGB(int col, int row) { validateColumnIndex(col); validateRowIndex(row); if (isOriginUpperLeft) return image.getRGB(col, row); @@ -314,39 +537,40 @@ public int getRGB(int col, int row) { } /** - * Sets the color of pixel ({@code col}, {@code row}) to given color. + * Sets the color of pixel ({@code col}, {@code row}) to the given color. * * @param col the column index * @param row the row index * @param color the color - * @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} * @throws IllegalArgumentException if {@code color} is {@code null} */ public void set(int col, int row, Color color) { validateColumnIndex(col); validateRowIndex(row); if (color == null) throw new IllegalArgumentException("color argument is null"); - int rgb = color.getRGB(); - setRGB(col, row, rgb); + int argb = color.getRGB(); + setARGB(col, row, argb); } /** - * Sets the color of pixel ({@code col}, {@code row}) to given color. + * Sets the color of pixel ({@code col}, {@code row}) to the given ARGB color. * * @param col the column index * @param row the row index - * @param rgb the integer representation of the color - * @throws IllegalArgumentException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + * @param argb the 32-bit integer representation of the color + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} */ - public void setRGB(int col, int row, int rgb) { + public void setARGB(int col, int row, int argb) { validateColumnIndex(col); validateRowIndex(row); - if (isOriginUpperLeft) image.setRGB(col, row, rgb); - else image.setRGB(col, height - row - 1, rgb); + if (isOriginUpperLeft) image.setRGB(col, row, argb); + else image.setRGB(col, height - row - 1, argb); } /** - * Returns true if this picture is equal to the argument picture. + * Returns {@code true} if this picture is equal to the argument picture, + * and {@code false} otherwise. * * @param other the other picture * @return {@code true} if this picture is the same dimension as {@code other} @@ -361,7 +585,7 @@ public boolean equals(Object other) { if (this.height() != that.height()) return false; for (int col = 0; col < width(); col++) for (int row = 0; row < height(); row++) - if (this.getRGB(col, row) != that.getRGB(col, row)) return false; + if (this.getARGB(col, row) != that.getARGB(col, row)) return false; return true; } @@ -378,7 +602,7 @@ public String toString() { sb.append(width +"-by-" + height + " picture (RGB values given in hex)\n"); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { - int rgb = 0; + int rgb; if (isOriginUpperLeft) rgb = image.getRGB(col, row); else rgb = image.getRGB(col, height - row - 1); sb.append(String.format("#%06X ", rgb & 0xFFFFFF)); @@ -398,40 +622,86 @@ public int hashCode() { throw new UnsupportedOperationException("hashCode() is not supported because pictures are mutable"); } + /** + * Sets the title of this picture. + * @param title the title + * @throws IllegalArgumentException if {@code title} is {@code null} + */ + public void setTitle(String title) { + if (title == null) throw new IllegalArgumentException("title is null"); + this.title = title; + } + + // does this picture use transparency (i.e., alpha < 255 for some pixel)? + private boolean hasAlpha() { + for (int col = 0; col < width; col++) { + for (int row = 0; row < height; row++) { + int argb = image.getRGB(col, row); + int alpha = (argb >> 24) & 0xFF; + if (alpha != 255) return true; + } + } + return false; + } + /** - * Saves the picture to a file in either PNG or JPEG format. - * The filetype extension must be either .png or .jpg. + * Saves the picture to a file in a supported file format + * (typically JPEG, PNG, GIF, TIFF, and BMP). + * The filetype extension must be {@code .jpg}, {@code .png}, {@code .gif}, + * {@code .bmp}, or {@code .tif}. + * If the file format does not support transparency (such as JPEG + * or BMP), it will be converted to be opaque (with purely + * transparent pixels converted to black). * - * @param name the name of the file - * @throws IllegalArgumentException if {@code name} is {@code null} + * @param filename the name of the file + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if {@code filename} is the empty string + * @throws IllegalArgumentException if {@code filename} has invalid filetype extension + * @throws IllegalArgumentException if cannot write the file {@code filename} */ - public void save(String name) { - if (name == null) throw new IllegalArgumentException("argument to save() is null"); - save(new File(name)); - filename = name; + public void save(String filename) { + if (filename == null) throw new IllegalArgumentException("argument to save() is null"); + if (filename.length() == 0) throw new IllegalArgumentException("argument to save() is the empty string"); + File file = new File(filename); + save(file); } /** - * Saves the picture to a file in a PNG or JPEG image format. + * Saves the picture to a file in a supported file format + * (typically JPEG, PNG, GIF, TIFF, and BMP). + * The filetype extension must be {@code .jpg}, {@code .png}, {@code .gif}, + * {@code .bmp}, or {@code .tif}. + * If the file format does not support transparency (such as JPEG + * or BMP), it will be converted to be opaque (with purely + * transparent pixels converted to black). * * @param file the file * @throws IllegalArgumentException if {@code file} is {@code null} */ public void save(File file) { if (file == null) throw new IllegalArgumentException("argument to save() is null"); - filename = file.getName(); - if (frame != null) frame.setTitle(filename); - String suffix = filename.substring(filename.lastIndexOf('.') + 1); - if ("jpg".equalsIgnoreCase(suffix) || "png".equalsIgnoreCase(suffix)) { - try { - ImageIO.write(image, suffix, file); - } - catch (IOException e) { - e.printStackTrace(); - } + title = file.getName(); + + String suffix = title.substring(title.lastIndexOf('.') + 1); + if (!title.contains(".") || suffix.length() == 0) { + throw new IllegalArgumentException("The filename '" + title + "' has no filetype extension, such as .jpg or .png"); } - else { - System.out.println("Error: filename must end in .jpg or .png"); + + try { + // for formats that support transparency (e.g., PNG and GIF) + if (ImageIO.write(image, suffix, file)) return; + + // for formats that don't support transparency (e.g., JPG and BMP) + // create BufferedImage in RGB format and use white background + BufferedImage imageRGB = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + imageRGB.createGraphics().drawImage(image, 0, 0, Color.WHITE, null); + if (ImageIO.write(imageRGB, suffix, file)) return; + + // failed to save the file; probably wrong format + throw new IllegalArgumentException("The filetype '" + suffix + "' is not supported"); + } + catch (IOException e) { + throw new IllegalArgumentException("could not write file '" + title + "'", e); } } @@ -439,12 +709,19 @@ public void save(File file) { * Opens a save dialog box when the user selects "Save As" from the menu. */ @Override - public void actionPerformed(ActionEvent e) { - FileDialog chooser = new FileDialog(frame, - "Use a .png or .jpg extension", FileDialog.SAVE); + public void actionPerformed(ActionEvent event) { + FileDialog chooser = new FileDialog(jframe, + "The filetype extension must be either .jpg or .png", FileDialog.SAVE); chooser.setVisible(true); - if (chooser.getFile() != null) { - save(chooser.getDirectory() + File.separator + chooser.getFile()); + String selectedDirectory = chooser.getDirectory(); + String selectedFilename = chooser.getFile(); + if (selectedDirectory != null && selectedFilename != null) { + try { + save(selectedDirectory + selectedFilename); + } + catch (IllegalArgumentException e) { + System.err.println(e.getMessage()); + } } } @@ -463,9 +740,8 @@ public static void main(String[] args) { } - /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/PictureDump.java b/src/main/java/edu/princeton/cs/algs4/PictureDump.java index cf13a573c..09d5d0ff1 100644 --- a/src/main/java/edu/princeton/cs/algs4/PictureDump.java +++ b/src/main/java/edu/princeton/cs/algs4/PictureDump.java @@ -3,11 +3,11 @@ * Execution: java PictureDump width height < file * Dependencies: BinaryStdIn.java Picture.java * Data file: http://introcs.cs.princeton.edu/stdlib/abra.txt - * + * * Reads in a binary file and writes out the bits as w-by-h picture, * with the 1 bits in black and the 0 bits in white. * - * % more abra.txt + * % more abra.txt * ABRACADABRA! * * % java PictureDump 16 6 < abra.txt @@ -24,7 +24,7 @@ * of a binary file as a black-and-white picture. *

* For additional documentation, - * see Section 5.5 of + * see Section 5.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. *

* See also {@link BinaryDump} and {@link HexDump}. @@ -66,7 +66,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Point2D.java b/src/main/java/edu/princeton/cs/algs4/Point2D.java index 53847978d..c58e905a1 100644 --- a/src/main/java/edu/princeton/cs/algs4/Point2D.java +++ b/src/main/java/edu/princeton/cs/algs4/Point2D.java @@ -17,13 +17,13 @@ * The {@code Point} class is an immutable data type to encapsulate a * two-dimensional point with real-value coordinates. *

- * Note: in order to deal with the difference behavior of double and + * Note: in order to deal with the difference behavior of double and * Double with respect to -0.0 and +0.0, the Point2D constructor converts * any coordinates that are -0.0 to +0.0. *

- * For additional documentation, - * see Section 1.2 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 1.2 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -86,7 +86,7 @@ public double y() { /** * Returns the polar radius of this point. - * @return the polar radius of this point in polar coordiantes: sqrt(x*x + y*y) + * @return the polar radius of this point in polar coordinates: sqrt(x*x + y*y) */ public double r() { return Math.sqrt(x*x + y*y); @@ -94,7 +94,7 @@ public double r() { /** * Returns the angle of this point in polar coordinates. - * @return the angle (in radians) of this point in polar coordiantes (between –π and π) + * @return the angle (in radians) of this point in polar coordinates (between –π and π) */ public double theta() { return Math.atan2(y, x); @@ -115,7 +115,7 @@ private double angleTo(Point2D that) { * @param a first point * @param b second point * @param c third point - * @return { -1, 0, +1 } if a→b→c is a { clockwise, collinear; counterclocwise } turn. + * @return { -1, 0, +1 } if a→b→c is a { clockwise, collinear; counterclockwise } turn. */ public static int ccw(Point2D a, Point2D b, Point2D c) { double area2 = (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x); @@ -207,18 +207,14 @@ public Comparator distanceToOrder() { // compare points according to their x-coordinate private static class XOrder implements Comparator { public int compare(Point2D p, Point2D q) { - if (p.x < q.x) return -1; - if (p.x > q.x) return +1; - return 0; + return Double.compare(p.x, q.x); } } // compare points according to their y-coordinate private static class YOrder implements Comparator { public int compare(Point2D p, Point2D q) { - if (p.y < q.y) return -1; - if (p.y > q.y) return +1; - return 0; + return Double.compare(p.y, q.y); } } @@ -226,20 +222,16 @@ public int compare(Point2D p, Point2D q) { private static class ROrder implements Comparator { public int compare(Point2D p, Point2D q) { double delta = (p.x*p.x + p.y*p.y) - (q.x*q.x + q.y*q.y); - if (delta < 0) return -1; - if (delta > 0) return +1; - return 0; + return Double.compare(delta, 0); } } - - // compare other points relative to atan2 angle (bewteen -pi/2 and pi/2) they make with this Point + + // compare other points relative to atan2 angle (between -pi/2 and pi/2) they make with this Point private class Atan2Order implements Comparator { public int compare(Point2D q1, Point2D q2) { double angle1 = angleTo(q1); double angle2 = angleTo(q2); - if (angle1 < angle2) return -1; - else if (angle1 > angle2) return +1; - else return 0; + return Double.compare(angle1, angle2); } } @@ -269,16 +261,14 @@ private class DistanceToOrder implements Comparator { public int compare(Point2D p, Point2D q) { double dist1 = distanceSquaredTo(p); double dist2 = distanceSquaredTo(q); - if (dist1 < dist2) return -1; - else if (dist1 > dist2) return +1; - else return 0; + return Double.compare(dist1, dist2); } } - /** + /** * Compares this point to the specified point. - * + * * @param other the other point * @return {@code true} if this point equals {@code other}; * {@code false} otherwise @@ -346,8 +336,8 @@ public static void main(String[] args) { Point2D[] points = new Point2D[n]; for (int i = 0; i < n; i++) { - int x = StdRandom.uniform(100); - int y = StdRandom.uniform(100); + int x = StdRandom.uniformInt(100); + int y = StdRandom.uniformInt(100); points[i] = new Point2D(x, y); points[i].draw(); } @@ -372,7 +362,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Polynomial.java b/src/main/java/edu/princeton/cs/algs4/Polynomial.java index 8f214b596..05f766498 100644 --- a/src/main/java/edu/princeton/cs/algs4/Polynomial.java +++ b/src/main/java/edu/princeton/cs/algs4/Polynomial.java @@ -29,6 +29,9 @@ * It includes methods for addition, subtraction, multiplication, composition, * differentiation, and evaluation. *

+ * This computes correct results if all arithmetic performed is + * without overflow. + *

* For additional documentation, * see Section 9.9 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. @@ -140,10 +143,10 @@ public Polynomial compose(Polynomial that) { } - /** + /** * Compares this polynomial to the specified polynomial. - * - * @param other the other polynoimal + * + * @param other the other polynomial * @return {@code true} if this polynomial equals {@code other}; * {@code false} otherwise */ @@ -192,7 +195,7 @@ public int evaluate(int x) { * @param that the other point * @return the value {@code 0} if this polynomial is equal to the argument * polynomial (precisely when {@code equals()} returns {@code true}); - * a negative integer if this polynomialt is less than the argument + * a negative integer if this polynomial is less than the argument * polynomial; and a positive integer if this polynomial is greater than the * argument point */ @@ -232,7 +235,7 @@ public String toString() { * * @param args the command-line arguments (none) */ - public static void main(String[] args) { + public static void main(String[] args) { Polynomial zero = new Polynomial(0, 0); Polynomial p1 = new Polynomial(4, 3); @@ -266,7 +269,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/PrimMST.java b/src/main/java/edu/princeton/cs/algs4/PrimMST.java index cf90a485d..372e803a0 100644 --- a/src/main/java/edu/princeton/cs/algs4/PrimMST.java +++ b/src/main/java/edu/princeton/cs/algs4/PrimMST.java @@ -9,7 +9,7 @@ * * Compute a minimum spanning forest using Prim's algorithm. * - * % java PrimMST tinyEWG.txt + * % java PrimMST tinyEWG.txt * 1-7 0.19000 * 0-2 0.26000 * 2-3 0.17000 @@ -44,17 +44,24 @@ * The edge weights can be positive, zero, or negative and need not * be distinct. If the graph is not connected, it computes a minimum * spanning forest, which is the union of minimum spanning trees - * in each connected component. The {@code weight()} method returns the + * in each connected component. The {@code weight()} method returns the * weight of a minimum spanning tree and the {@code edges()} method * returns its edges. *

* This implementation uses Prim's algorithm with an indexed * binary heap. - * The constructor takes time proportional to E log V - * and extra space (not including the graph) proportional to V, - * where V is the number of vertices and E is the number of edges. - * Afterwards, the {@code weight()} method takes constant time - * and the {@code edges()} method takes time proportional to V. + * The constructor takes Θ(E log V) time in + * the worst case, where V is the number of + * vertices and E is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the + * edge-weighted graph). + *

+ * This {@code weight()} method correctly computes the weight of the MST + * if all arithmetic performed is without floating-point rounding error + * or arithmetic overflow. + * This is the case if all edge weights are non-negative integers + * and the weight of the MST does not exceed 252. *

* For additional documentation, * see Section 4.3 of @@ -66,7 +73,7 @@ * @author Kevin Wayne */ public class PrimMST { - private static final double FLOATING_POINT_EPSILON = 1E-12; + private static final double FLOATING_POINT_EPSILON = 1.0E-12; private Edge[] edgeTo; // edgeTo[v] = shortest edge from tree vertex to non-tree vertex private double[] distTo; // distTo[v] = weight of shortest such edge @@ -162,7 +169,7 @@ private boolean check(EdgeWeightedGraph G) { UF uf = new UF(G.V()); for (Edge e : edges()) { int v = e.either(), w = e.other(v); - if (uf.connected(v, w)) { + if (uf.find(v) == uf.find(w)) { System.err.println("Not a forest"); return false; } @@ -172,7 +179,7 @@ private boolean check(EdgeWeightedGraph G) { // check that it is a spanning forest for (Edge e : G.edges()) { int v = e.either(), w = e.other(v); - if (!uf.connected(v, w)) { + if (uf.find(v) != uf.find(w)) { System.err.println("Not a spanning forest"); return false; } @@ -191,7 +198,7 @@ private boolean check(EdgeWeightedGraph G) { // check that e is min weight edge in crossing cut for (Edge f : G.edges()) { int x = f.either(), y = f.other(x); - if (!uf.connected(x, y)) { + if (uf.find(x) != uf.find(y)) { if (f.weight() < e.weight()) { System.err.println("Edge " + f + " violates cut optimality conditions"); return false; @@ -223,7 +230,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Queue.java b/src/main/java/edu/princeton/cs/algs4/Queue.java index 4275e4c6e..713c97230 100644 --- a/src/main/java/edu/princeton/cs/algs4/Queue.java +++ b/src/main/java/edu/princeton/cs/algs4/Queue.java @@ -2,11 +2,11 @@ * Compilation: javac Queue.java * Execution: java Queue < input.txt * Dependencies: StdIn.java StdOut.java - * Data files: https://algs4.cs.princeton.edu/13stacks/tobe.txt + * Data files: https://algs4.cs.princeton.edu/13stacks/tobe.txt * * A generic queue, implemented using a linked list. * - * % java Queue < tobe.txt + * % java Queue < tobe.txt * to be or not to be (2 left on queue) * ******************************************************************************/ @@ -37,7 +37,7 @@ * @author Robert Sedgewick * @author Kevin Wayne * - * @param the generic type of an item in this queue + * @param the generic type of each item in this queue */ public class Queue implements Iterable { private Node first; // beginning of queue @@ -130,7 +130,7 @@ public String toString() { s.append(' '); } return s.toString(); - } + } /** * Returns an iterator that iterates over the items in this queue in FIFO order. @@ -138,24 +138,25 @@ public String toString() { * @return an iterator that iterates over the items in this queue in FIFO order */ public Iterator iterator() { - return new ListIterator(first); + return new LinkedIterator(first); } - // an iterator, doesn't implement remove() since it's optional - private class ListIterator implements Iterator { + // a linked-list iterator + private class LinkedIterator implements Iterator { private Node current; - public ListIterator(Node first) { + public LinkedIterator(Node first) { current = first; } - public boolean hasNext() { return current != null; } - public void remove() { throw new UnsupportedOperationException(); } + public boolean hasNext() { + return current != null; + } public Item next() { if (!hasNext()) throw new NoSuchElementException(); Item item = current.item; - current = current.next; + current = current.next; return item; } } @@ -180,7 +181,7 @@ else if (!queue.isEmpty()) } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Quick.java b/src/main/java/edu/princeton/cs/algs4/Quick.java index 40cdfab08..84cfd276a 100644 --- a/src/main/java/edu/princeton/cs/algs4/Quick.java +++ b/src/main/java/edu/princeton/cs/algs4/Quick.java @@ -6,7 +6,7 @@ * https://algs4.cs.princeton.edu/23quicksort/words3.txt * * Sorts a sequence of strings from standard input using quicksort. - * + * * % more tiny.txt * S O R T E X A M P L E * @@ -15,7 +15,7 @@ * * % more words3.txt * bed bug dad yes zoo ... all bad yet - * + * * % java Quick < words3.txt * all bad bed bug dad ... yes yet zoo [ one string per line ] * @@ -32,9 +32,9 @@ * The {@code Quick} class provides static methods for sorting an * array and selecting the ith smallest element in an array using quicksort. *

- * For additional documentation, - * see Section 2.3 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, see + * Section 2.3 + * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -55,7 +55,7 @@ public static void sort(Comparable[] a) { } // quicksort the subarray from a[lo] to a[hi] - private static void sort(Comparable[] a, int lo, int hi) { + private static void sort(Comparable[] a, int lo, int hi) { if (hi <= lo) return; int j = partition(a, lo, hi); sort(a, lo, j-1); @@ -69,7 +69,7 @@ private static int partition(Comparable[] a, int lo, int hi) { int i = lo; int j = hi + 1; Comparable v = a[lo]; - while (true) { + while (true) { // find item on lo to swap while (less(a[++i], v)) { @@ -124,13 +124,13 @@ public static Comparable select(Comparable[] a, int k) { /*************************************************************************** * Helper sorting functions. ***************************************************************************/ - + // is v < w ? private static boolean less(Comparable v, Comparable w) { if (v == w) return false; // optimization when reference equals return v.compareTo(w) < 0; } - + // exchange a[i] and a[j] private static void exch(Object[] a, int i, int j) { Object swap = a[i]; @@ -161,8 +161,8 @@ private static void show(Comparable[] a) { } /** - * Reads in a sequence of strings from standard input; quicksorts them; - * and prints them to standard output in ascending order. + * Reads in a sequence of strings from standard input; quicksorts them; + * and prints them to standard output in ascending order. * Shuffles the array and then prints the strings again to * standard output, but this time, using the select method. * @@ -188,7 +188,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Quick3string.java b/src/main/java/edu/princeton/cs/algs4/Quick3string.java index b116650af..41ecfaf4d 100644 --- a/src/main/java/edu/princeton/cs/algs4/Quick3string.java +++ b/src/main/java/edu/princeton/cs/algs4/Quick3string.java @@ -1,7 +1,7 @@ /****************************************************************************** * Compilation: javac Quick3string.java * Execution: java Quick3string < input.txt - * Dependencies: StdIn.java StdOut.java + * Dependencies: StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/51radix/words3.txt * https://algs4.cs.princeton.edu/51radix/shells.txt * @@ -43,9 +43,9 @@ public class Quick3string { private static final int CUTOFF = 15; // cutoff to insertion sort // do not instantiate - private Quick3string() { } + private Quick3string() { } - /** + /** * Rearranges the array of strings in ascending order. * * @param a the array to be sorted @@ -57,7 +57,7 @@ public static void sort(String[] a) { } // return the dth character of s, -1 if d = length of s - private static int charAt(String s, int d) { + private static int charAt(String s, int d) { assert d >= 0 && d <= s.length(); if (d == s.length()) return -1; return s.charAt(d); @@ -65,7 +65,7 @@ private static int charAt(String s, int d) { // 3-way string quicksort a[lo..hi] starting at dth character - private static void sort(String[] a, int lo, int hi, int d) { + private static void sort(String[] a, int lo, int hi, int d) { // cutoff to insertion sort for small subarrays if (hi <= lo + CUTOFF) { @@ -83,7 +83,7 @@ private static void sort(String[] a, int lo, int hi, int d) { else i++; } - // a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]. + // a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]. sort(a, lo, lt-1, d); if (v >= 0) sort(a, lt, gt, d+1); sort(a, gt+1, hi, d); @@ -107,7 +107,7 @@ private static void exch(String[] a, int i, int j) { // DEPRECATED BECAUSE OF SLOW SUBSTRING EXTRACTION IN JAVA 7 // private static boolean less(String v, String w, int d) { // assert v.substring(0, d).equals(w.substring(0, d)); - // return v.substring(d).compareTo(w.substring(d)) < 0; + // return v.substring(d).compareTo(w.substring(d)) < 0; // } // is v less than w, starting at character d @@ -151,7 +151,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Quick3way.java b/src/main/java/edu/princeton/cs/algs4/Quick3way.java index e8583b546..44b7d1450 100644 --- a/src/main/java/edu/princeton/cs/algs4/Quick3way.java +++ b/src/main/java/edu/princeton/cs/algs4/Quick3way.java @@ -4,18 +4,18 @@ * Dependencies: StdOut.java StdIn.java * Data files: https://algs4.cs.princeton.edu/23quicksort/tiny.txt * https://algs4.cs.princeton.edu/23quicksort/words3.txt - * + * * Sorts a sequence of strings from standard input using 3-way quicksort. - * + * * % more tiny.txt * S O R T E X A M P L E * * % java Quick3way < tiny.txt * A E E L M O P R S T X [ one string per line ] - * + * * % more words3.txt * bed bug dad yes zoo ... all bad yet - * + * * % java Quick3way < words3.txt * all bad bed bug dad ... yes yet zoo [ one string per line ] * @@ -27,9 +27,9 @@ * The {@code Quick3way} class provides static methods for sorting an * array using quicksort with 3-way partitioning. *

- * For additional documentation, - * see Section 2.3 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, see + * Section 2.3 + * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -50,7 +50,7 @@ public static void sort(Comparable[] a) { } // quicksort the subarray a[lo .. hi] using 3-way partitioning - private static void sort(Comparable[] a, int lo, int hi) { + private static void sort(Comparable[] a, int lo, int hi) { if (hi <= lo) return; int lt = lo, gt = hi; Comparable v = a[lo]; @@ -62,7 +62,7 @@ private static void sort(Comparable[] a, int lo, int hi) { else i++; } - // a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]. + // a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]. sort(a, lo, lt-1); sort(a, gt+1, hi); assert isSorted(a, lo, hi); @@ -73,12 +73,12 @@ private static void sort(Comparable[] a, int lo, int hi) { /*************************************************************************** * Helper sorting functions. ***************************************************************************/ - + // is v < w ? private static boolean less(Comparable v, Comparable w) { return v.compareTo(w) < 0; } - + // exchange a[i] and a[j] private static void exch(Object[] a, int i, int j) { Object swap = a[i]; @@ -111,7 +111,7 @@ private static void show(Comparable[] a) { /** * Reads in a sequence of strings from standard input; 3-way - * quicksorts them; and prints them to standard output in ascending order. + * quicksorts them; and prints them to standard output in ascending order. * * @param args the command-line arguments */ @@ -124,7 +124,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/QuickBentleyMcIlroy.java b/src/main/java/edu/princeton/cs/algs4/QuickBentleyMcIlroy.java index 60a716b5d..fe285eb1b 100644 --- a/src/main/java/edu/princeton/cs/algs4/QuickBentleyMcIlroy.java +++ b/src/main/java/edu/princeton/cs/algs4/QuickBentleyMcIlroy.java @@ -4,13 +4,13 @@ * Dependencies: StdOut.java StdIn.java * Data files: https://algs4.cs.princeton.edu/23quicksort/tiny.txt * https://algs4.cs.princeton.edu/23quicksort/words3.txt - * + * * Uses the Bentley-McIlroy 3-way partitioning scheme, * chooses the partitioning element using Tukey's ninther, * and cuts off to insertion sort. * * Reference: Engineering a Sort Function by Jon L. Bentley - * and M. Douglas McIlroy. Softwae-Practice and Experience, + * and M. Douglas McIlroy. Software-Practice and Experience, * Vol. 23 (11), 1249-1265 (November 1993). * ******************************************************************************/ @@ -22,9 +22,9 @@ * an array using an optimized version of quicksort (using Bentley-McIlroy * 3-way partitioning, Tukey's ninther, and cutoff to insertion sort). *

- * For additional documentation, - * see Section 2.3 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, see + * Section 2.3 + * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -48,7 +48,7 @@ public static void sort(Comparable[] a) { sort(a, 0, a.length - 1); } - private static void sort(Comparable[] a, int lo, int hi) { + private static void sort(Comparable[] a, int lo, int hi) { int n = hi - lo + 1; // cutoff to insertion sort @@ -69,7 +69,7 @@ else if (n <= MEDIAN_OF_3_CUTOFF) { int mid = lo + n/2; int m1 = median3(a, lo, lo + eps, lo + eps + eps); int m2 = median3(a, mid - eps, mid, mid + eps); - int m3 = median3(a, hi - eps - eps, hi - eps, hi); + int m3 = median3(a, hi - eps - eps, hi - eps, hi); int ninther = median3(a, m1, m2, m3); exch(a, ninther, lo); } @@ -124,7 +124,7 @@ private static int median3(Comparable[] a, int i, int j, int k) { /*************************************************************************** * Helper sorting functions. ***************************************************************************/ - + // is v < w ? private static boolean less(Comparable v, Comparable w) { if (v == w) return false; // optimization when reference equal @@ -136,7 +136,7 @@ private static boolean eq(Comparable v, Comparable w) { if (v == w) return true; // optimization when reference equal return v.compareTo(w) == 0; } - + // exchange a[i] and a[j] private static void exch(Object[] a, int i, int j) { Object swap = a[i]; @@ -163,8 +163,8 @@ private static void show(Comparable[] a) { /** * Reads in a sequence of strings from standard input; quicksorts them - * (using an optimized version of quicksort); - * and prints them to standard output in ascending order. + * (using an optimized version of quicksort); + * and prints them to standard output in ascending order. * * @param args the command-line arguments */ @@ -178,7 +178,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/QuickFindUF.java b/src/main/java/edu/princeton/cs/algs4/QuickFindUF.java index 6f364a96a..8891b8840 100644 --- a/src/main/java/edu/princeton/cs/algs4/QuickFindUF.java +++ b/src/main/java/edu/princeton/cs/algs4/QuickFindUF.java @@ -15,65 +15,47 @@ /** * The {@code QuickFindUF} class represents a union–find data type * (also known as the disjoint-sets data type). - * It supports the union and find operations, - * along with a connected operation for determining whether - * two sites are in the same component and a count operation that - * returns the total number of components. + * It supports the classic union and find operations, + * along with a count operation that returns the total number + * of sets. *

- * The union–find data type models connectivity among a set of n - * sites, named 0 through n–1. - * The is-connected-to relation must be an - * equivalence relation: + * The union–find data type models a collection of sets containing + * n elements, with each element in exactly one set. + * The elements are named 0 through n–1. + * Initially, there are n sets, with each element in its + * own set. The canonical element of a set + * (also known as the root, identifier, + * leader, or set representative) + * is one distinguished element in the set. Here is a summary of + * the operations: *

    - *
  • Reflexive: p is connected to p. - *
  • Symmetric: If p is connected to q, - * then q is connected to p. - *
  • Transitive: If p is connected to q - * and q is connected to r, then - * p is connected to r. + *
  • find(p) returns the canonical element + * of the set containing p. The find operation + * returns the same value for two elements if and only if + * they are in the same set. + *
  • union(p, q) merges the set + * containing element p with the set containing + * element q. That is, if p and q + * are in different sets, replace these two sets + * with a new set that is the union of the two. + *
  • count() returns the number of sets. *
*

- * An equivalence relation partitions the sites into - * equivalence classes (or components). In this case, - * two sites are in the same component if and only if they are connected. - * Both sites and components are identified with integers between 0 and - * n–1. - * Initially, there are n components, with each site in its - * own component. The component identifier of a component - * (also known as the root, canonical element, leader, - * or set representative) is one of the sites in the component: - * two sites have the same component identifier if and only if they are - * in the same component. - *

    - *
  • union(p, q) adds a - * connection between the two sites p and q. - * If p and q are in different components, - * then it replaces - * these two components with a new component that is the union of - * the two. - *
  • find(p) returns the component - * identifier of the component containing p. - *
  • connected(p, q) - * returns true if both p and q - * are in the same component, and false otherwise. - *
  • count() returns the number of components. - *
+ * The canonical element of a set can change only when the set + * itself changes during a call to union—it cannot + * change during a call to either find or count. *

- * The component identifier of a component can change - * only when the component itself changes during a call to - * union—it cannot change during a call - * to find, connected, or count. + * This implementation uses quick find. + * The constructor takes Θ(n) time, where n + * is the number of sites. + * The find, connected, and count + * operations take Θ(1) time; the union operation + * takes Θ(n) time. *

- * This implementation uses quick find. - * Initializing a data structure with n sites takes linear time. - * Afterwards, the find, connected, and count - * operations take constant time but the union operation - * takes linear time. - * For alternate implementations of the same API, see + * For alternative implementations of the same API, see * {@link UF}, {@link QuickUnionUF}, and {@link WeightedQuickUnionUF}. - * - *

- * For additional documentation, see Section 1.5 of + * For additional documentation, see + * Section 1.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -85,11 +67,11 @@ public class QuickFindUF { private int count; // number of components /** - * Initializes an empty union–find data structure with {@code n} sites - * {@code 0} through {@code n-1}. Each site is initially in its own - * component. + * Initializes an empty union-find data structure with + * {@code n} elements {@code 0} through {@code n-1}. + * Initially, each element is in its own set. * - * @param n the number of sites + * @param n the number of elements * @throws IllegalArgumentException if {@code n < 0} */ public QuickFindUF(int n) { @@ -100,19 +82,19 @@ public QuickFindUF(int n) { } /** - * Returns the number of components. + * Returns the number of sets. * - * @return the number of components (between {@code 1} and {@code n}) + * @return the number of sets (between {@code 1} and {@code n}) */ public int count() { return count; } - + /** - * Returns the component identifier for the component containing site {@code p}. + * Returns the canonical element of the set containing element {@code p}. * - * @param p the integer representing one site - * @return the component identifier for the component containing site {@code p} + * @param p an element + * @return the canonical element of the set containing {@code p} * @throws IllegalArgumentException unless {@code 0 <= p < n} */ public int find(int p) { @@ -129,27 +111,29 @@ private void validate(int p) { } /** - * Returns true if the the two sites are in the same component. + * Returns true if the two elements are in the same set. * - * @param p the integer representing one site - * @param q the integer representing the other site - * @return {@code true} if the two sites {@code p} and {@code q} are in the same component; + * @param p one element + * @param q the other element + * @return {@code true} if {@code p} and {@code q} are in the same set; * {@code false} otherwise * @throws IllegalArgumentException unless * both {@code 0 <= p < n} and {@code 0 <= q < n} + * @deprecated Replace with two calls to {@link #find(int)}. */ + @Deprecated public boolean connected(int p, int q) { validate(p); validate(q); return id[p] == id[q]; } - + /** - * Merges the component containing site {@code p} with the - * the component containing site {@code q}. + * Merges the set containing element {@code p} with the set + * containing element {@code q}. * - * @param p the integer representing one site - * @param q the integer representing the other site + * @param p one element + * @param q the other element * @throws IllegalArgumentException unless * both {@code 0 <= p < n} and {@code 0 <= q < n} */ @@ -168,9 +152,10 @@ public void union(int p, int q) { } /** - * Reads in a sequence of pairs of integers (between 0 and n-1) from standard input, - * where each integer represents some site; - * if the sites are in different components, merge the two components + * Reads an integer {@code n} and a sequence of pairs of integers + * (between {@code 0} and {@code n-1}) from standard input, where each integer + * in the pair represents some element; + * if the elements are in different sets, merge the two sets * and print the pair to standard output. * * @param args the command-line arguments @@ -181,7 +166,7 @@ public static void main(String[] args) { while (!StdIn.isEmpty()) { int p = StdIn.readInt(); int q = StdIn.readInt(); - if (uf.connected(p, q)) continue; + if (uf.find(p) == uf.find(q)) continue; uf.union(p, q); StdOut.println(p + " " + q); } @@ -191,7 +176,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/QuickUnionUF.java b/src/main/java/edu/princeton/cs/algs4/QuickUnionUF.java index dbf174194..58d8f323f 100644 --- a/src/main/java/edu/princeton/cs/algs4/QuickUnionUF.java +++ b/src/main/java/edu/princeton/cs/algs4/QuickUnionUF.java @@ -15,65 +15,47 @@ /** * The {@code QuickUnionUF} class represents a union–find data type * (also known as the disjoint-sets data type). - * It supports the union and find operations, - * along with a connected operation for determining whether - * two sites are in the same component and a count operation that - * returns the total number of components. + * It supports the classic union and find operations, + * along with a count operation that returns the total number + * of sets. *

- * The union–find data type models connectivity among a set of n - * sites, named 0 through n–1. - * The is-connected-to relation must be an - * equivalence relation: + * The union–find data type models a collection of sets containing + * n elements, with each element in exactly one set. + * The elements are named 0 through n–1. + * Initially, there are n sets, with each element in its + * own set. The canonical element of a set + * (also known as the root, identifier, + * leader, or set representative) + * is one distinguished element in the set. Here is a summary of + * the operations: *

    - *
  • Reflexive: p is connected to p. - *
  • Symmetric: If p is connected to q, - * then q is connected to p. - *
  • Transitive: If p is connected to q - * and q is connected to r, then - * p is connected to r. + *
  • find(p) returns the canonical element + * of the set containing p. The find operation + * returns the same value for two elements if and only if + * they are in the same set. + *
  • union(p, q) merges the set + * containing element p with the set containing + * element q. That is, if p and q + * are in different sets, replace these two sets + * with a new set that is the union of the two. + *
  • count() returns the number of sets. *
*

- * An equivalence relation partitions the sites into - * equivalence classes (or components). In this case, - * two sites are in the same component if and only if they are connected. - * Both sites and components are identified with integers between 0 and - * n–1. - * Initially, there are n components, with each site in its - * own component. The component identifier of a component - * (also known as the root, canonical element, leader, - * or set representative) is one of the sites in the component: - * two sites have the same component identifier if and only if they are - * in the same component. - *

    - *
  • union(p, q) adds a - * connection between the two sites p and q. - * If p and q are in different components, - * then it replaces - * these two components with a new component that is the union of - * the two. - *
  • find(p) returns the component - * identifier of the component containing p. - *
  • connected(p, q) - * returns true if both p and q - * are in the same component, and false otherwise. - *
  • count() returns the number of components. - *
+ * The canonical element of a set can change only when the set + * itself changes during a call to union—it cannot + * change during a call to either find or count. *

- * The component identifier of a component can change - * only when the component itself changes during a call to - * union—it cannot change during a call - * to find, connected, or count. + * This implementation uses quick union. + * The constructor takes Θ(n) time, where + * n is the number of sites. + * The union and find operations take + * Θ(n) time in the worst case. + * The count operation takes Θ(1) time. *

- * This implementation uses quick union. - * Initializing a data structure with n sites takes linear time. - * Afterwards, the union, find, and connected - * operations take linear time (in the worst case) and the - * count operation takes constant time. - * For alternate implementations of the same API, see + * For alternative implementations of the same API, see * {@link UF}, {@link QuickFindUF}, and {@link WeightedQuickUnionUF}. - * - *

- * For additional documentation, see Section 1.5 of + * For additional documentation, + * see Section 1.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -84,11 +66,11 @@ public class QuickUnionUF { private int count; // number of components /** - * Initializes an empty union–find data structure with {@code n} sites - * {@code 0} through {@code n-1}. Each site is initially in its own - * component. + * Initializes an empty union-find data structure with + * {@code n} elements {@code 0} through {@code n-1}. + * Initially, each element is in its own set. * - * @param n the number of sites + * @param n the number of elements * @throws IllegalArgumentException if {@code n < 0} */ public QuickUnionUF(int n) { @@ -100,19 +82,19 @@ public QuickUnionUF(int n) { } /** - * Returns the number of components. + * Returns the number of sets. * - * @return the number of components (between {@code 1} and {@code n}) + * @return the number of sets (between {@code 1} and {@code n}) */ public int count() { return count; } - + /** - * Returns the component identifier for the component containing site {@code p}. + * Returns the canonical element of the set containing element {@code p}. * - * @param p the integer representing one object - * @return the component identifier for the component containing site {@code p} + * @param p an element + * @return the canonical element of the set containing {@code p} * @throws IllegalArgumentException unless {@code 0 <= p < n} */ public int find(int p) { @@ -131,26 +113,27 @@ private void validate(int p) { } /** - * Returns true if the the two sites are in the same component. + * Returns true if the two elements are in the same set. * - * @param p the integer representing one site - * @param q the integer representing the other site - * @return {@code true} if the two sites {@code p} and {@code q} are in the same component; + * @param p one element + * @param q the other element + * @return {@code true} if {@code p} and {@code q} are in the same set; * {@code false} otherwise * @throws IllegalArgumentException unless * both {@code 0 <= p < n} and {@code 0 <= q < n} + * @deprecated Replace with two calls to {@link #find(int)}. */ + @Deprecated public boolean connected(int p, int q) { return find(p) == find(q); } - /** - * Merges the component containing site {@code p} with the - * the component containing site {@code q}. + * Merges the set containing element {@code p} with the set + * containing element {@code q}. * - * @param p the integer representing one site - * @param q the integer representing the other site + * @param p one element + * @param q the other element * @throws IllegalArgumentException unless * both {@code 0 <= p < n} and {@code 0 <= q < n} */ @@ -158,14 +141,15 @@ public void union(int p, int q) { int rootP = find(p); int rootQ = find(q); if (rootP == rootQ) return; - parent[rootP] = rootQ; + parent[rootP] = rootQ; count--; } /** - * Reads in a sequence of pairs of integers (between 0 and n-1) from standard input, - * where each integer represents some object; - * if the sites are in different components, merge the two components + * Reads an integer {@code n} and a sequence of pairs of integers + * (between {@code 0} and {@code n-1}) from standard input, where each integer + * in the pair represents some element; + * if the elements are in different sets, merge the two sets * and print the pair to standard output. * * @param args the command-line arguments @@ -176,7 +160,7 @@ public static void main(String[] args) { while (!StdIn.isEmpty()) { int p = StdIn.readInt(); int q = StdIn.readInt(); - if (uf.connected(p, q)) continue; + if (uf.find(p) == uf.find(q)) continue; uf.union(p, q); StdOut.println(p + " " + q); } @@ -187,7 +171,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/QuickX.java b/src/main/java/edu/princeton/cs/algs4/QuickX.java index ff56bac56..c84db26be 100644 --- a/src/main/java/edu/princeton/cs/algs4/QuickX.java +++ b/src/main/java/edu/princeton/cs/algs4/QuickX.java @@ -4,7 +4,7 @@ * Dependencies: StdOut.java StdIn.java * Data files: https://algs4.cs.princeton.edu/23quicksort/tiny.txt * https://algs4.cs.princeton.edu/23quicksort/words3.txt - * + * * Uses the Hoare's 2-way partitioning scheme, chooses the partitioning * element using median-of-3, and cuts off to insertion sort. * @@ -18,9 +18,9 @@ * algorithm, median-of-3 to choose the partitioning element, and cutoff * to insertion sort). *

- * For additional documentation, - * see Section 2.3 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, see + * Section 2.3 + * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -44,7 +44,7 @@ public static void sort(Comparable[] a) { } // quicksort the subarray from a[lo] to a[hi] - private static void sort(Comparable[] a, int lo, int hi) { + private static void sort(Comparable[] a, int lo, int hi) { if (hi <= lo) return; // cutoff to insertion sort (Insertion.sort() uses half-open intervals) @@ -81,7 +81,7 @@ private static int partition(Comparable[] a, int lo, int hi) { } // the main loop - while (i < j) { + while (i < j) { exch(a, i, j); while (less(a[++i], v)) ; while (less(v, a[--j])) ; @@ -104,7 +104,7 @@ private static int median3(Comparable[] a, int i, int j, int k) { /*************************************************************************** * Helper sorting functions. ***************************************************************************/ - + // is v < w ? private static boolean less(Comparable v, Comparable w) { return v.compareTo(w) < 0; @@ -136,8 +136,8 @@ private static void show(Comparable[] a) { /** * Reads in a sequence of strings from standard input; quicksorts them - * (using an optimized version of 2-way quicksort); - * and prints them to standard output in ascending order. + * (using an optimized version of 2-way quicksort); + * and prints them to standard output in ascending order. * * @param args the command-line arguments */ @@ -151,7 +151,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/RabinKarp.java b/src/main/java/edu/princeton/cs/algs4/RabinKarp.java index e9d039286..eaa6e23c1 100644 --- a/src/main/java/edu/princeton/cs/algs4/RabinKarp.java +++ b/src/main/java/edu/princeton/cs/algs4/RabinKarp.java @@ -9,17 +9,17 @@ * * % java RabinKarp abracadabra abacadabrabracabracadabrabrabracad * pattern: abracadabra - * text: abacadabrabracabracadabrabrabracad - * match: abracadabra + * text: abacadabrabracabracadabrabrabracad + * match: abracadabra * * % java RabinKarp rab abacadabrabracabracadabrabrabracad * pattern: rab - * text: abacadabrabracabracadabrabrabracad - * match: rab + * text: abacadabrabracabracadabrabrabracad + * match: rab * * % java RabinKarp bcara abacadabrabracabracadabrabrabracad * pattern: bcara - * text: abacadabrabracabracadabrabrabracad + * text: abacadabrabracabracadabrabrabracad * * % java RabinKarp rabrabracad abacadabrabracabracadabrabrabracad * text: abacadabrabracabracadabrabrabracad @@ -62,7 +62,7 @@ public class RabinKarp { */ public RabinKarp(char[] pattern, int R) { this.pat = String.valueOf(pattern); - this.R = R; + this.R = R; throw new UnsupportedOperationException("Operation not supported yet"); } @@ -82,21 +82,21 @@ public RabinKarp(String pat) { for (int i = 1; i <= m-1; i++) RM = (R * RM) % q; patHash = hash(pat, m); - } + } - // Compute hash for key[0..m-1]. - private long hash(String key, int m) { - long h = 0; - for (int j = 0; j < m; j++) + // Compute hash for key[0..m-1]. + private long hash(String key, int m) { + long h = 0; + for (int j = 0; j < m; j++) h = (R * h + key.charAt(j)) % q; return h; } // Las Vegas version: does pat[] match txt[i..i-m+1] ? private boolean check(String txt, int i) { - for (int j = 0; j < m; j++) - if (pat.charAt(j) != txt.charAt(i + j)) - return false; + for (int j = 0; j < m; j++) + if (pat.charAt(j) != txt.charAt(i + j)) + return false; return true; } @@ -104,9 +104,9 @@ private boolean check(String txt, int i) { // private boolean check(int i) { // return true; //} - + /** - * Returns the index of the first occurrrence of the pattern string + * Returns the index of the first occurrence of the pattern string * in the text string. * * @param txt the text string @@ -114,9 +114,9 @@ private boolean check(String txt, int i) { * in the text string; n if no such match */ public int search(String txt) { - int n = txt.length(); + int n = txt.length(); if (n < m) return n; - long txtHash = hash(txt, m); + long txtHash = hash(txt, m); // check for match at offset 0 if ((patHash == txtHash) && check(txt, 0)) @@ -124,9 +124,9 @@ public int search(String txt) { // check for hash match; if hash match, check for exact match for (int i = m; i < n; i++) { - // Remove leading digit, add trailing digit, check for match. - txtHash = (txtHash + q - RM*txt.charAt(i-m) % q) % q; - txtHash = (txtHash*R + txt.charAt(i)) % q; + // Remove leading digit, add trailing digit, check for match. + txtHash = (txtHash + q - RM*txt.charAt(i-m) % q) % q; + txtHash = (txtHash*R + txt.charAt(i)) % q; // match int offset = i - m + 1; @@ -145,7 +145,7 @@ private static long longRandomPrime() { return prime.longValue(); } - /** + /** * Takes a pattern string and an input string as command-line arguments; * searches for the pattern string in the text string; and prints * the first occurrence of the pattern string in the text string. @@ -171,7 +171,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/RandomSeq.java b/src/main/java/edu/princeton/cs/algs4/RandomSeq.java index 632a06d1b..c2a15c5c9 100644 --- a/src/main/java/edu/princeton/cs/algs4/RandomSeq.java +++ b/src/main/java/edu/princeton/cs/algs4/RandomSeq.java @@ -26,7 +26,7 @@ * @author Robert Sedgewick * @author Kevin Wayne */ -public class RandomSeq { +public class RandomSeq { // this class should not be instantiated private RandomSeq() { } @@ -47,7 +47,7 @@ public static void main(String[] args) { if (args.length == 1) { // generate and print n numbers between 0.0 and 1.0 for (int i = 0; i < n; i++) { - double x = StdRandom.uniform(); + double x = StdRandom.uniformDouble(0.0, 1.0); StdOut.println(x); } } @@ -58,7 +58,7 @@ else if (args.length == 3) { // generate and print n numbers between lo and hi for (int i = 0; i < n; i++) { - double x = StdRandom.uniform(lo, hi); + double x = StdRandom.uniformDouble(lo, hi); StdOut.printf("%.2f\n", x); } } @@ -70,7 +70,7 @@ else if (args.length == 3) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/RectHV.java b/src/main/java/edu/princeton/cs/algs4/RectHV.java index 7bd117eab..2417c6e8d 100644 --- a/src/main/java/edu/princeton/cs/algs4/RectHV.java +++ b/src/main/java/edu/princeton/cs/algs4/RectHV.java @@ -14,9 +14,9 @@ * two-dimensional axis-aligned rectagle with real-value coordinates. * The rectangle is closed—it includes the points on the boundary. *

- * For additional documentation, - * see Section 1.2 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 1.2 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -31,11 +31,11 @@ public final class RectHV { * x [ymin, ymax]. * * @param xmin the x-coordinate of the lower-left endpoint - * @param xmax the x-coordinate of the upper-right endpoint * @param ymin the y-coordinate of the lower-left endpoint + * @param xmax the x-coordinate of the upper-right endpoint * @param ymax the y-coordinate of the upper-right endpoint * @throws IllegalArgumentException if any of {@code xmin}, - * {@code xmax}, {@code ymin}, or {@code ymax} + * {@code ymin}, {@code xmax}, or {@code ymax} * is {@code Double.NaN}. * @throws IllegalArgumentException if {@code xmax < xmin} or {@code ymax < ymin}. */ @@ -113,14 +113,14 @@ public double height() { } /** - * Returns true if the two rectangles intersect. This includes - * improper intersections (at points on the boundary - * of each rectangle) and nested intersctions - * (when one rectangle is contained inside the other) + * Returns true if the two rectangles intersect, and false otherwise. + * This includes improper intersections (at points on the + * boundary of each rectangle) and nested intersections + * (when one rectangle is contained inside the other). * * @param that the other rectangle - * @return {@code true} if this rectangle intersect the argument - rectangle at one or more points + * @return {@code true} if this rectangle intersects the argument + rectangle at one or more points; false otherwise */ public boolean intersects(RectHV that) { return this.xmax >= that.xmin && this.ymax >= that.ymin @@ -128,9 +128,10 @@ public boolean intersects(RectHV that) { } /** - * Returns true if this rectangle contain the point. + * Returns true if this rectangle contains the point, and false otherwise. + * This includes point on the boundary of the rectangle. * @param p the point - * @return {@code true} if this rectangle contain the point {@code p}, + * @return {@code true} if this rectangle contains the point {@code p}, possibly at the boundary; {@code false} otherwise */ public boolean contains(Point2D p) { @@ -224,7 +225,7 @@ public void draw() { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/RedBlackBST.java b/src/main/java/edu/princeton/cs/algs4/RedBlackBST.java index 079f1c741..531734379 100644 --- a/src/main/java/edu/princeton/cs/algs4/RedBlackBST.java +++ b/src/main/java/edu/princeton/cs/algs4/RedBlackBST.java @@ -1,9 +1,9 @@ /****************************************************************************** * Compilation: javac RedBlackBST.java * Execution: java RedBlackBST < input.txt - * Dependencies: StdIn.java StdOut.java - * Data files: https://algs4.cs.princeton.edu/33balanced/tinyST.txt - * + * Dependencies: StdIn.java StdOut.java + * Data files: https://algs4.cs.princeton.edu/33balanced/tinyST.txt + * * A symbol table implemented using a left-leaning red-black BST. * This is the 2-3 version. * @@ -12,7 +12,7 @@ * * % more tinyST.txt * S E A R C H E X A M P L E - * + * * % java RedBlackBST < tinyST.txt * A 8 * C 4 @@ -47,21 +47,30 @@ * value associated with a key to {@code null} is equivalent to deleting the key * from the symbol table. *

- * This implementation uses a left-leaning red-black BST. It requires that + * It requires that * the key type implements the {@code Comparable} interface and calls the * {@code compareTo()} and method to compare two keys. It does not call either * {@code equals()} or {@code hashCode()}. - * The put, contains, remove, minimum, - * maximum, ceiling, and floor operations each take - * logarithmic time in the worst case, if the tree becomes unbalanced. - * The size, and is-empty operations take constant time. - * Construction takes constant time. *

- * For additional documentation, see Section 3.3 of + * This implementation uses a left-leaning red-black BST. + * The put, get, contains, remove, + * minimum, maximum, ceiling, floor, + * rank, and select operations each take + * Θ(log n) time in the worst case, where n is the + * number of key-value pairs in the symbol table. + * The size, and is-empty operations take Θ(1) time. + * The keys methods take + * O(log n + m) time, where m is + * the number of keys returned by the iterator. + * Construction takes Θ(1) time. + *

+ * For alternative implementations of the symbol table API, see {@link ST}, + * {@link BinarySearchST}, {@link SequentialSearchST}, {@link BST}, + * {@link SeparateChainingHashST}, {@link LinearProbingHashST}, and + * {@link AVLTreeST}. + * For additional documentation, see + * Section 3.3 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. - * For other implementations of the same API, see {@link ST}, {@link BinarySearchST}, - * {@link SequentialSearchST}, {@link BST}, - * {@link SeparateChainingHashST}, {@link LinearProbingHashST}, and {@link AVLTreeST}. * * @author Robert Sedgewick * @author Kevin Wayne @@ -109,7 +118,7 @@ private boolean isRed(Node x) { private int size(Node x) { if (x == null) return 0; return x.size; - } + } /** @@ -172,7 +181,7 @@ public boolean contains(Key key) { ***************************************************************************/ /** - * Inserts the specified key-value pair into the symbol table, overwriting the old + * Inserts the specified key-value pair into the symbol table, overwriting the old * value with the new value if the symbol table already contains the specified key. * Deletes the specified key (and its associated value) from this symbol table * if the specified value is {@code null}. @@ -194,12 +203,12 @@ public void put(Key key, Value val) { } // insert the key-value pair in the subtree rooted at h - private Node put(Node h, Key key, Value val) { + private Node put(Node h, Key key, Value val) { if (h == null) return new Node(key, val, RED, 1); int cmp = key.compareTo(h.key); - if (cmp < 0) h.left = put(h.left, key, val); - else if (cmp > 0) h.right = put(h.right, key, val); + if (cmp < 0) h.left = put(h.left, key, val); + else if (cmp > 0) h.right = put(h.right, key, val); else h.val = val; // fix-up any right-leaning links @@ -232,7 +241,7 @@ public void deleteMin() { } // delete the key-value pair with the minimum key rooted at h - private Node deleteMin(Node h) { + private Node deleteMin(Node h) { if (h.left == null) return null; @@ -261,7 +270,7 @@ public void deleteMax() { } // delete the key-value pair with the maximum key rooted at h - private Node deleteMax(Node h) { + private Node deleteMax(Node h) { if (isRed(h.left)) h = rotateRight(h); @@ -277,13 +286,13 @@ private Node deleteMax(Node h) { } /** - * Removes the specified key and its associated value from this symbol table - * (if the key is in this symbol table). + * Removes the specified key and its associated value from this symbol table + * (if the key is in this symbol table). * * @param key the key * @throws IllegalArgumentException if {@code key} is {@code null} */ - public void delete(Key key) { + public void delete(Key key) { if (key == null) throw new IllegalArgumentException("argument to delete() is null"); if (!contains(key)) return; @@ -297,7 +306,7 @@ public void delete(Key key) { } // delete the key-value pair with the given key rooted at h - private Node delete(Node h, Key key) { + private Node delete(Node h, Key key) { // assert get(h, key) != null; if (key.compareTo(h.key) < 0) { @@ -331,12 +340,13 @@ private Node delete(Node h, Key key) { // make a left-leaning link lean to the right private Node rotateRight(Node h) { - // assert (h != null) && isRed(h.left); + assert (h != null) && isRed(h.left); + // assert (h != null) && isRed(h.left) && !isRed(h.right); // for insertion only Node x = h.left; h.left = x.right; x.right = h; - x.color = x.right.color; - x.right.color = RED; + x.color = h.color; + h.color = RED; x.size = h.size; h.size = size(h.left) + size(h.right) + 1; return x; @@ -344,12 +354,13 @@ private Node rotateRight(Node h) { // make a right-leaning link lean to the left private Node rotateLeft(Node h) { - // assert (h != null) && isRed(h.right); + assert (h != null) && isRed(h.right); + // assert (h != null) && isRed(h.right) && !isRed(h.left); // for insertion only Node x = h.right; h.right = x.left; x.left = h; - x.color = x.left.color; - x.left.color = RED; + x.color = h.color; + h.color = RED; x.size = h.size; h.size = size(h.left) + size(h.right) + 1; return x; @@ -373,7 +384,7 @@ private Node moveRedLeft(Node h) { // assert isRed(h) && !isRed(h.left) && !isRed(h.left.left); flipColors(h); - if (isRed(h.right.left)) { + if (isRed(h.right.left)) { h.right = rotateRight(h.right); h = rotateLeft(h); flipColors(h); @@ -387,7 +398,7 @@ private Node moveRedRight(Node h) { // assert (h != null); // assert isRed(h) && !isRed(h.right) && !isRed(h.right.left); flipColors(h); - if (isRed(h.left.left)) { + if (isRed(h.left.left)) { h = rotateRight(h); flipColors(h); } @@ -398,7 +409,7 @@ private Node moveRedRight(Node h) { private Node balance(Node h) { // assert (h != null); - if (isRed(h.right)) h = rotateLeft(h); + if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h); if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h); if (isRed(h.left) && isRed(h.right)) flipColors(h); @@ -435,14 +446,14 @@ private int height(Node x) { public Key min() { if (isEmpty()) throw new NoSuchElementException("calls min() with empty symbol table"); return min(root).key; - } + } // the smallest key in subtree rooted at x; null if no such key - private Node min(Node x) { + private Node min(Node x) { // assert x != null; - if (x.left == null) return x; - else return min(x.left); - } + if (x.left == null) return x; + else return min(x.left); + } /** * Returns the largest key in the symbol table. @@ -452,14 +463,14 @@ private Node min(Node x) { public Key max() { if (isEmpty()) throw new NoSuchElementException("calls max() with empty symbol table"); return max(root).key; - } + } // the largest key in the subtree rooted at x; null if no such key - private Node max(Node x) { + private Node max(Node x) { // assert x != null; - if (x.right == null) return x; - else return max(x.right); - } + if (x.right == null) return x; + else return max(x.right); + } /** @@ -473,9 +484,9 @@ public Key floor(Key key) { if (key == null) throw new IllegalArgumentException("argument to floor() is null"); if (isEmpty()) throw new NoSuchElementException("calls floor() with empty symbol table"); Node x = floor(root, key); - if (x == null) return null; + if (x == null) throw new NoSuchElementException("argument to floor() is too small"); else return x.key; - } + } // the largest key in the subtree rooted at x less than or equal to the given key private Node floor(Node x, Key key) { @@ -484,7 +495,7 @@ private Node floor(Node x, Key key) { if (cmp == 0) return x; if (cmp < 0) return floor(x.left, key); Node t = floor(x.right, key); - if (t != null) return t; + if (t != null) return t; else return x; } @@ -499,47 +510,48 @@ public Key ceiling(Key key) { if (key == null) throw new IllegalArgumentException("argument to ceiling() is null"); if (isEmpty()) throw new NoSuchElementException("calls ceiling() with empty symbol table"); Node x = ceiling(root, key); - if (x == null) return null; - else return x.key; + if (x == null) throw new NoSuchElementException("argument to ceiling() is too large"); + else return x.key; } // the smallest key in the subtree rooted at x greater than or equal to the given key - private Node ceiling(Node x, Key key) { + private Node ceiling(Node x, Key key) { if (x == null) return null; int cmp = key.compareTo(x.key); if (cmp == 0) return x; if (cmp > 0) return ceiling(x.right, key); Node t = ceiling(x.left, key); - if (t != null) return t; + if (t != null) return t; else return x; } /** - * Return the key in the symbol table whose rank is {@code k}. - * This is the (k+1)st smallest key in the symbol table. + * Return the key in the symbol table of a given {@code rank}. + * This key has the property that there are {@code rank} keys in + * the symbol table that are smaller. In other words, this key is the + * ({@code rank}+1)st smallest key in the symbol table. * - * @param k the order statistic - * @return the key in the symbol table of rank {@code k} - * @throws IllegalArgumentException unless {@code k} is between 0 and + * @param rank the order statistic + * @return the key in the symbol table of given {@code rank} + * @throws IllegalArgumentException unless {@code rank} is between 0 and * n–1 */ - public Key select(int k) { - if (k < 0 || k >= size()) { - throw new IllegalArgumentException("argument to select() is invalid: " + k); + public Key select(int rank) { + if (rank < 0 || rank >= size()) { + throw new IllegalArgumentException("argument to select() is invalid: " + rank); } - Node x = select(root, k); - return x.key; + return select(root, rank); } - // the key of rank k in the subtree rooted at x - private Node select(Node x, int k) { - // assert x != null; - // assert k >= 0 && k < size(x); - int t = size(x.left); - if (t > k) return select(x.left, k); - else if (t < k) return select(x.right, k-t-1); - else return x; - } + // Return key in BST rooted at x of given rank. + // Precondition: rank is in legal range. + private Key select(Node x, int rank) { + if (x == null) return null; + int leftSize = size(x.left); + if (leftSize > rank) return select(x.left, rank); + else if (leftSize < rank) return select(x.right, rank - leftSize - 1); + else return x.key; + } /** * Return the number of keys in the symbol table strictly less than {@code key}. @@ -550,26 +562,26 @@ private Node select(Node x, int k) { public int rank(Key key) { if (key == null) throw new IllegalArgumentException("argument to rank() is null"); return rank(key, root); - } + } // number of keys less than key in the subtree rooted at x private int rank(Key key, Node x) { - if (x == null) return 0; - int cmp = key.compareTo(x.key); - if (cmp < 0) return rank(key, x.left); - else if (cmp > 0) return 1 + size(x.left) + rank(key, x.right); - else return size(x.left); - } + if (x == null) return 0; + int cmp = key.compareTo(x.key); + if (cmp < 0) return rank(key, x.left); + else if (cmp > 0) return 1 + size(x.left) + rank(key, x.right); + else return size(x.left); + } /*************************************************************************** * Range count and range search. ***************************************************************************/ /** - * Returns all keys in the symbol table as an {@code Iterable}. + * Returns all keys in the symbol table in ascending order as an {@code Iterable}. * To iterate over all of the keys in the symbol table named {@code st}, * use the foreach notation: {@code for (Key key : st.keys())}. - * @return all keys in the symbol table as an {@code Iterable} + * @return all keys in the symbol table in ascending order */ public Iterable keys() { if (isEmpty()) return new Queue(); @@ -577,13 +589,13 @@ public Iterable keys() { } /** - * Returns all keys in the symbol table in the given range, + * Returns all keys in the symbol table in the given range in ascending order, * as an {@code Iterable}. * * @param lo minimum endpoint * @param hi maximum endpoint - * @return all keys in the sybol table between {@code lo} - * (inclusive) and {@code hi} (inclusive) as an {@code Iterable} + * @return all keys in the symbol table between {@code lo} + * (inclusive) and {@code hi} (inclusive) in ascending order * @throws IllegalArgumentException if either {@code lo} or {@code hi} * is {@code null} */ @@ -595,25 +607,25 @@ public Iterable keys(Key lo, Key hi) { // if (isEmpty() || lo.compareTo(hi) > 0) return queue; keys(root, queue, lo, hi); return queue; - } + } // add the keys between lo and hi in the subtree rooted at x // to the queue - private void keys(Node x, Queue queue, Key lo, Key hi) { - if (x == null) return; - int cmplo = lo.compareTo(x.key); - int cmphi = hi.compareTo(x.key); - if (cmplo < 0) keys(x.left, queue, lo, hi); - if (cmplo <= 0 && cmphi >= 0) queue.enqueue(x.key); - if (cmphi > 0) keys(x.right, queue, lo, hi); - } + private void keys(Node x, Queue queue, Key lo, Key hi) { + if (x == null) return; + int cmplo = lo.compareTo(x.key); + int cmphi = hi.compareTo(x.key); + if (cmplo < 0) keys(x.left, queue, lo, hi); + if (cmplo <= 0 && cmphi >= 0) queue.enqueue(x.key); + if (cmphi > 0) keys(x.right, queue, lo, hi); + } /** * Returns the number of keys in the symbol table in the given range. * * @param lo minimum endpoint * @param hi maximum endpoint - * @return the number of keys in the sybol table between {@code lo} + * @return the number of keys in the symbol table between {@code lo} * (inclusive) and {@code hi} (inclusive) * @throws IllegalArgumentException if either {@code lo} or {@code hi} * is {@code null} @@ -648,13 +660,13 @@ private boolean isBST() { // is the tree rooted at x a BST with all keys strictly between min and max // (if min or max is null, treat as empty constraint) - // Credit: Bob Dondero's elegant solution + // Credit: elegant solution due to Bob Dondero private boolean isBST(Node x, Key min, Key max) { if (x == null) return true; if (min != null && x.key.compareTo(min) <= 0) return false; if (max != null && x.key.compareTo(max) >= 0) return false; return isBST(x.left, min, x.key) && isBST(x.right, x.key, max); - } + } // are the size fields correct? private boolean isSizeConsistent() { return isSizeConsistent(root); } @@ -662,7 +674,7 @@ private boolean isSizeConsistent(Node x) { if (x == null) return true; if (x.size != size(x.left) + size(x.right) + 1) return false; return isSizeConsistent(x.left) && isSizeConsistent(x.right); - } + } // check that ranks are consistent private boolean isRankConsistent() { @@ -682,10 +694,10 @@ private boolean is23(Node x) { if (x != root && isRed(x) && isRed(x.left)) return false; return is23(x.left) && is23(x.right); - } + } // do all paths from root to leaf have same number of black edges? - private boolean isBalanced() { + private boolean isBalanced() { int black = 0; // number of black links on path from root to min Node x = root; while (x != null) { @@ -700,7 +712,7 @@ private boolean isBalanced(Node x, int black) { if (x == null) return black == 0; if (!isRed(x)) black--; return isBalanced(x.left, black) && isBalanced(x.right, black); - } + } /** @@ -708,12 +720,13 @@ private boolean isBalanced(Node x, int black) { * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { RedBlackBST st = new RedBlackBST(); for (int i = 0; !StdIn.isEmpty(); i++) { String key = StdIn.readString(); st.put(key, i); } + StdOut.println(); for (String s : st.keys()) StdOut.println(s + " " + st.get(s)); StdOut.println(); @@ -721,7 +734,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/ResizingArrayBag.java b/src/main/java/edu/princeton/cs/algs4/ResizingArrayBag.java index c81eeb70c..b247a0435 100644 --- a/src/main/java/edu/princeton/cs/algs4/ResizingArrayBag.java +++ b/src/main/java/edu/princeton/cs/algs4/ResizingArrayBag.java @@ -2,7 +2,7 @@ * Compilation: javac ResizingArrayBag.java * Execution: java ResizingArrayBag * Dependencies: StdIn.java StdOut.java - * + * * Bag implementation with a resizing array. * ******************************************************************************/ @@ -13,8 +13,8 @@ import java.util.NoSuchElementException; /** - * The {@code ResizingArrayBag} class represents a bag (or multiset) of - * generic items. It supports insertion and iterating over the + * The {@code ResizingArrayBag} class represents a bag (or multiset) of + * generic items. It supports insertion and iterating over the * items in arbitrary order. *

* This implementation uses a resizing array. @@ -30,6 +30,9 @@ * @author Kevin Wayne */ public class ResizingArrayBag implements Iterable { + // initial capacity of underlying resizing array + private static final int INIT_CAPACITY = 8; + private Item[] a; // array of items private int n; // number of elements on bag @@ -37,7 +40,7 @@ public class ResizingArrayBag implements Iterable { * Initializes an empty bag. */ public ResizingArrayBag() { - a = (Item[]) new Object[2]; + a = (Item[]) new Object[INIT_CAPACITY]; n = 0; } @@ -60,10 +63,10 @@ public int size() { // resize the underlying array holding the elements private void resize(int capacity) { assert capacity >= n; - Item[] temp = (Item[]) new Object[capacity]; + Item[] copy = (Item[]) new Object[capacity]; for (int i = 0; i < n; i++) - temp[i] = a[i]; - a = temp; + copy[i] = a[i]; + a = copy; } /** @@ -84,11 +87,13 @@ public Iterator iterator() { return new ArrayIterator(); } - // an iterator, doesn't implement remove() since it's optional + // an array iterator private class ArrayIterator implements Iterator { private int i = 0; - public boolean hasNext() { return i < n; } - public void remove() { throw new UnsupportedOperationException(); } + + public boolean hasNext() { + return i < n; + } public Item next() { if (!hasNext()) throw new NoSuchElementException(); @@ -116,7 +121,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/ResizingArrayQueue.java b/src/main/java/edu/princeton/cs/algs4/ResizingArrayQueue.java index f4ccf463b..227d7f121 100644 --- a/src/main/java/edu/princeton/cs/algs4/ResizingArrayQueue.java +++ b/src/main/java/edu/princeton/cs/algs4/ResizingArrayQueue.java @@ -2,11 +2,11 @@ * Compilation: javac ResizingArrayQueue.java * Execution: java ResizingArrayQueue < input.txt * Dependencies: StdIn.java StdOut.java - * Data files: https://algs4.cs.princeton.edu/13stacks/tobe.txt - * + * Data files: https://algs4.cs.princeton.edu/13stacks/tobe.txt + * * Queue implementation with a resizing array. * - * % java ResizingArrayQueue < tobe.txt + * % java ResizingArrayQueue < tobe.txt * to be or not to be (2 left on queue) * ******************************************************************************/ @@ -28,7 +28,7 @@ * when it is full and halves the underlying array when it is one-quarter full. * The enqueue and dequeue operations take constant amortized time. * The size, peek, and is-empty operations takes - * constant time in the worst case. + * constant time in the worst case. *

* For additional documentation, see Section 1.3 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. @@ -37,6 +37,9 @@ * @author Kevin Wayne */ public class ResizingArrayQueue implements Iterable { + // initial capacity of underlying resizing array + private static final int INIT_CAPACITY = 8; + private Item[] q; // queue elements private int n; // number of elements on queue private int first; // index of first element of queue @@ -47,7 +50,7 @@ public class ResizingArrayQueue implements Iterable { * Initializes an empty queue. */ public ResizingArrayQueue() { - q = (Item[]) new Object[2]; + q = (Item[]) new Object[INIT_CAPACITY]; n = 0; first = 0; last = 0; @@ -72,11 +75,11 @@ public int size() { // resize the underlying array private void resize(int capacity) { assert capacity >= n; - Item[] temp = (Item[]) new Object[capacity]; + Item[] copy = (Item[]) new Object[capacity]; for (int i = 0; i < n; i++) { - temp[i] = q[(first + i) % q.length]; + copy[i] = q[(first + i) % q.length]; } - q = temp; + q = copy; first = 0; last = n; } @@ -106,7 +109,7 @@ public Item dequeue() { first++; if (first == q.length) first = 0; // wrap-around // shrink size of array if necessary - if (n > 0 && n == q.length/4) resize(q.length/2); + if (n > 0 && n == q.length/4) resize(q.length/2); return item; } @@ -129,11 +132,13 @@ public Iterator iterator() { return new ArrayIterator(); } - // an iterator, doesn't implement remove() since it's optional + // an array iterator, from first to last-1 private class ArrayIterator implements Iterator { private int i = 0; - public boolean hasNext() { return i < n; } - public void remove() { throw new UnsupportedOperationException(); } + + public boolean hasNext() { + return i < n; + } public Item next() { if (!hasNext()) throw new NoSuchElementException(); @@ -161,7 +166,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/ResizingArrayStack.java b/src/main/java/edu/princeton/cs/algs4/ResizingArrayStack.java index c489e0298..d9ce96cfa 100644 --- a/src/main/java/edu/princeton/cs/algs4/ResizingArrayStack.java +++ b/src/main/java/edu/princeton/cs/algs4/ResizingArrayStack.java @@ -3,10 +3,10 @@ * Execution: java ResizingArrayStack < input.txt * Dependencies: StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/13stacks/tobe.txt - * + * * Stack implementation with a resizing array. * - * % more tobe.txt + * % more tobe.txt * to be or not to - be - - that - - - is * * % java ResizingArrayStack < tobe.txt @@ -30,7 +30,7 @@ * when it is full and halves the underlying array when it is one-quarter full. * The push and pop operations take constant amortized time. * The size, peek, and is-empty operations takes - * constant time in the worst case. + * constant time in the worst case. *

* For additional documentation, * see Section 1.3 of @@ -40,6 +40,10 @@ * @author Kevin Wayne */ public class ResizingArrayStack implements Iterable { + + // initial capacity of underlying resizing array + private static final int INIT_CAPACITY = 8; + private Item[] a; // array of items private int n; // number of elements on stack @@ -48,7 +52,7 @@ public class ResizingArrayStack implements Iterable { * Initializes an empty stack. */ public ResizingArrayStack() { - a = (Item[]) new Object[2]; + a = (Item[]) new Object[INIT_CAPACITY]; n = 0; } @@ -74,11 +78,11 @@ private void resize(int capacity) { assert capacity >= n; // textbook implementation - Item[] temp = (Item[]) new Object[capacity]; + Item[] copy = (Item[]) new Object[capacity]; for (int i = 0; i < n; i++) { - temp[i] = a[i]; + copy[i] = a[i]; } - a = temp; + a = copy; // alternative implementation // a = java.util.Arrays.copyOf(a, capacity); @@ -129,7 +133,7 @@ public Iterator iterator() { return new ReverseArrayIterator(); } - // an iterator, doesn't implement remove() since it's optional + // a array iterator, in reverse order private class ReverseArrayIterator implements Iterator { private int i; @@ -141,10 +145,6 @@ public boolean hasNext() { return i >= 0; } - public void remove() { - throw new UnsupportedOperationException(); - } - public Item next() { if (!hasNext()) throw new NoSuchElementException(); return a[i--]; @@ -169,7 +169,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/RunLength.java b/src/main/java/edu/princeton/cs/algs4/RunLength.java index a86104efc..03e52865f 100644 --- a/src/main/java/edu/princeton/cs/algs4/RunLength.java +++ b/src/main/java/edu/princeton/cs/algs4/RunLength.java @@ -10,7 +10,7 @@ * Compress or expand binary input from standard input using * run-length encoding. * - * % java BinaryDump 40 < 4runs.bin + * % java BinaryDump 40 < 4runs.bin * 0000000000000001111111000000011111111111 * 40 bits * @@ -30,7 +30,7 @@ * run lengths. *

* For additional documentation, - * see Section 5.5 of + * see Section 5.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -48,8 +48,8 @@ private RunLength() { } * using run-length encoding with 8-bit run lengths); decodes them; * and writes the results to standard output. */ - public static void expand() { - boolean b = false; + public static void expand() { + boolean b = false; while (!BinaryStdIn.isEmpty()) { int run = BinaryStdIn.readInt(LG_R); for (int i = 0; i < run; i++) @@ -64,25 +64,25 @@ public static void expand() { * them using run-length coding with 8-bit run lengths; and writes the * results to standard output. */ - public static void compress() { - char run = 0; + public static void compress() { + char run = 0; boolean old = false; - while (!BinaryStdIn.isEmpty()) { + while (!BinaryStdIn.isEmpty()) { boolean b = BinaryStdIn.readBoolean(); if (b != old) { BinaryStdOut.write(run, LG_R); run = 1; old = !old; } - else { - if (run == R-1) { + else { + if (run == R-1) { BinaryStdOut.write(run, LG_R); run = 0; BinaryStdOut.write(run, LG_R); } run++; - } - } + } + } BinaryStdOut.write(run, LG_R); BinaryStdOut.close(); } @@ -103,7 +103,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/SET.java b/src/main/java/edu/princeton/cs/algs4/SET.java index 5782ea03f..a079d9d6b 100644 --- a/src/main/java/edu/princeton/cs/algs4/SET.java +++ b/src/main/java/edu/princeton/cs/algs4/SET.java @@ -2,7 +2,7 @@ * Compilation: javac SET.java * Execution: java SET * Dependencies: StdOut.java - * + * * Set implementation using Java's TreeSet library. * Does not allow duplicates. * @@ -46,7 +46,7 @@ * @author Robert Sedgewick * @author Kevin Wayne * - * @param the generic type of a key in this set + * @param the generic type of each key in this set */ public class SET> implements Iterable { @@ -95,6 +95,7 @@ public boolean contains(Key key) { /** * Removes the specified key from this set (if the set contains the specified key). + * This is equivalent to {@code remove()}, but we plan to deprecate {@code delete()}. * * @param key the key * @throws IllegalArgumentException if {@code key} is {@code null} @@ -104,6 +105,18 @@ public void delete(Key key) { set.remove(key); } + /** + * Removes the specified key from this set (if the set contains the specified key). + * This is equivalent to {@code delete()}, but we plan to deprecate {@code delete()}. + * + * @param key the key + * @throws IllegalArgumentException if {@code key} is {@code null} + */ + public void remove(Key key) { + if (key == null) throw new IllegalArgumentException("called remove() with a null key"); + set.remove(key); + } + /** * Returns the number of keys in this set. * @@ -122,7 +135,7 @@ public int size() { public boolean isEmpty() { return size() == 0; } - + /** * Returns all of the keys in this set, as an iterator. * To iterate over all of the keys in a set named {@code set}, use the @@ -229,14 +242,14 @@ public SET intersects(SET that) { return c; } - /** + /** * Compares this set to the specified set. *

* Note that this method declares two empty sets to be equal * even if they are parameterized by different generic types. - * This is consistent with the behavior of {@code equals()} + * This is consistent with the behavior of {@code equals()} * within Java's Collections framework. - * + * * @param other the other set * @return {@code true} if this set equals {@code other}; * {@code false} otherwise @@ -332,7 +345,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/ST.java b/src/main/java/edu/princeton/cs/algs4/ST.java index 057414d19..0d4a1286f 100644 --- a/src/main/java/edu/princeton/cs/algs4/ST.java +++ b/src/main/java/edu/princeton/cs/algs4/ST.java @@ -3,7 +3,7 @@ * Execution: java ST < input.txt * Dependencies: StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/35applications/tinyST.txt - * + * * Sorted symbol table implementation using a java.util.TreeMap. * Does not allow duplicates. * @@ -31,17 +31,21 @@ * value associated with a key to {@code null} is equivalent to deleting the key * from the symbol table. *

- * This implementation uses a balanced binary search tree. It requires that + * It requires that * the key type implements the {@code Comparable} interface and calls the * {@code compareTo()} and method to compare two keys. It does not call either * {@code equals()} or {@code hashCode()}. - * The put, contains, remove, minimum, - * maximum, ceiling, and floor operations each take - * logarithmic time in the worst case. - * The size, and is-empty operations take constant time. - * Construction takes constant time. *

- * For additional documentation, see Section 3.5 of + * This implementation uses a red-black BST. + * The put, get, contains, remove, + * minimum, maximum, ceiling, and floor + * operations each take Θ(log n) time in the worst case, + * where n is the number of key-value pairs in the symbol table. + * The size and is-empty operations take Θ(1) time. + * Construction takes Θ(1) time. + *

+ * For additional documentation, see + * Section 3.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -76,7 +80,7 @@ public Value get(Key key) { } /** - * Inserts the specified key-value pair into the symbol table, overwriting the old + * Inserts the specified key-value pair into the symbol table, overwriting the old * value with the new value if the symbol table already contains the specified key. * Deletes the specified key (and its associated value) from this symbol table * if the specified value is {@code null}. @@ -92,8 +96,9 @@ public void put(Key key, Value val) { } /** - * Removes the specified key and its associated value from this symbol table + * Removes the specified key and its associated value from this symbol table * (if the key is in this symbol table). + * This is equivalent to {@code remove()}, but we plan to deprecate {@code delete()}. * * @param key the key * @throws IllegalArgumentException if {@code key} is {@code null} @@ -103,6 +108,19 @@ public void delete(Key key) { st.remove(key); } + /** + * Removes the specified key and its associated value from this symbol table + * (if the key is in this symbol table). + * This is equivalent to {@code delete()}, but we plan to deprecate {@code delete()}. + * + * @param key the key + * @throws IllegalArgumentException if {@code key} is {@code null} + */ + public void remove(Key key) { + if (key == null) throw new IllegalArgumentException("calls remove() with null key"); + st.remove(key); + } + /** * Returns true if this symbol table contain the given key. * @@ -135,26 +153,27 @@ public boolean isEmpty() { } /** - * Returns all keys in this symbol table. + * Returns all keys in this symbol table in ascending order, + * as an {@code Iterable}. *

* To iterate over all of the keys in the symbol table named {@code st}, * use the foreach notation: {@code for (Key key : st.keys())}. * - * @return all keys in this symbol table + * @return all keys in this symbol table in ascending order */ public Iterable keys() { return st.keySet(); } /** - * Returns all of the keys in this symbol table. + * Returns all keys in this symbol table in ascending order. * To iterate over all of the keys in a symbol table named {@code st}, use the * foreach notation: {@code for (Key key : st)}. *

* This method is provided for backward compatibility with the version from * Introduction to Programming in Java: An Interdisciplinary Approach. * - * @return an iterator to all of the keys in this symbol table + * @return all keys in this symbol table in ascending order * @deprecated Replaced by {@link #keys()}. */ @Deprecated @@ -195,7 +214,7 @@ public Key max() { public Key ceiling(Key key) { if (key == null) throw new IllegalArgumentException("argument to ceiling() is null"); Key k = st.ceilingKey(key); - if (k == null) throw new NoSuchElementException("all keys are less than " + key); + if (k == null) throw new NoSuchElementException("argument to ceiling() is too large"); return k; } @@ -210,7 +229,7 @@ public Key ceiling(Key key) { public Key floor(Key key) { if (key == null) throw new IllegalArgumentException("argument to floor() is null"); Key k = st.floorKey(key); - if (k == null) throw new NoSuchElementException("all keys are greater than " + key); + if (k == null) throw new NoSuchElementException("argument to floor() is too small"); return k; } @@ -231,7 +250,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/SegmentTree.java b/src/main/java/edu/princeton/cs/algs4/SegmentTree.java index 80cb84363..0f235a81a 100644 --- a/src/main/java/edu/princeton/cs/algs4/SegmentTree.java +++ b/src/main/java/edu/princeton/cs/algs4/SegmentTree.java @@ -334,7 +334,7 @@ else if (line[0].equals("rmq")) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Selection.java b/src/main/java/edu/princeton/cs/algs4/Selection.java index a21af787e..a64d0ab71 100644 --- a/src/main/java/edu/princeton/cs/algs4/Selection.java +++ b/src/main/java/edu/princeton/cs/algs4/Selection.java @@ -4,18 +4,18 @@ * Dependencies: StdOut.java StdIn.java * Data files: https://algs4.cs.princeton.edu/21elementary/tiny.txt * https://algs4.cs.princeton.edu/21elementary/words3.txt - * + * * Sorts a sequence of strings from standard input using selection sort. - * + * * % more tiny.txt * S O R T E X A M P L E * * % java Selection < tiny.txt * A E E L M O P R S T X [ one string per line ] - * + * * % more words3.txt * bed bug dad yes zoo ... all bad yet - * + * * % java Selection < words3.txt * all bad bed bug dad ... yes yet zoo [ one string per line ] * @@ -27,10 +27,17 @@ /** * The {@code Selection} class provides static methods for sorting an - * array using selection sort. + * array using selection sort. + * This implementation makes ~ ½ n2 compares to sort + * any array of length n, so it is not suitable for sorting large arrays. + * It performs exactly n exchanges. + *

+ * This sorting algorithm is not stable. It uses Θ(1) extra memory + * (not including the input array). *

- * For additional documentation, see Section 2.1 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, see + * Section 2.1 + * of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -79,7 +86,7 @@ public static void sort(Object[] a, Comparator comparator) { /*************************************************************************** * Helper sorting functions. ***************************************************************************/ - + // is v < w ? private static boolean less(Comparable v, Comparable w) { return v.compareTo(w) < 0; @@ -89,8 +96,8 @@ private static boolean less(Comparable v, Comparable w) { private static boolean less(Comparator comparator, Object v, Object w) { return comparator.compare(v, w) < 0; } - - + + // exchange a[i] and a[j] private static void exch(Object[] a, int i, int j) { Object swap = a[i]; @@ -107,7 +114,7 @@ private static void exch(Object[] a, int i, int j) { private static boolean isSorted(Comparable[] a) { return isSorted(a, 0, a.length - 1); } - + // is the array sorted from a[lo] to a[hi] private static boolean isSorted(Comparable[] a, int lo, int hi) { for (int i = lo + 1; i <= hi; i++) @@ -137,8 +144,8 @@ private static void show(Comparable[] a) { } /** - * Reads in a sequence of strings from standard input; selection sorts them; - * and prints them to standard output in ascending order. + * Reads in a sequence of strings from standard input; selection sorts them; + * and prints them to standard output in ascending order. * * @param args the command-line arguments */ @@ -150,7 +157,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/SeparateChainingHashST.java b/src/main/java/edu/princeton/cs/algs4/SeparateChainingHashST.java index 42968022f..a6e0ca140 100644 --- a/src/main/java/edu/princeton/cs/algs4/SeparateChainingHashST.java +++ b/src/main/java/edu/princeton/cs/algs4/SeparateChainingHashST.java @@ -5,7 +5,7 @@ * Data files: https://algs4.cs.princeton.edu/34hash/tinyST.txt * * A symbol table implemented with a separate-chaining hash table. - * + * ******************************************************************************/ package edu.princeton.cs.algs4; @@ -53,7 +53,7 @@ public class SeparateChainingHashST { */ public SeparateChainingHashST() { this(INIT_CAPACITY); - } + } /** * Initializes an empty symbol table with {@code m} chains. @@ -64,7 +64,7 @@ public SeparateChainingHashST(int m) { st = (SequentialSearchST[]) new SequentialSearchST[m]; for (int i = 0; i < m; i++) st[i] = new SequentialSearchST(); - } + } // resize the hash table to have the given number of chains, // rehashing all of the keys @@ -80,10 +80,18 @@ private void resize(int chains) { this.st = temp.st; } - // hash value between 0 and m-1 - private int hash(Key key) { + // hash function for keys - returns value between 0 and m-1 + private int hashTextbook(Key key) { return (key.hashCode() & 0x7fffffff) % m; - } + } + + // hash function for keys - returns value between 0 and m-1 (assumes m is a power of 2) + // (from Java 7 implementation, protects against poor quality hashCode() implementations) + private int hash(Key key) { + int h = key.hashCode(); + h ^= (h >>> 20) ^ (h >>> 12) ^ (h >>> 7) ^ (h >>> 4); + return h & (m-1); + } /** * Returns the number of key-value pairs in this symbol table. @@ -92,7 +100,7 @@ private int hash(Key key) { */ public int size() { return n; - } + } /** * Returns true if this symbol table is empty. @@ -115,7 +123,7 @@ public boolean isEmpty() { public boolean contains(Key key) { if (key == null) throw new IllegalArgumentException("argument to contains() is null"); return get(key) != null; - } + } /** * Returns the value associated with the specified key in this symbol table. @@ -129,10 +137,10 @@ public Value get(Key key) { if (key == null) throw new IllegalArgumentException("argument to get() is null"); int i = hash(key); return st[i].get(key); - } + } /** - * Inserts the specified key-value pair into the symbol table, overwriting the old + * Inserts the specified key-value pair into the symbol table, overwriting the old * value with the new value if the symbol table already contains the specified key. * Deletes the specified key (and its associated value) from this symbol table * if the specified value is {@code null}. @@ -154,11 +162,11 @@ public void put(Key key, Value val) { int i = hash(key); if (!st[i].contains(key)) n++; st[i].put(key, val); - } + } /** - * Removes the specified key and its associated value from this symbol table - * (if the key is in this symbol table). + * Removes the specified key and its associated value from this symbol table + * (if the key is in this symbol table). * * @param key the key * @throws IllegalArgumentException if {@code key} is {@code null} @@ -172,7 +180,7 @@ public void delete(Key key) { // halve table size if average length of list <= 2 if (m > INIT_CAPACITY && n <= 2*m) resize(m/2); - } + } // return keys in symbol table as an Iterable public Iterable keys() { @@ -182,7 +190,7 @@ public Iterable keys() { queue.enqueue(key); } return queue; - } + } /** @@ -190,7 +198,7 @@ public Iterable keys() { * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { SeparateChainingHashST st = new SeparateChainingHashST(); for (int i = 0; !StdIn.isEmpty(); i++) { String key = StdIn.readString(); @@ -198,15 +206,15 @@ public static void main(String[] args) { } // print keys - for (String s : st.keys()) - StdOut.println(s + " " + st.get(s)); + for (String s : st.keys()) + StdOut.println(s + " " + st.get(s)); } } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/SequentialSearchST.java b/src/main/java/edu/princeton/cs/algs4/SequentialSearchST.java index 813b62a17..81731400a 100644 --- a/src/main/java/edu/princeton/cs/algs4/SequentialSearchST.java +++ b/src/main/java/edu/princeton/cs/algs4/SequentialSearchST.java @@ -2,15 +2,15 @@ * Compilation: javac SequentialSearchST.java * Execution: java SequentialSearchST * Dependencies: StdIn.java StdOut.java - * Data files: https://algs4.cs.princeton.edu/31elementary/tinyST.txt - * + * Data files: https://algs4.cs.princeton.edu/31elementary/tinyST.txt + * * Symbol table implementation with sequential search in an * unordered linked list of key-value pairs. * * % more tinyST.txt * S E A R C H E X A M P L E * - * % java SequentialSearchST < tiny.txt + * % java SequentialSearchST < tinyST.txt * L 11 * P 10 * M 9 @@ -39,16 +39,20 @@ * value associated with a key to {@code null} is equivalent to deleting the key * from the symbol table. *

- * This implementation uses a singly-linked list and sequential search. * It relies on the {@code equals()} method to test whether two keys * are equal. It does not call either the {@code compareTo()} or - * {@code hashCode()} method. - * The put and delete operations take linear time; the - * get and contains operations takes linear time in the worst case. - * The size, and is-empty operations take constant time. - * Construction takes constant time. + * {@code hashCode()} method. + *

+ * This implementation uses a singly linked list and + * sequential search. + * The put and delete operations take Θ(n). + * The get and contains operations takes Θ(n) + * time in the worst case. + * The size, and is-empty operations take Θ(1) time. + * Construction takes Θ(1) time. *

- * For additional documentation, see Section 3.1 of + * For additional documentation, see + * Section 3.1 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -118,7 +122,7 @@ public boolean contains(Key key) { * @throws IllegalArgumentException if {@code key} is {@code null} */ public Value get(Key key) { - if (key == null) throw new IllegalArgumentException("argument to get() is null"); + if (key == null) throw new IllegalArgumentException("argument to get() is null"); for (Node x = first; x != null; x = x.next) { if (key.equals(x.key)) return x.val; @@ -127,7 +131,7 @@ public Value get(Key key) { } /** - * Inserts the specified key-value pair into the symbol table, overwriting the old + * Inserts the specified key-value pair into the symbol table, overwriting the old * value with the new value if the symbol table already contains the specified key. * Deletes the specified key (and its associated value) from this symbol table * if the specified value is {@code null}. @@ -137,7 +141,7 @@ public Value get(Key key) { * @throws IllegalArgumentException if {@code key} is {@code null} */ public void put(Key key, Value val) { - if (key == null) throw new IllegalArgumentException("first argument to put() is null"); + if (key == null) throw new IllegalArgumentException("first argument to put() is null"); if (val == null) { delete(key); return; @@ -154,14 +158,14 @@ public void put(Key key, Value val) { } /** - * Removes the specified key and its associated value from this symbol table - * (if the key is in this symbol table). + * Removes the specified key and its associated value from this symbol table + * (if the key is in this symbol table). * * @param key the key * @throws IllegalArgumentException if {@code key} is {@code null} */ public void delete(Key key) { - if (key == null) throw new IllegalArgumentException("argument to delete() is null"); + if (key == null) throw new IllegalArgumentException("argument to delete() is null"); first = delete(first, key); } @@ -210,7 +214,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Shell.java b/src/main/java/edu/princeton/cs/algs4/Shell.java index e7a8fa4e0..a44a7e9c5 100644 --- a/src/main/java/edu/princeton/cs/algs4/Shell.java +++ b/src/main/java/edu/princeton/cs/algs4/Shell.java @@ -4,24 +4,18 @@ * Dependencies: StdOut.java StdIn.java * Data files: https://algs4.cs.princeton.edu/21elementary/tiny.txt * https://algs4.cs.princeton.edu/21elementary/words3.txt - * + * * Sorts a sequence of strings from standard input using shellsort. * - * Uses increment sequence proposed by Sedgewick and Incerpi. - * The nth element of the sequence is the smallest integer >= 2.5^n - * that is relatively prime to all previous terms in the sequence. - * For example, incs[4] is 41 because 2.5^4 = 39.0625 and 41 is - * the next integer that is relatively prime to 3, 7, and 16. - * * % more tiny.txt * S O R T E X A M P L E * * % java Shell < tiny.txt * A E E L M O P R S T X [ one string per line ] - * + * * % more words3.txt * bed bug dad yes zoo ... all bad yet - * + * * % java Shell < words3.txt * all bad bed bug dad ... yes yet zoo [ one string per line ] * @@ -32,11 +26,19 @@ /** * The {@code Shell} class provides static methods for sorting an - * array using Shellsort with Knuth's increment sequence (1, 4, 13, 40, ...). + * array using Shellsort with + * Knuth's increment sequence + * (1, 4, 13, 40, ...). In the worst case, this implementation makes + * Θ(n3/2) compares and exchanges to sort + * an array of length n. + *

+ * This sorting algorithm is not stable. + * It uses Θ(1) extra memory (not including the input array). *

- * For additional documentation, see Section 2.1 of + * For additional documentation, see + * Section 2.1 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. - * + * * @author Robert Sedgewick * @author Kevin Wayne */ @@ -52,9 +54,9 @@ private Shell() { } public static void sort(Comparable[] a) { int n = a.length; - // 3x+1 increment sequence: 1, 4, 13, 40, 121, 364, 1093, ... + // 3x+1 increment sequence: 1, 4, 13, 40, 121, 364, 1093, ... int h = 1; - while (h < n/3) h = 3*h + 1; + while (h < n/3) h = 3*h + 1; while (h >= 1) { // h-sort the array @@ -63,7 +65,7 @@ public static void sort(Comparable[] a) { exch(a, j, j-h); } } - assert isHsorted(a, h); + assert isHsorted(a, h); h /= 3; } assert isSorted(a); @@ -74,12 +76,12 @@ public static void sort(Comparable[] a) { /*************************************************************************** * Helper sorting functions. ***************************************************************************/ - + // is v < w ? private static boolean less(Comparable v, Comparable w) { return v.compareTo(w) < 0; } - + // exchange a[i] and a[j] private static void exch(Object[] a, int i, int j) { Object swap = a[i]; @@ -112,8 +114,8 @@ private static void show(Comparable[] a) { } /** - * Reads in a sequence of strings from standard input; Shellsorts them; - * and prints them to standard output in ascending order. + * Reads in a sequence of strings from standard input; Shellsorts them; + * and prints them to standard output in ascending order. * * @param args the command-line arguments */ @@ -126,7 +128,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/SparseVector.java b/src/main/java/edu/princeton/cs/algs4/SparseVector.java index 7f391f396..caa26123a 100644 --- a/src/main/java/edu/princeton/cs/algs4/SparseVector.java +++ b/src/main/java/edu/princeton/cs/algs4/SparseVector.java @@ -2,7 +2,7 @@ * Compilation: javac SparseVector.java * Execution: java SparseVector * Dependencies: StdOut.java - * + * * A sparse vector, implementing using a symbol table. * * [Not clear we need the instance variable N except for error checking.] @@ -18,12 +18,12 @@ * dot product, scalar product, unit vector, and Euclidean norm. *

* The implementation is a symbol table of indices and values for which the vector - * coordinates are nonzero. This makes it efficient when most of the vector coordindates + * coordinates are nonzero. This makes it efficient when most of the vector coordinates * are zero. *

- * For additional documentation, + * For additional documentation, * see Section 3.5 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * See also {@link Vector} for an immutable (dense) vector data type. * * @author Robert Sedgewick @@ -138,7 +138,7 @@ public double dot(double[] that) { /** * Returns the magnitude of this vector. * This is also known as the L2 norm or the Euclidean norm. - * + * * @return the magnitude of this vector */ public double magnitude() { @@ -186,8 +186,8 @@ public SparseVector plus(SparseVector that) { /** * Returns a string representation of this vector. - * @return a string representation of this vector, which consists of the - * the vector entries, separates by commas, enclosed in parentheses + * @return a string representation of this vector, which consists of the + * vector entries, separates by commas, enclosed in parentheses */ public String toString() { StringBuilder s = new StringBuilder(); @@ -221,7 +221,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Stack.java b/src/main/java/edu/princeton/cs/algs4/Stack.java index 76250c6b9..91bb565d5 100644 --- a/src/main/java/edu/princeton/cs/algs4/Stack.java +++ b/src/main/java/edu/princeton/cs/algs4/Stack.java @@ -10,8 +10,8 @@ * This version uses a static nested class Node (to save 8 bytes per * Node), whereas the version in the textbook uses a non-static nested * class (for simplicity). - * - * % more tobe.txt + * + * % more tobe.txt * to be or not to - be - - that - - - is * * % java Stack < tobe.txt @@ -45,7 +45,7 @@ * @author Robert Sedgewick * @author Kevin Wayne * - * @param the generic type of an item in this stack + * @param the generic type each item in this stack */ public class Stack implements Iterable { private Node first; // top of stack @@ -135,7 +135,7 @@ public String toString() { } return s.toString(); } - + /** * Returns an iterator to this stack that iterates through the items in LIFO order. @@ -143,29 +143,27 @@ public String toString() { * @return an iterator to this stack that iterates through the items in LIFO order */ public Iterator iterator() { - return new ListIterator(first); + return new LinkedIterator(first); } - // an iterator, doesn't implement remove() since it's optional - private class ListIterator implements Iterator { + // the iterator + private class LinkedIterator implements Iterator { private Node current; - public ListIterator(Node first) { + public LinkedIterator(Node first) { current = first; } + // is there a next item? public boolean hasNext() { return current != null; } - public void remove() { - throw new UnsupportedOperationException(); - } - + // returns the next item public Item next() { if (!hasNext()) throw new NoSuchElementException(); Item item = current.item; - current = current.next; + current = current.next; return item; } } @@ -191,7 +189,7 @@ else if (!stack.isEmpty()) /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/StaticSETofInts.java b/src/main/java/edu/princeton/cs/algs4/StaticSETofInts.java index 6bcd91e05..78874d02c 100644 --- a/src/main/java/edu/princeton/cs/algs4/StaticSETofInts.java +++ b/src/main/java/edu/princeton/cs/algs4/StaticSETofInts.java @@ -2,7 +2,7 @@ * Compilation: javac StaticSetOfInts.java * Execution: none * Dependencies: StdOut.java - * + * * Data type to store a set of integers. * ******************************************************************************/ @@ -81,7 +81,7 @@ public int rank(int key) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/StdArrayIO.java b/src/main/java/edu/princeton/cs/algs4/StdArrayIO.java index 46101cde9..88cb46d7b 100644 --- a/src/main/java/edu/princeton/cs/algs4/StdArrayIO.java +++ b/src/main/java/edu/princeton/cs/algs4/StdArrayIO.java @@ -10,19 +10,19 @@ * and booleans from standard input and printing them out to * standard output. * - * % more tinyDouble1D.txt + * % more tinyDouble1D.txt * 4 * .000 .246 .222 -.032 * - * % more tinyDouble2D.txt - * 4 3 - * .000 .270 .000 - * .246 .224 -.036 - * .222 .176 .0893 - * -.032 .739 .270 + * % more tinyDouble2D.txt + * 4 3 + * .000 .270 .000 + * .246 .224 -.036 + * .222 .176 .0893 + * -.032 .739 .270 * - * % more tinyBoolean2D.txt - * 4 3 + * % more tinyBoolean2D.txt + * 4 3 * 1 1 0 * 0 0 0 * 0 1 1 @@ -30,19 +30,19 @@ * * % cat tinyDouble1D.txt tinyDouble2D.txt tinyBoolean2D.txt | java StdArrayIO * 4 - * 0.00000 0.24600 0.22200 -0.03200 - * + * 0.00000 0.24600 0.22200 -0.03200 + * * 4 3 - * 0.00000 0.27000 0.00000 - * 0.24600 0.22400 -0.03600 - * 0.22200 0.17600 0.08930 - * 0.03200 0.73900 0.27000 + * 0.00000 0.27000 0.00000 + * 0.24600 0.22400 -0.03600 + * 0.22200 0.17600 0.08930 + * 0.03200 0.73900 0.27000 * * 4 3 - * 1 1 0 - * 0 0 0 - * 0 1 1 - * 1 1 1 + * 1 1 0 + * 0 0 0 + * 0 1 1 + * 1 1 1 * ******************************************************************************/ @@ -50,8 +50,8 @@ /** - * Standard array IO. This class provides methods for reading - * in 1D and 2D arrays from standard input and printing out to + * The StdArrayIO class provides static methods for reading + * in 1D and 2D arrays from standard input and printing out to * standard output. *

* For additional documentation, see @@ -95,7 +95,6 @@ public static void print(double[] a) { StdOut.println(); } - /** * Reads a 2D array of doubles from standard input and returns it. * @@ -159,7 +158,6 @@ public static void print(int[] a) { StdOut.println(); } - /** * Reads a 2D array of integers from standard input and returns it. * @@ -194,7 +192,6 @@ public static void print(int[][] a) { } } - /** * Reads a 1D array of booleans from standard input and returns it. * @@ -259,7 +256,6 @@ public static void print(boolean[][] a) { } } - /** * Unit tests {@code StdArrayIO}. * @@ -286,7 +282,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/StdAudio.java b/src/main/java/edu/princeton/cs/algs4/StdAudio.java index e23116432..2ab2b4ced 100644 --- a/src/main/java/edu/princeton/cs/algs4/StdAudio.java +++ b/src/main/java/edu/princeton/cs/algs4/StdAudio.java @@ -2,14 +2,8 @@ * Compilation: javac StdAudio.java * Execution: java StdAudio * Dependencies: none - * - * Simple library for reading, writing, and manipulating .wav files. * - * - * Limitations - * ----------- - * - Does not seem to work properly when reading .wav files from a .jar file. - * - Assumes the audio is monaural, with sampling rate of 44,100. + * Simple library for reading, writing, and manipulating audio. * ******************************************************************************/ @@ -17,18 +11,17 @@ import javax.sound.sampled.Clip; -// for playing midi sound files on some older systems -import java.applet.Applet; -import java.applet.AudioClip; -import java.net.MalformedURLException; - import java.io.File; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; +import java.util.LinkedList; + import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; @@ -39,14 +32,180 @@ import javax.sound.sampled.UnsupportedAudioFileException; /** - * Standard audio. This class provides a basic capability for - * creating, reading, and saving audio. + * The {@code StdAudio} class provides static methods for + * playing, reading, and saving audio. + * It uses a simple audio model that allows you + * to send one sample to the sound card at a time. + * Each sample is a real number between –1.0 and +1.0. + * The samples are played in real time using a sampling + * rate of 44,100 Hz. + * In addition to playing individual samples, standard audio supports + * reading, writing, and playing audio files in a variety of standard formats. + *

+ * See {@link StdAudioStereo} for a version that supports + * stereo audio (separate left and right channels). + *

+ * Getting started. + * To use this class, you must have {@code StdAudio} in your Java classpath. + * Here are three possible ways to do this: + *

    + *
  • If you ran our autoinstaller, use the commands + * {@code javac-introcs} and {@code java-introcs} (or {@code javac-algs4} + * and {@code java-algs4}) when compiling and executing. These commands + * add {@code stdlib.jar} (or {@code algs4.jar}) to the Java classpath, which + * provides access to {@code StdAudio}. + *
  • Download stdlib.jar + * (or algs4.jar) + * and add it to the Java classpath. + *
  • Download StdAudio.java + * and put it in the working directory. + *
+ *

+ * As a test, cut-and-paste the following short program into your editor: + *

+ *   public class TestStdAudio {
+ *       public static void main(String[] args) {
+ *           double freq = 440.0;
+ *           for (int i = 0; i < StdAudio.SAMPLE_RATE; i++) {
+ *               double sample = 0.5 * Math.sin(2 * Math.PI * freq * i / StdAudio.SAMPLE_RATE);
+ *               StdAudio.play(sample);
+ *           }
+ *           StdAudio.drain();
+ *       }
+ *   }
+ *  
+ *

+ * If you compile and execute the program, you should hear a pure tone + * whose frequency is concert A (440 Hz). + * + *

+ * Playing audio samples. + * You can use the following two methods to play individual audio samples: + *

    + *
  • {@link #play(double sample)} + *
  • {@link #play(double[] samples)} + *
+ *

+ * Each method sends the specified sample (or samples) to the sound card. + * The individual samples are real numbers between –1.0 and +1.0. If a + * sample is outside this range, it will be clipped (rounded to + * –1.0 or +1.0). The samples are played in real time using a sampling + * rate of 44,100 Hz. + * + *

+ * Playing audio files. + * You can use the following method to play an audio file: + *

    + *
  • {@link #play(String filename)} + *
+ *

+ * It plays an audio file (in WAVE, AU, AIFF, or MIDI format) and does + * not return until the audio file is finished playing. This can produce + * particularly striking programs with minimal code. + * For example, the following code fragment plays a drum loop: + * + *

+ *   while (true) {
+ *       StdAudio.play("BassDrum.wav");
+ *       StdAudio.play("SnareDrum.wav");
+ *   }
+ *  
+ * + * The individual audio files + * (such as BassDrum.wav + * and SnareDrum.wav) + * must be accessible to Java, typically + * by being in the same directory as the {@code .class} file. + *

+ * + * Reading and writing audio files. + * You can read and write audio files using the following two methods: + *

    + *
  • {@link #read(String filename)} + *
  • {@link #save(String filename, double[] samples)} + *
*

- * The audio format uses a sampling rate of 44,100 (CD quality audio), 16-bit, monaural. + * The first method reads audio samples from an audio file + * (in WAVE, AU, AIFF, or MIDI format) + * and returns them as a double array with values between –1.0 and +1.0. + * The second method saves the audio samples in the specified double array to an + * audio file (in WAVE, AU, or AIFF format). * *

- * For additional documentation, see Section 1.5 of - * Computer Science: An Interdisciplinary Approach by Robert Sedgewick and Kevin Wayne. + * Audio file formats. + * {@code StdAudio} relies on the + * Java Media Framework + * for reading, writing, and playing audio files. You should be able to read or play files + * in WAVE, AU, AIFF, and MIDI formats and save them to WAVE, AU, and AIFF formats. + * The file extensions corresponding to WAVE, AU, AIFF, and MIDI files + * are {@code .wav}, {@code .au}, {@code .aiff}, and {@code .midi}, + * respectively. + * Some systems support additional audio file formats, but probably not MP3 or M4A. + *

+ * The Java Media Framework supports a variety of different audio data formats, + * which includes + *

    + *
  • the sampling rate (e.g., 44,100 Hz); + *
  • the number of bits per sample per channel (e.g., 8-bit or 16-bit); + *
  • the number of channels (e.g., monaural or stereo); + *
  • the byte ordering (e.g., little endian or big endian); and + *
  • the encoding scheme (typically linear PCM). + *
+ *

+ * When saving files, {@code StdAudio} uses a sampling rate of 44,100 Hz, + * 16 bits per sample, monaural audio, little endian, and linear PCM encoding. + * When reading files, {@code StdAudio} converts to a sammpling rate of 44,100 Hz, + * with 16 bits per sample. + * + *

+ * Recording audio. + * You can use the following methods to record audio samples that are + * played as a result of calls to {@link #play(double sample)} or + * {@link #play(double[] samples)}. + *

    + *
  • {@link #startRecording()} + *
  • {@link #stopRecording()} + *
+ *

+ * The method {@code startRecording()} begins recording audio. + * The method {@code stopRecording()} stops recording and returns the recorded + * samples as an array of doubles. + *

+ * {@code StdAudio} does not currently support recording audio that calls + * {@code playInBackground()}. + *

+ * Playing audio files in a background thread. + * You can use the following methods to play an audio file in a background thread + * (e.g., as a background score in your program). + *

    + *
  • {@link #playInBackground(String filename)} + *
  • {@link #stopInBackground()} + *
+ *

+ * Each call to the first method plays the specified sound in a separate background + * thread. Unlike with the {@code play()} methods, your program will not wait + * for the samples to finish playing before continuing. + * It supports playing an audio file in WAVE, AU, AIFF, or MIDI format. + * It is possible to play + * multiple audio files simultaneously (in separate background threads). + * The second method stops the playing of all audio in background threads. + *

+ * Draining standard audio. + * On some systems, your Java program may terminate before all of the samples have been + * sent to the sound card. To prevent this, it is recommend that you call the + * following method to indicate that you are done using standard audio: + *

    + *
  • {@link #drain()} + *
+ *

+ * The method drains any samples queued to the sound card that have not yet been + * sent to the sound card. + *

+ * Reference. + * For additional documentation, + * see Section 1.5 of + * Computer Science: An Interdisciplinary Approach + * by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -54,24 +213,38 @@ public final class StdAudio { /** - * The sample rate - 44,100 Hz for CD quality audio. + * The sample rate: 44,100 Hz for CD quality audio. */ public static final int SAMPLE_RATE = 44100; - private static final int BYTES_PER_SAMPLE = 2; // 16-bit audio - private static final int BITS_PER_SAMPLE = 16; // 16-bit audio - private static final double MAX_16_BIT = Short.MAX_VALUE; // 32,767 + private static final int BYTES_PER_SAMPLE = 2; // 16-bit audio + private static final int BITS_PER_SAMPLE = 16; // 16-bit audio + private static final int MAX_16_BIT = 32768; private static final int SAMPLE_BUFFER_SIZE = 4096; + private static final int MONAURAL = 1; + private static final int STEREO = 2; + private static final boolean LITTLE_ENDIAN = false; + private static final boolean BIG_ENDIAN = true; + private static final boolean SIGNED = true; + private static final boolean UNSIGNED = false; + private static SourceDataLine line; // to play the sound private static byte[] buffer; // our internal buffer private static int bufferSize = 0; // number of samples currently in internal buffer + // queue of background Runnable objects + private static LinkedList backgroundRunnables = new LinkedList<>(); + + // for recording audio + private static QueueOfDoubles recordedSamples = null; + private static boolean isRecording = false; + private StdAudio() { // can not instantiate } - + // static initializer static { init(); @@ -80,13 +253,13 @@ private StdAudio() { // open up an audio stream private static void init() { try { - // 44,100 samples per second, 16-bit audio, mono, signed PCM, little Endian - AudioFormat format = new AudioFormat((float) SAMPLE_RATE, BITS_PER_SAMPLE, 1, true, false); + // 44,100 Hz, 16-bit audio, monaural, signed PCM, little endian + AudioFormat format = new AudioFormat((float) SAMPLE_RATE, BITS_PER_SAMPLE, MONAURAL, SIGNED, LITTLE_ENDIAN); DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); line = (SourceDataLine) AudioSystem.getLine(info); line.open(format, SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE); - + // the internal buffer is a fraction of the actual buffer size, this choice is arbitrary // it gets divided because we can't expect the buffered data to line up exactly with when // the sound card decides to push out its samples. @@ -100,35 +273,95 @@ private static void init() { line.start(); } + // get an AudioInputStream object from a file + private static AudioInputStream getAudioInputStreamFromFile(String filename) { + if (filename == null) { + throw new IllegalArgumentException("filename is null"); + } + + try { + // first try to read file from local file system + File file = new File(filename); + if (file.exists()) { + return AudioSystem.getAudioInputStream(file); + } + + // resource relative to .class file + InputStream is1 = StdAudio.class.getResourceAsStream(filename); + if (is1 != null) { + return AudioSystem.getAudioInputStream(is1); + } + + // resource relative to classloader root + InputStream is2 = StdAudio.class.getClassLoader().getResourceAsStream(filename); + if (is2 != null) { + return AudioSystem.getAudioInputStream(is2); + } + + // from URL (including jar file) + URI uri = new URI(filename); + if (uri.isAbsolute()) { + URL url = uri.toURL(); + return AudioSystem.getAudioInputStream(url); + } + else throw new IllegalArgumentException("could not read audio file '" + filename + "'"); + } + catch (IOException | URISyntaxException e) { + throw new IllegalArgumentException("could not read audio file '" + filename + "'", e); + } + catch (UnsupportedAudioFileException e) { + throw new IllegalArgumentException("file of unsupported audio file format: '" + filename + "'", e); + } + } + + /** + * Sends any queued samples to the sound card. + */ + public static void drain() { + if (bufferSize > 0) { + line.write(buffer, 0, bufferSize); + bufferSize = 0; + } + line.drain(); + } + /** * Closes standard audio. */ +/* public static void close() { - line.drain(); + drain(); line.stop(); } - +*/ /** - * Writes one sample (between -1.0 and +1.0) to standard audio. - * If the sample is outside the range, it will be clipped. + * Writes one sample (between –1.0 and +1.0) to standard audio. + * If the sample is outside the range, it will be clipped + * (rounded to –1.0 or +1.0). * * @param sample the sample to play * @throws IllegalArgumentException if the sample is {@code Double.NaN} */ public static void play(double sample) { + if (Double.isNaN(sample)) throw new IllegalArgumentException("sample is NaN"); // clip if outside [-1, +1] - if (Double.isNaN(sample)) throw new IllegalArgumentException("sample is NaN"); if (sample < -1.0) sample = -1.0; if (sample > +1.0) sample = +1.0; + // save sample if recording + if (isRecording) { + recordedSamples.enqueue(sample); + } + // convert to bytes short s = (short) (MAX_16_BIT * sample); + if (sample == 1.0) s = Short.MAX_VALUE; // special case since 32768 not a short buffer[bufferSize++] = (byte) s; - buffer[bufferSize++] = (byte) (s >> 8); // little Endian + buffer[bufferSize++] = (byte) (s >> 8); // little endian - // send to sound card if buffer is full + // send to sound card if buffer is full if (bufferSize >= buffer.length) { line.write(buffer, 0, buffer.length); bufferSize = 0; @@ -136,7 +369,7 @@ public static void play(double sample) { } /** - * Writes the array of samples (between -1.0 and +1.0) to standard audio. + * Writes the array of samples (between –1.0 and +1.0) to standard audio. * If a sample is outside the range, it will be clipped. * * @param samples the array of samples to play @@ -151,96 +384,170 @@ public static void play(double[] samples) { } /** - * Reads audio samples from a file (in .wav or .au format) and returns - * them as a double array with values between -1.0 and +1.0. + * Plays an audio file (in WAVE, AU, AIFF, or MIDI format) and waits for it to finish. + * The file extension must be either {@code .wav}, {@code .au}, + * or {@code .aiff}. + * + * @param filename the name of the audio file + * @throws IllegalArgumentException if unable to play {@code filename} + * @throws IllegalArgumentException if {@code filename} is {@code null} + */ + public static void play(String filename) { + + // may not work for streaming file formats + if (isRecording) { + double[] samples = read(filename); + for (double sample : samples) + recordedSamples.enqueue(sample); + } + + AudioInputStream ais = getAudioInputStreamFromFile(filename); + SourceDataLine line = null; + int BUFFER_SIZE = 4096; // 4K buffer + try { + AudioFormat audioFormat = ais.getFormat(); + DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); + line = (SourceDataLine) AudioSystem.getLine(info); + line.open(audioFormat); + line.start(); + byte[] samples = new byte[BUFFER_SIZE]; + int count; + while ((count = ais.read(samples, 0, BUFFER_SIZE)) != -1) { + line.write(samples, 0, count); + } + } + catch (IOException | LineUnavailableException e) { + System.out.println(e); + } + finally { + if (line != null) { + line.drain(); + line.close(); + } + } + } + + + /** + * Reads audio samples from a file (in WAVE, AU, AIFF, or MIDI format) + * and returns them as a double array with values between –1.0 and +1.0. + * The file extension must be either {@code .wav}, {@code .au}, + * or {@code .aiff}. * * @param filename the name of the audio file * @return the array of samples */ public static double[] read(String filename) { - byte[] data = readByte(filename); - int n = data.length; - double[] d = new double[n/2]; - for (int i = 0; i < n/2; i++) { - d[i] = ((short) (((data[2*i+1] & 0xFF) << 8) + (data[2*i] & 0xFF))) / ((double) MAX_16_BIT); + // 4K buffer (must be a multiple of 2 for monaural or 4 for stereo) + int READ_BUFFER_SIZE = 4096; + + // create AudioInputStream from file + AudioInputStream fromAudioInputStream = getAudioInputStreamFromFile(filename); + AudioFormat fromAudioFormat = fromAudioInputStream.getFormat(); + + // normalize AudioInputStream to 44,100 Hz, 16-bit audio, monaural, signed PCM, little endian + // https://docs.oracle.com/javase/tutorial/sound/converters.html + AudioFormat toAudioFormat = new AudioFormat((float) SAMPLE_RATE, BITS_PER_SAMPLE, MONAURAL, SIGNED, LITTLE_ENDIAN); + if (!AudioSystem.isConversionSupported(toAudioFormat, fromAudioFormat)) { + throw new IllegalArgumentException("system cannot convert from " + fromAudioFormat + " to " + toAudioFormat); } - return d; - } + AudioInputStream toAudioInputStream = AudioSystem.getAudioInputStream(toAudioFormat, fromAudioInputStream); - // return data as a byte array - private static byte[] readByte(String filename) { - byte[] data = null; - AudioInputStream ais = null; + // extract the audio data and convert to a double[] with each sample between -1 and +1 try { + QueueOfDoubles queue = new QueueOfDoubles(); + byte[] bytes = new byte[READ_BUFFER_SIZE]; + int count; + while ((count = toAudioInputStream.read(bytes, 0, READ_BUFFER_SIZE)) != -1) { + + // little endian, monaural + for (int i = 0; i < count/2; i++) { + double sample = ((short) (((bytes[2*i+1] & 0xFF) << 8) | (bytes[2*i] & 0xFF))) / ((double) MAX_16_BIT); + queue.enqueue(sample); + } - // try to read from file - File file = new File(filename); - if (file.exists()) { - ais = AudioSystem.getAudioInputStream(file); - int bytesToRead = ais.available(); - data = new byte[bytesToRead]; - int bytesRead = ais.read(data); - if (bytesToRead != bytesRead) - throw new IllegalStateException("read only " + bytesRead + " of " + bytesToRead + " bytes"); - } - - // try to read from URL - else { - URL url = StdAudio.class.getResource(filename); - ais = AudioSystem.getAudioInputStream(url); - int bytesToRead = ais.available(); - data = new byte[bytesToRead]; - int bytesRead = ais.read(data); - if (bytesToRead != bytesRead) - throw new IllegalStateException("read only " + bytesRead + " of " + bytesToRead + " bytes"); + // little endian, stereo (perhaps, for a future version that supports stereo) + /* + for (int i = 0; i < count/4; i++) { + double left = ((short) (((bytes[4*i + 1] & 0xFF) << 8) | (bytes[4*i + 0] & 0xFF))) / ((double) MAX_16_BIT); + double right = ((short) (((bytes[4*i + 3] & 0xFF) << 8) | (bytes[4*i + 2] & 0xFF))) / ((double) MAX_16_BIT); + double sample = (left + right) / 2.0; + queue.enqueue(sample); + } + */ } + toAudioInputStream.close(); + fromAudioInputStream.close(); + return queue.toArray(); } - catch (IOException e) { - throw new IllegalArgumentException("could not read '" + filename + "'", e); - } - - catch (UnsupportedAudioFileException e) { - throw new IllegalArgumentException("unsupported audio format: '" + filename + "'", e); + catch (IOException ioe) { + throw new IllegalArgumentException("could not read audio file '" + filename + "'", ioe); } - - return data; } /** - * Saves the double array as an audio file (using .wav or .au format). + * Saves the audio samples as an audio file (using WAV, AU, or AIFF format). + * The file extension must be either {@code .wav}, {@code .au}, + * or {@code .aiff}. + * The format uses a sampling rate of 44,100 Hz, 16-bit audio, + * monaural, signed PCM, ands little Endian. * * @param filename the name of the audio file * @param samples the array of samples * @throws IllegalArgumentException if unable to save {@code filename} * @throws IllegalArgumentException if {@code samples} is {@code null} + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if {@code filename} is the empty string + * @throws IllegalArgumentException if {@code filename} extension is not + * {@code .wav}, {@code .au}, or {@code .aiff}. */ public static void save(String filename, double[] samples) { + if (filename == null) { + throw new IllegalArgumentException("filename is null"); + } if (samples == null) { throw new IllegalArgumentException("samples[] is null"); } + if (filename.length() == 0) { + throw new IllegalArgumentException("argument to save() is the empty string"); + } - // assumes 44,100 samples per second - // use 16-bit audio, mono, signed PCM, little Endian - AudioFormat format = new AudioFormat(SAMPLE_RATE, 16, 1, true, false); + // assumes 16-bit samples with sample rate = 44,100 Hz + // use 16-bit audio, monaural, signed PCM, little Endian + AudioFormat format = new AudioFormat(SAMPLE_RATE, 16, MONAURAL, SIGNED, LITTLE_ENDIAN); byte[] data = new byte[2 * samples.length]; for (int i = 0; i < samples.length; i++) { int temp = (short) (samples[i] * MAX_16_BIT); + if (samples[i] == 1.0) temp = Short.MAX_VALUE; // special case since 32768 not a short data[2*i + 0] = (byte) temp; - data[2*i + 1] = (byte) (temp >> 8); + data[2*i + 1] = (byte) (temp >> 8); // little endian } + // now save the file - try { - ByteArrayInputStream bais = new ByteArrayInputStream(data); - AudioInputStream ais = new AudioInputStream(bais, format, samples.length); + try (ByteArrayInputStream bais = new ByteArrayInputStream(data); + AudioInputStream ais = new AudioInputStream(bais, format, samples.length)) { + if (filename.endsWith(".wav") || filename.endsWith(".WAV")) { + if (!AudioSystem.isFileTypeSupported(AudioFileFormat.Type.WAVE, ais)) { + throw new IllegalArgumentException("saving to WAVE file format is not supported on this system"); + } AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File(filename)); } else if (filename.endsWith(".au") || filename.endsWith(".AU")) { + if (!AudioSystem.isFileTypeSupported(AudioFileFormat.Type.AU, ais)) { + throw new IllegalArgumentException("saving to AU file format is not supported on this system"); + } AudioSystem.write(ais, AudioFileFormat.Type.AU, new File(filename)); } + else if (filename.endsWith(".aif") || filename.endsWith(".aiff") || filename.endsWith(".AIF") || filename.endsWith(".AIFF")) { + if (!AudioSystem.isFileTypeSupported(AudioFileFormat.Type.AIFF, ais)) { + throw new IllegalArgumentException("saving to AIFF file format is not supported on this system"); + } + AudioSystem.write(ais, AudioFileFormat.Type.AIFF, new File(filename)); + } else { - throw new IllegalArgumentException("unsupported audio format: '" + filename + "'"); + throw new IllegalArgumentException("file extension for saving must be .wav, .au, or .aif"); } } catch (IOException ioe) { @@ -248,186 +555,231 @@ else if (filename.endsWith(".au") || filename.endsWith(".AU")) { } } - + /** + * Stops the playing of all audio files in background threads. + */ + public static synchronized void stopInBackground() { + for (BackgroundRunnable runnable : backgroundRunnables) { + runnable.stop(); + } + backgroundRunnables.clear(); + } /** - * Plays an audio file (in .wav, .mid, or .au format) in a background thread. + * Plays an audio file (in WAVE, AU, AIFF, or MIDI format) in its own + * background thread. Multiple audio files can be played simultaneously. + * The file extension must be either {@code .wav}, {@code .au}, + * or {@code .aiff}. * * @param filename the name of the audio file * @throws IllegalArgumentException if unable to play {@code filename} * @throws IllegalArgumentException if {@code filename} is {@code null} */ - public static synchronized void play(final String filename) { - if (filename == null) throw new IllegalArgumentException(); - - InputStream is = StdAudio.class.getResourceAsStream(filename); - if (is == null) { - throw new IllegalArgumentException("could not read '" + filename + "'"); - } - - // code adapted from: http://stackoverflow.com/questions/26305/how-can-i-play-sound-in-java - try { - // check if file format is supported - // (if not, will throw an UnsupportedAudioFileException) - AudioSystem.getAudioInputStream(is); - - new Thread(new Runnable() { - @Override - public void run() { - stream(filename); - } - }).start(); - } - - // let's try Applet.newAudioClip() instead - catch (UnsupportedAudioFileException e) { - playApplet(filename); - return; - } - - // something else went wrong - catch (IOException ioe) { - throw new IllegalArgumentException("could not play '" + filename + "'", ioe); - } - + public static synchronized void playInBackground(final String filename) { + BackgroundRunnable runnable = new BackgroundRunnable(filename); + new Thread(runnable).start(); + backgroundRunnables.add(runnable); } + private static class BackgroundRunnable implements Runnable { + private volatile boolean exit = false; + private final String filename; - // play sound file using Applet.newAudioClip(); - private static void playApplet(String filename) { - URL url = null; - try { - File file = new File(filename); - if (file.canRead()) url = file.toURI().toURL(); - } - catch (MalformedURLException e) { - throw new IllegalArgumentException("could not play '" + filename + "'", e); - } - - // URL url = StdAudio.class.getResource(filename); - if (url == null) { - throw new IllegalArgumentException("could not play '" + filename + "'"); + public BackgroundRunnable(String filename) { + this.filename = filename; } - AudioClip clip = Applet.newAudioClip(url); - clip.play(); - } - - // https://www3.ntu.edu.sg/home/ehchua/programming/java/J8c_PlayingSound.html - // play a wav or aif file - // javax.sound.sampled.Clip fails for long clips (on some systems) - private static void stream(String filename) { - SourceDataLine line = null; - int BUFFER_SIZE = 4096; // 4K buffer - - try { - InputStream is = StdAudio.class.getResourceAsStream(filename); - AudioInputStream ais = AudioSystem.getAudioInputStream(is); - AudioFormat audioFormat = ais.getFormat(); - DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); - line = (SourceDataLine) AudioSystem.getLine(info); - line.open(audioFormat); - line.start(); - byte[] samples = new byte[BUFFER_SIZE]; - int count = 0; - while ((count = ais.read(samples, 0, BUFFER_SIZE)) != -1) { - line.write(samples, 0, count); + // https://www3.ntu.edu.sg/home/ehchua/programming/java/J8c_PlayingSound.html + // play a wav or aif file + // javax.sound.sampled.Clip fails for long clips (on some systems) + public void run() { + AudioInputStream ais = getAudioInputStreamFromFile(filename); + + SourceDataLine line = null; + int BUFFER_SIZE = 4096; // 4K buffer + + try { + AudioFormat audioFormat = ais.getFormat(); + DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); + line = (SourceDataLine) AudioSystem.getLine(info); + line.open(audioFormat); + line.start(); + byte[] samples = new byte[BUFFER_SIZE]; + int count; + while (!exit && (count = ais.read(samples, 0, BUFFER_SIZE)) != -1) { + line.write(samples, 0, count); + } } - } - catch (IOException e) { - e.printStackTrace(); - } - catch (UnsupportedAudioFileException e) { - e.printStackTrace(); - } - catch (LineUnavailableException e) { - e.printStackTrace(); - } - finally { - if (line != null) { - line.drain(); - line.close(); + catch (IOException | LineUnavailableException e) { + System.out.println(e); + } + finally { + if (line != null) { + line.drain(); + line.close(); + } + backgroundRunnables.remove(this); } } + + public void stop() { + exit = true; + } } + /** - * Loops an audio file (in .wav, .mid, or .au format) in a background thread. + * Loops an audio file (in WAVE, AU, AIFF, or MIDI format) in its + * own background thread. * * @param filename the name of the audio file * @throws IllegalArgumentException if {@code filename} is {@code null} + * @deprecated to be removed in a future update, as it doesn't interact + * well with {@link #playInBackground(String filename)} or + * {@link #stopInBackground()}. */ - public static synchronized void loop(String filename) { + @Deprecated + public static synchronized void loopInBackground(String filename) { if (filename == null) throw new IllegalArgumentException(); - // code adapted from: http://stackoverflow.com/questions/26305/how-can-i-play-sound-in-java + final AudioInputStream ais = getAudioInputStreamFromFile(filename); + try { Clip clip = AudioSystem.getClip(); - InputStream is = StdAudio.class.getResourceAsStream(filename); - AudioInputStream ais = AudioSystem.getAudioInputStream(is); + // Clip clip = (Clip) AudioSystem.getLine(new Line.Info(Clip.class)); clip.open(ais); clip.loop(Clip.LOOP_CONTINUOUSLY); } - catch (UnsupportedAudioFileException e) { - throw new IllegalArgumentException("unsupported audio format: '" + filename + "'", e); + catch (IOException | LineUnavailableException e) { + System.out.println(e); } - catch (LineUnavailableException e) { - throw new IllegalArgumentException("could not play '" + filename + "'", e); + + // keep JVM open + new Thread(new Runnable() { + public void run() { + while (true) { + try { + Thread.sleep(1000); + } + catch (InterruptedException e) { + System.out.println(e); + } + } + } + }).start(); + } + + + /** + * Turns on audio recording. + */ + public static void startRecording() { + if (!isRecording) { + recordedSamples = new QueueOfDoubles(); + isRecording = true; + } + else { + throw new IllegalStateException("startRecording() must not be called twice in a row"); + } + } + + /** + * Turns off audio recording and returns the recorded samples. + * @return the array of recorded samples + */ + public static double[] stopRecording() { + if (isRecording) { + double[] results = recordedSamples.toArray(); + isRecording = false; + recordedSamples = null; + return results; } - catch (IOException e) { - throw new IllegalArgumentException("could not play '" + filename + "'", e); + else { + throw new IllegalStateException("stopRecording() must be called after calling startRecording()"); } } /*************************************************************************** - * Unit tests {@code StdAudio}. + * Helper class for reading and recording audio. ***************************************************************************/ + private static class QueueOfDoubles { + private static final int INIT_CAPACITY = 16; + private double[] a; // array of doubles + private int n; // number of items in queue + + // create an empty queue + public QueueOfDoubles() { + a = new double[INIT_CAPACITY]; + n = 0; + } + + // resize the underlying array holding the items + private void resize(int capacity) { + assert capacity >= n; + double[] temp = new double[capacity]; + for (int i = 0; i < n; i++) + temp[i] = a[i]; + a = temp; + } + + // enqueue item onto the queue + public void enqueue(double item) { + if (n == a.length) resize(2*a.length); // double length of array if necessary + a[n++] = item; // add item + } + + + // number of items in queue + public int size() { + return n; + } + + // return the items as an array of length n + public double[] toArray() { + double[] result = new double[n]; + for (int i = 0; i < n; i++) + result[i] = a[i]; + return result; + } - // create a note (sine wave) of the given frequency (Hz), for the given - // duration (seconds) scaled to the given volume (amplitude) - private static double[] note(double hz, double duration, double amplitude) { - int n = (int) (StdAudio.SAMPLE_RATE * duration); - double[] a = new double[n+1]; - for (int i = 0; i <= n; i++) - a[i] = amplitude * Math.sin(2 * Math.PI * i * hz / StdAudio.SAMPLE_RATE); - return a; } + /** - * Test client - play an A major scale to standard audio. - * - * @param args the command-line arguments - */ - /** - * Test client - play an A major scale to standard audio. + * Test client - plays some sound files and concert A. * - * @param args the command-line arguments + * @param args the command-line arguments (none should be specified) */ public static void main(String[] args) { - // 440 Hz for 1 sec double freq = 440.0; for (int i = 0; i <= StdAudio.SAMPLE_RATE; i++) { StdAudio.play(0.5 * Math.sin(2*Math.PI * freq * i / StdAudio.SAMPLE_RATE)); } - - // scale increments - int[] steps = { 0, 2, 4, 5, 7, 9, 11, 12 }; - for (int i = 0; i < steps.length; i++) { - double hz = 440.0 * Math.pow(2, steps[i] / 12.0); - StdAudio.play(note(hz, 1.0, 0.5)); - } + String base = "https://introcs.cs.princeton.edu/java/stdlib/"; + + // play some sound files + StdAudio.play(base + "test.wav"); // helicopter + StdAudio.play(base + "test-22050.wav"); // twenty-four + StdAudio.play(base + "test.midi"); // a Mozart measure + + // a sound loop + for (int i = 0; i < 10; i++) { + StdAudio.play(base + "BaseDrum.wav"); + StdAudio.play(base + "SnareDrum.wav"); + } + // need to call this in non-interactive stuff so the program doesn't terminate // until all the sound leaves the speaker. - StdAudio.close(); + StdAudio.drain(); } } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/StdAudioStereo.java b/src/main/java/edu/princeton/cs/algs4/StdAudioStereo.java new file mode 100644 index 000000000..32405a3cb --- /dev/null +++ b/src/main/java/edu/princeton/cs/algs4/StdAudioStereo.java @@ -0,0 +1,1068 @@ +/****************************************************************************** + * Compilation: javac StdAudioStereo.java + * Execution: java StdAudioStereo + * Dependencies: none + * + * Simple library for reading, writing, and manipulating audio. + * + * Supported formats: + * https://www.oracle.com/java/technologies/javase/jmf-211-formats.html + * + ******************************************************************************/ + +package edu.princeton.cs.algs4; + +import javax.sound.sampled.Clip; + +import java.io.File; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.IOException; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +import java.util.LinkedList; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.SourceDataLine; +import javax.sound.sampled.UnsupportedAudioFileException; + +/** + * The {@code StdAudioStereo} class provides static methods for playing, + * reading, and saving stereo audio. + * It uses a simple audio model that allows + * you to send one pair of samples (left channel and right channel) + * to the sound card at a time. Each sample is a + * real number between –1.0 and +1.0. The samples are played in real + * time using a sampling rate of 44,100 Hz. + * In addition to playing individual pairs of samples, standard audio supports + * reading, writing, and playing audio files in a number of standard formats. + *

+ * See {@link StdAudio} for a version that supports monaural + * audio (one channel only). + *

+ * Getting started. + * To use this class, you must have {@code StdAudioStereo} in your Java classpath. + * Here are three possible ways to do this: + *

    + *
  • If you ran our autoinstaller, use the commands + * {@code javac-introcs} and {@code java-introcs} (or {@code javac-algs4} + * and {@code java-algs4}) when compiling and executing. These commands + * add {@code stdlib.jar} (or {@code algs4.jar}) to the Java classpath, which + * provides access to {@code StdAudioStereo}. + *
  • Download stdlib.jar + * (or algs4.jar) + * and add it to the Java classpath. + *
  • Download StdAudioStereo.java + * and put it in the working directory. + *
+ *

+ * As a test, cut-and-paste the following short program into your editor: + *

+ *   public class TestStdAudioStereo {
+ *       public static void main(String[] args) {
+ *           double freq1 = 440.0;
+ *           double freq2 = 220.0;
+ *           for (int i = 0; i < StdAudioStereo.SAMPLE_RATE; i++) {
+ *               double sampleLeft  = 0.5 * Math.sin(2 * Math.PI * freq1 * i / StdAudioStereo.SAMPLE_RATE);
+ *               double sampleRight = 0.5 * Math.sin(2 * Math.PI * freq2 * i / StdAudioStereo.SAMPLE_RATE);
+ *               StdAudioStereo.play(sampleLeft, sampleRight);
+ *           }
+ *           StdAudioStereo.drain();
+ *       }
+ *   }
+ *  
+ *

+ * If you compile and execute the program, you should hear a pure tone + * whose frequency is concert A (440 Hz) in the left speaker + * and the pitch A3 (220 Hz) in the right speaker. + * + *

+ * Playing audio samples. + * You can use the following methods to play individual audio samples, + * either in monaural or stereo: + *

    + *
  • {@link #play(double sample)} + *
  • {@link #play(double[] samples)} + *
  • {@link #play(double sampleLeft, double sampleRight)} + *
  • {@link #play(double[] samplesLeft, double[] samplesRight)} + *
+ *

+ * Each method sends the specified sample (or samples) to the sound card. + * The individual samples are real numbers between –1.0 and +1.0. If a + * sample is outside this range, it will be clipped (rounded to + * –1.0 or +1.0). The samples are played in real time using a sampling + * rate of 44,100 Hz. + * + *

+ * Playing audio files. + * You can use the following method to play an audio file: + *

    + *
  • {@link #play(String filename)} + *
+ *

+ * It plays an audio file (in WAVE, AU, AIFF, or MIDI format) and does + * not return until the audio file is finished playing. This can produce + * particularly striking programs with minimal code. + * For example, the following code fragment plays a drum loop: + * + *

+ *   while (true) {
+ *       StdAudioStereo.play("BassDrum.wav");
+ *       StdAudioStereo.play("SnareDrum.wav");
+ *   }
+ *  
+ * + * The individual audio files + * (such as BassDrum.wav + * and SnareDrum.wav) + * must be accessible to Java, typically + * by being in the same directory as the {@code .class} file. + *

+ * + * Reading and writing audio files. + * You can read and write audio files using the following methods: + *

    + *
  • {@link #read(String filename)} + *
  • {@link #save(String filename, double[] samples)} + *
  • {@link #save(String filename, double[] samplesLeft, double[] samplesRight)} + *
+ *

+ * The first method reads audio samples from an audio file + * (in WAVE, AU, AIFF, or MIDI format) + * and returns them as a double array with values between –1.0 and +1.0. + * The second two method saves the audio samples in the specified double array to an + * audio file (in WAVE, AU, or AIFF format), either in monaural or stereo. + * + *

+ * Audio file formats. + * {@code StdAudioStereo} relies on the + * Java Media Framework + * for reading, writing, and playing audio files. You should be able to read or play files + * in WAVE, AU, AIFF, and MIDI formats and save them to WAVE, AU, and AIFF formats. + * The file extensions corresponding to WAVE, AU, AIFF, and MIDI files + * are {@code .wav}, {@code .au}, {@code .aiff}, and {@code .midi}, + * respectively. + * Some systems support additional audio file formats, but probably not MP3 or M4A. + *

+ * The Java Media Framework supports a variety of different audio data formats, + * which includes + *

    + *
  • the sampling rate (e.g., 44,100 Hz); + *
  • the number of bits per sample per channel (e.g., 8-bit or 16-bit); + *
  • the number of channels (e.g., monaural or stereo); + *
  • the byte ordering (e.g., little endian or big endian); and + *
  • the encoding scheme (typically linear PCM). + *
+ *

+ * When saving files, {@code StdAudioStereo} uses a sampling rate of 44,100 Hz, + * 16 bits per sample, stereo audio, little endian, and linear PCM encoding. + * When reading files, {@code StdAudioStereo} converts to a sammpling rate of 44,100 Hz, + * with 16 bits per sample. + * + *

+ * Recording audio. + * You can use the following methods to record audio samples that are + * played as a result of calls to + * {@link #play(double sample)}, + * {@link #play(double sampleLeft, double sampleRight)}, + * {@link #play(double[] samples)}, + * {@link #play(double[] samplesLeft, double[] samplesRight)}, + * or {@link #play(String filename)}: + *

    + *
  • {@link #startRecording()} + *
  • {@link #stopRecording()} + *
  • {@link #getRecordingMono()} + *
  • {@link #getRecordingLeft()} + *
  • {@link #getRecordingRight()} + *
+ *

+ * The method {@code startRecording()} begins recording audio. + * The method {@code stopRecording()} stops recording. + * After calling {@code stopRecording()}, you can access the + * recorded left and right channels using + * {@code getRecordingLeft()} and {@code getRecordingRight()}. + *

+ * {@code StdAudioStereo} does not currently support recording audio that calls + * {@code playInBackground()}. + *

+ * Playing audio files in a background thread. + * You can use the following methods to play an audio file in a background thread + * (e.g., as a background score in your program). + *

    + *
  • {@link #playInBackground(String filename)} + *
  • {@link #stopInBackground()} + *
+ *

+ * Each call to the first method plays the specified sound in a separate background + * thread. Unlike with the {@link play} methods, your program will not wait + * for the samples to finish playing before continuing. + * It supports playing an audio file in WAVE, AU, AIFF, or MIDI format. + * It is possible to play + * multiple audio files simultaneously (in separate background threads). + * The second method stops the playing of all audio in background threads. + *

+ * Draining standard audio. + * On some systems, your Java program may terminate before all of the samples have been + * sent to the sound card. To prevent this, it is recommend that you call the + * following method to indicate that you are done using standard audio: + *

    + *
  • {@link #drain()} + *
+ *

+ * The method drains any samples queued to the sound card that have not yet been + * sent to the sound card. + *

+ * Reference. + * For additional documentation, + * see Section 1.5 of + * Computer Science: An Interdisciplinary Approach + * by Robert Sedgewick and Kevin Wayne. + * + * @author Robert Sedgewick + * @author Kevin Wayne + */ +public final class StdAudioStereo { + + /** + * The sample rate: 44,100 Hz for CD quality audio. + */ + public static final int SAMPLE_RATE = 44100; + + private static final int BYTES_PER_SAMPLE = 2; // 16-bit audio + private static final int BITS_PER_SAMPLE = 16; // 16-bit audio + private static final int MAX_16_BIT = 32768; + private static final int BYTES_PER_FRAME = 4; // 2 bytes per sample * 2 channels + private static final int SAMPLE_BUFFER_SIZE = 4096; // should be a multiple of frame size + + private static final int MONAURAL = 1; // 1 channel + private static final int STEREO = 2; // 2 channels + + private static final boolean LITTLE_ENDIAN = false; + private static final boolean BIG_ENDIAN = true; + private static final boolean UNSIGNED = false; + private static final boolean SIGNED = true; + private static final boolean LEFT_CHANNEL = false; + private static final boolean RIGHT_CHANNEL = true; + + private static SourceDataLine line; // to play the sound + private static byte[] buffer; // our internal buffer + private static int bufferSize = 0; // number of samples currently in internal buffer + + // queue of background runnables + private static LinkedList backgroundRunnables = new LinkedList<>(); + + // for recording audio + private static QueueOfDoubles recordedSamplesLeft = null; + private static QueueOfDoubles recordedSamplesRight = null; + private static boolean isRecording = false; + + private StdAudioStereo() { + // can not instantiate + } + + // static initializer + static { + init(); + } + + // open up an audio stream + private static void init() { + try { + // 44,100 Hz, 16-bit audio, monaural, signed PCM, little endian + AudioFormat format = new AudioFormat((float) SAMPLE_RATE, BITS_PER_SAMPLE, STEREO, SIGNED, LITTLE_ENDIAN); + DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); + + line = (SourceDataLine) AudioSystem.getLine(info); + line.open(format, SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE); + + // the internal buffer is a fraction of the actual buffer size, this choice is arbitrary + // it gets divided because we can't expect the buffered data to line up exactly with when + // the sound card decides to push out its samples. The buffer length must be a multiple of 4. + buffer = new byte[SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE / 2]; + } + catch (LineUnavailableException e) { + System.out.println(e.getMessage()); + } + + // no sound gets made before this call + line.start(); + } + + // get an AudioInputStream object from a file + private static AudioInputStream getAudioInputStreamFromFile(String filename) { + if (filename == null) { + throw new IllegalArgumentException("filename is null"); + } + + try { + // first try to read file from local file system + File file = new File(filename); + if (file.exists()) { + return AudioSystem.getAudioInputStream(file); + } + + // resource relative to .class file + InputStream is1 = StdAudioStereo.class.getResourceAsStream(filename); + if (is1 != null) { + return AudioSystem.getAudioInputStream(is1); + } + + // resource relative to classloader root + InputStream is2 = StdAudioStereo.class.getClassLoader().getResourceAsStream(filename); + if (is2 != null) { + return AudioSystem.getAudioInputStream(is2); + } + + // from URL (including jar file) + URI uri = new URI(filename); + if (uri.isAbsolute()) { + URL url = uri.toURL(); + return AudioSystem.getAudioInputStream(url); + } + else throw new IllegalArgumentException("could not read audio file '" + filename + "'"); + } + catch (IOException | URISyntaxException e) { + throw new IllegalArgumentException("could not read audio file '" + filename + "'"); + } + catch (UnsupportedAudioFileException e) { + throw new IllegalArgumentException("file of unsupported audio file format: '" + filename + "'", e); + } + } + + /** + * Sends any queued samples to the sound card. + */ + public static void drain() { + if (bufferSize > 0) { + line.write(buffer, 0, bufferSize); + bufferSize = 0; + } + line.drain(); + } + + + /** + * Closes standard audio. + */ +/* + public static void close() { + drain(); + line.stop(); + } +*/ + + /** + * Writes one stereo sample (between –1.0 and +1.0) to standard audio. + * If the sample is outside the range, it will be clipped + * (rounded to –1.0 or +1.0). + * + * @param sampleLeft the left sample to play + * @param sampleRight the right sample to play + * @throws IllegalArgumentException if either + * {@code sampleLeft} or {@code sampleRight} is {@code Double.NaN} + */ + public static void play(double sampleLeft, double sampleRight) { + if (Double.isNaN(sampleLeft)) throw new IllegalArgumentException("sampleLeft is NaN"); + if (Double.isNaN(sampleRight)) throw new IllegalArgumentException("sampleRight is NaN"); + + // clip if outside [-1, +1] + if (sampleLeft < -1.0) sampleLeft = -1.0; + if (sampleLeft > +1.0) sampleLeft = +1.0; + if (sampleRight < -1.0) sampleRight = -1.0; + if (sampleRight > +1.0) sampleRight = +1.0; + + // save sample if recording + if (isRecording) { + recordedSamplesLeft.enqueue(sampleLeft); + recordedSamplesRight.enqueue(sampleRight); + } + + // convert left sample to bytes + short sLeft = (short) (MAX_16_BIT * sampleLeft); + if (sampleLeft == 1.0) sLeft = Short.MAX_VALUE; // special case since 32768 not a short + buffer[bufferSize++] = (byte) sLeft; + buffer[bufferSize++] = (byte) (sLeft >> 8); // little endian + + // convert right sample to bytes + short sRight = (short) (MAX_16_BIT * sampleRight); + if (sampleRight == 1.0) sRight = Short.MAX_VALUE; + buffer[bufferSize++] = (byte) sRight; + buffer[bufferSize++] = (byte) (sRight >> 8); // little endian + + // send to sound card if buffer is full + if (bufferSize >= buffer.length) { + line.write(buffer, 0, buffer.length); + bufferSize = 0; + } + } + + /** + * Writes one sample (between –1.0 and +1.0) to standard audio. + * If the sample is outside the range, it will be clipped + * (rounded to –1.0 or +1.0). + * + * @param sample the sample to play + * @throws IllegalArgumentException if {@code sample} is {@code Double.NaN} + */ + public static void play(double sample) { + play(sample, sample); + } + + /** + * Writes the array of samples (between –1.0 and +1.0) to standard audio. + * If a sample is outside the range, it will be clipped. + * + * @param samples the array of samples to play + * @throws IllegalArgumentException if any sample is {@code Double.NaN} + * @throws IllegalArgumentException if {@code samples} is {@code null} + */ + public static void play(double[] samples) { + if (samples == null) throw new IllegalArgumentException("argument to play() is null"); + for (int i = 0; i < samples.length; i++) { + play(samples[i]); + } + } + + /** + * Writes the array of stereo samples (between –1.0 and +1.0) to standard audio. + * If a sample is outside the range, it will be clipped. + * + * @param samplesLeft the array of samples for the left channel to play + * @param samplesRight the array of samples for the right channel to play + * @throws IllegalArgumentException if any sample is {@code Double.NaN} + * @throws IllegalArgumentException if either + * {@code samplesLeft} or {@code samplesRight} is {@code null} + * @throws IllegalArgumentException if {@code samplesLeft.length != samplesRight.length} + */ + public static void play(double[] samplesLeft, double[] samplesRight) { + if (samplesLeft == null) throw new IllegalArgumentException("argument to play() is null"); + if (samplesRight == null) throw new IllegalArgumentException("argument to play() is null"); + if (samplesLeft.length != samplesRight.length) throw new IllegalArgumentException("left and right arrays have different lengths"); + + for (int i = 0; i < samplesLeft.length; i++) { + play(samplesLeft[i], samplesRight[i]); + } + } + + /** + * Plays an audio file (in WAVE, AU, AIFF, or MIDI format) and waits for it to finish. + * + * @param filename the name of the audio file + * @throws IllegalArgumentException if unable to play {@code filename} + * @throws IllegalArgumentException if {@code filename} is {@code null} + */ + public static void play(String filename) { + + // may not work for streaming file formats + if (isRecording) { + double[] samplesLeft = readLeft(filename); + double[] samplesRight = readRight(filename); + for (double sample : samplesLeft) + recordedSamplesLeft.enqueue(sample); + for (double sample : samplesRight) + recordedSamplesRight.enqueue(sample); + } + + AudioInputStream ais = getAudioInputStreamFromFile(filename); + SourceDataLine line = null; + int BUFFER_SIZE = 4096; // 4K buffer + try { + AudioFormat audioFormat = ais.getFormat(); + DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); + line = (SourceDataLine) AudioSystem.getLine(info); + line.open(audioFormat); + line.start(); + byte[] samples = new byte[BUFFER_SIZE]; + int count = 0; + while ((count = ais.read(samples, 0, BUFFER_SIZE)) != -1) { + line.write(samples, 0, count); + } + } + catch (IOException | LineUnavailableException e) { + System.out.println(e); + } + finally { + if (line != null) { + line.drain(); + line.close(); + } + } + } + + + // helper method to read the left or right channel from the given audio file and return as a double[] + private static double[] readChannel(String filename, boolean channel) { + // 4K buffer (must be a multiple of 4 for stereo) + int READ_BUFFER_SIZE = 4096; + + // create AudioInputStream from file + AudioInputStream fromAudioInputStream = getAudioInputStreamFromFile(filename); + AudioFormat fromAudioFormat = fromAudioInputStream.getFormat(); + + // normalize AudioInputStream to 44,100 Hz, 16-bit audio, stereo, signed PCM, little endian + // https://docs.oracle.com/javase/tutorial/sound/converters.html + AudioFormat toAudioFormat = new AudioFormat((float) SAMPLE_RATE, BITS_PER_SAMPLE, STEREO, SIGNED, LITTLE_ENDIAN); + if (!AudioSystem.isConversionSupported(toAudioFormat, fromAudioFormat)) { + throw new IllegalArgumentException("system cannot convert from " + fromAudioFormat + " to " + toAudioFormat); + } + AudioInputStream toAudioInputStream = AudioSystem.getAudioInputStream(toAudioFormat, fromAudioInputStream); + + // extract the audio data and convert to a double[] with each sample between -1 and +1 + try { + QueueOfDoubles queue = new QueueOfDoubles(); + int count = 0; + byte[] bytes = new byte[READ_BUFFER_SIZE]; + while ((count = toAudioInputStream.read(bytes, 0, READ_BUFFER_SIZE)) != -1) { + // little endian, stereo (perhaps, for a future version that supports stereo) + for (int i = 0; i < count/4; i++) { + double left = ((short) (((bytes[4*i + 1] & 0xFF) << 8) | (bytes[4*i + 0] & 0xFF))) / ((double) MAX_16_BIT); + double right = ((short) (((bytes[4*i + 3] & 0xFF) << 8) | (bytes[4*i + 2] & 0xFF))) / ((double) MAX_16_BIT); + if (channel == LEFT_CHANNEL) queue.enqueue(left); + else queue.enqueue(right); + } + } + toAudioInputStream.close(); + fromAudioInputStream.close(); + return queue.toArray(); + } + catch (IOException ioe) { + throw new IllegalArgumentException("could not read audio file '" + filename + "'", ioe); + } + } + + /** + * Reads the audio samples (left channel) from an audio file + * (in WAVE, AU, AIFF, or MIDI format) + * and returns them as a double array with values between –1.0 and +1.0. + * The file can be any audio file supported by the Java Media Framework; + * if the audio data format is monaural, + * then {@link readLeft(String filename)} + * and {@link readRight(String filename)} + * will return the same sequence of values. + * + * @param filename the name of the audio file + * @return the array of samples + */ + public static double[] readLeft(String filename) { + return readChannel(filename, LEFT_CHANNEL); + } + + /** + * Reads the audio samples (right channel) from an audio file + * (in WAVE, AU, AIFF, or MIDI format) + * and returns them as a double array with values between –1.0 and +1.0. + * The file can be any audio file supported by the Java Media Framework; + * if the audio data format is monaural, + * then {@link readLeft(String filename)} + * and {@link readRight(String filename)} + * will return the same sequence of values. + * + * @param filename the name of the audio file + * @return the array of samples + */ + public static double[] readRight(String filename) { + return readChannel(filename, RIGHT_CHANNEL); + } + + + /** + * Reads the audio samples from a file (in WAVE, AU, AIFF, or MIDI format) + * and returns them as a double array with values between –1.0 and +1.0. + * The samples returned use 16-bit audio data, a sampling rate of 44,100, + * and monaural audio. + * The file can be any audio file supported by the Java Media Framework; + * if the audio data format is stereo, it is first converted + * to monaural. + * + * @param filename the name of the audio file + * @return the array of samples + */ + public static double[] read(String filename) { + // 4K buffer (must be a multiple of 2 for monaural) + int READ_BUFFER_SIZE = 4096; + + // create AudioInputStream from file + AudioInputStream fromAudioInputStream = getAudioInputStreamFromFile(filename); + AudioFormat fromAudioFormat = fromAudioInputStream.getFormat(); + + // normalize AudioInputStream to 44,100 Hz, 16-bit audio, monaural, signed PCM, little endian + // https://docs.oracle.com/javase/tutorial/sound/converters.html + AudioFormat toAudioFormat = new AudioFormat((float) SAMPLE_RATE, BITS_PER_SAMPLE, MONAURAL, SIGNED, LITTLE_ENDIAN); + if (!AudioSystem.isConversionSupported(toAudioFormat, fromAudioFormat)) { + throw new IllegalArgumentException("system cannot convert from " + fromAudioFormat + " to " + toAudioFormat); + } + AudioInputStream toAudioInputStream = AudioSystem.getAudioInputStream(toAudioFormat, fromAudioInputStream); + + // extract the audio data and convert to a double[] with each sample between -1 and +1 + try { + QueueOfDoubles queue = new QueueOfDoubles(); + int count = 0; + byte[] bytes = new byte[READ_BUFFER_SIZE]; + while ((count = toAudioInputStream.read(bytes, 0, READ_BUFFER_SIZE)) != -1) { + + // little endian, monaural + for (int i = 0; i < count/2; i++) { + double sample = ((short) (((bytes[2*i+1] & 0xFF) << 8) | (bytes[2*i] & 0xFF))) / ((double) MAX_16_BIT); + queue.enqueue(sample); + } + } + toAudioInputStream.close(); + fromAudioInputStream.close(); + return queue.toArray(); + } + catch (IOException ioe) { + throw new IllegalArgumentException("could not read audio file '" + filename + "'", ioe); + } + } + + + // helper method to save a file in specifed format + private static void save(String filename, byte[] data, AudioFormat format, long numberOfFrames) { + try (ByteArrayInputStream bais = new ByteArrayInputStream(data); + AudioInputStream ais = new AudioInputStream(bais, format, numberOfFrames)) { + + if (filename.endsWith(".wav") || filename.endsWith(".WAV")) { + if (!AudioSystem.isFileTypeSupported(AudioFileFormat.Type.WAVE, ais)) { + throw new IllegalArgumentException("saving to WAVE file format is not supported on this system"); + } + AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File(filename)); + } + else if (filename.endsWith(".au") || filename.endsWith(".AU")) { + if (!AudioSystem.isFileTypeSupported(AudioFileFormat.Type.AU, ais)) { + throw new IllegalArgumentException("saving to AU file format is not supported on this system"); + } + AudioSystem.write(ais, AudioFileFormat.Type.AU, new File(filename)); + } + else if (filename.endsWith(".aif") || filename.endsWith(".aiff") || filename.endsWith(".AIF") || filename.endsWith(".AIFF")) { + if (!AudioSystem.isFileTypeSupported(AudioFileFormat.Type.AIFF, ais)) { + throw new IllegalArgumentException("saving to AIFF file format is not supported on this system"); + } + AudioSystem.write(ais, AudioFileFormat.Type.AIFF, new File(filename)); + } + else { + throw new IllegalArgumentException("file extension for saving must be .wav, .au, or .aif"); + } + } + catch (IOException ioe) { + throw new IllegalArgumentException("unable to save file '" + filename + "'", ioe); + } + } + + /** + * Saves the audio samples as an monaural audio file + * (in WAV, AU, or AIFF format). + * The file extension type must be either {@code .wav}, {@code .au}, + * or {@code .aiff}. + * The audio data format uses a sampling rate of 44,100 Hz, 16-bit audio, + * monaural, signed PCM, ands little endian. + * + * @param filename the name of the audio file + * @param samples the array of samples to save + * @throws IllegalArgumentException if unable to save {@code filename} + * @throws IllegalArgumentException if {@code samples} is {@code null} + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if {@code filename} extension is not + * {@code .wav}, {@code .au}, or {@code .aiff}. + */ + public static void save(String filename, double[] samples) { + if (filename == null) { + throw new IllegalArgumentException("filename is null"); + } + if (samples == null) { + throw new IllegalArgumentException("samples[] is null"); + } + if (filename.length() == 0) { + throw new IllegalArgumentException("argument to save() is the empty string"); + } + + // assumes 16-bit samples with sample rate = 44,100 Hz + // use 16-bit audio, monaural, signed PCM, little Endian + AudioFormat format = new AudioFormat(SAMPLE_RATE, 16, MONAURAL, SIGNED, LITTLE_ENDIAN); + byte[] data = new byte[2 * samples.length]; + for (int i = 0; i < samples.length; i++) { + int temp = (short) (samples[i] * MAX_16_BIT); + if (samples[i] == 1.0) temp = Short.MAX_VALUE; // special case since 32768 not a short + data[2*i + 0] = (byte) temp; + data[2*i + 1] = (byte) (temp >> 8); // little endian + } + + // now save the file + save(filename, data, format, samples.length); + } + + + /** + * Saves the stereo samples as a stereo audio file + * (in WAV, AU, or AIFF format). + * The file extension type must be either {@code .wav}, {@code .au}, + * or {@code .aiff}. + * The audio data format uses a sampling rate of 44,100 Hz, 16-bit audio, + * stereo, signed PCM, ands little endian. + * + * @param filename the name of the audio file + * @param samplesLeft the array of samples in left channel to save + * @param samplesRight the array of samples in right channel to save + * @throws IllegalArgumentException if unable to save {@code filename} + * @throws IllegalArgumentException if {@code samplesLeft} is {@code null} + * @throws IllegalArgumentException if {@code samplesRight} is {@code null} + * @throws IllegalArgumentException if {@code samplesLeft.length != samplesRight.length} + * @throws IllegalArgumentException if {@code filename} extension is not + * {@code .wav}, {@code .au}, or {@code .aiff}. + */ + public static void save(String filename, double[] samplesLeft, double[] samplesRight) { + if (filename == null) { + throw new IllegalArgumentException("filename is null"); + } + if (samplesLeft == null) { + throw new IllegalArgumentException("samplesLeft[] is null"); + } + if (samplesRight == null) { + throw new IllegalArgumentException("samplesRight[] is null"); + } + if (samplesLeft.length != samplesRight.length) { + throw new IllegalArgumentException("input arrays have different lengths"); + } + // assumes 16-bit samples with sample rate = 44,100 Hz + // use 16-bit audio, monaural, signed PCM, little Endian + AudioFormat format = new AudioFormat(SAMPLE_RATE, 16, STEREO, SIGNED, LITTLE_ENDIAN); + byte[] data = new byte[4 * samplesLeft.length]; + for (int i = 0; i < samplesLeft.length; i++) { + int tempLeft = (short) (samplesLeft[i] * MAX_16_BIT); + int tempRight = (short) (samplesRight[i] * MAX_16_BIT); + if (samplesLeft[i] == 1.0) tempLeft = Short.MAX_VALUE; // special case since 32768 not a short + if (samplesRight[i] == 1.0) tempRight = Short.MAX_VALUE; + data[4*i + 0] = (byte) tempLeft; + data[4*i + 1] = (byte) (tempLeft >> 8); // little endian + data[4*i + 2] = (byte) tempRight; + data[4*i + 3] = (byte) (tempRight >> 8); + } + + // now save the file + save(filename, data, format, samplesLeft.length); + } + + /** + * Stops the playing of all audio files in background threads. + */ + public static synchronized void stopInBackground() { + for (BackgroundRunnable runnable : backgroundRunnables) { + runnable.stop(); + } + backgroundRunnables.clear(); + } + + /** + * Plays an audio file (in WAVE, AU, AIFF, or MIDI format) in its own + * background thread. Multiple audio files can be played simultaneously. + * + * @param filename the name of the audio file + * @throws IllegalArgumentException if unable to play {@code filename} + * @throws IllegalArgumentException if {@code filename} is {@code null} + */ + public static synchronized void playInBackground(final String filename) { + BackgroundRunnable runnable = new BackgroundRunnable(filename); + new Thread(runnable).start(); + backgroundRunnables.add(runnable); + } + + private static class BackgroundRunnable implements Runnable { + private volatile boolean exit = false; + private final String filename; + + public BackgroundRunnable(String filename) { + this.filename = filename; + } + + // https://www3.ntu.edu.sg/home/ehchua/programming/java/J8c_PlayingSound.html + // play a wav or aif file + // javax.sound.sampled.Clip fails for long clips (on some systems) + public void run() { + AudioInputStream ais = getAudioInputStreamFromFile(filename); + + SourceDataLine line = null; + int BUFFER_SIZE = 4096; // 4K buffer + + try { + AudioFormat audioFormat = ais.getFormat(); + DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); + line = (SourceDataLine) AudioSystem.getLine(info); + line.open(audioFormat); + line.start(); + byte[] samples = new byte[BUFFER_SIZE]; + int count = 0; + while (!exit && (count = ais.read(samples, 0, BUFFER_SIZE)) != -1) { + line.write(samples, 0, count); + } + } + catch (IOException | LineUnavailableException e) { + System.out.println(e); + } + finally { + if (line != null) { + line.drain(); + line.close(); + } + backgroundRunnables.remove(this); + } + } + + public void stop() { + exit = true; + } + } + + + /** + * Loops an audio file (in WAVE, AU, AIFF, or MIDI format) in its + * own background thread. + * + * @param filename the name of the audio file + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @deprecated to be removed in a future update, as it doesn't interact + * well with {@link #playInBackground(String filename)} or + * {@link #stopInBackground()}. + */ + @Deprecated + public static synchronized void loopInBackground(String filename) { + if (filename == null) throw new IllegalArgumentException(); + + final AudioInputStream ais = getAudioInputStreamFromFile(filename); + + try { + Clip clip = AudioSystem.getClip(); + // Clip clip = (Clip) AudioSystem.getLine(new Line.Info(Clip.class)); + clip.open(ais); + clip.loop(Clip.LOOP_CONTINUOUSLY); + } + catch (IOException | LineUnavailableException e) { + System.out.println(e); + } + + // keep JVM open + new Thread(new Runnable() { + public void run() { + while (true) { + try { + Thread.sleep(1000); + } + catch (InterruptedException e) { + System.out.println(e); + } + } + } + }).start(); + } + + + /** + * Starts recording audio samples. + * This includes all calls to {@code play()} that take samples as arguments, + * but not calls to {@code play()} that play audio files. + * + * @throws IllegalArgumentException if recording mode is already on + */ + public static void startRecording() { + if (!isRecording) { + recordedSamplesLeft = new QueueOfDoubles(); + recordedSamplesRight = new QueueOfDoubles(); + isRecording = true; + } + else { + throw new IllegalStateException("startRecording() must not be called twice in a row"); + } + } + + /** + * Stops recording audio samples. + * + * @throws IllegalArgumentException if recording mode is not currently on + */ + public static void stopRecording() { + if (isRecording) { + isRecording = false; + } + else { + throw new IllegalStateException("stopRecording() must be called after calling startRecording()"); + } + } + + /** + * Returns the array of samples that was recorded in the left channel in + * between the last calls to {@link startRecording()} and {@link stopRecording()}. + * + * @return the array of samples + * @throws IllegalArgumentException unless {@code startRecording} and + * {@code stopRecording} were previously called in the appropriate order + */ + public static double[] getRecordingLeft() { + if (recordedSamplesRight != null) { + throw new IllegalStateException("getRecordingLeft() must be called after startRecording()"); + } + + if (isRecording) { + throw new IllegalStateException("getRecordingLeft() must be called after stopRecording()"); + } + + double[] results = recordedSamplesLeft.toArray(); + return results; + } + + /** + * Returns the array of samples that was recorded in the right channel in + * between the last calls to {@link startRecording()} and {@link stopRecording()}. + * + * @return the array of samples + * @throws IllegalArgumentException unless {@code startRecording} and + * {@code stopRecording} were previously called in the appropriate order + */ + public static double[] getRecordingRight() { + if (recordedSamplesRight != null) { + throw new IllegalStateException("getRecordingRight() must be called after startRecording()"); + } + + if (isRecording) { + throw new IllegalStateException("getRecordingRight() must be called after stopRecording()"); + } + + double[] result = recordedSamplesRight.toArray(); + return result; + } + + /** + * Returns the array of samples that was recorded in the right channel in + * between the last calls to {@link startRecording()} and {@link stopRecording()}, + * by taking the average of the samples in the left and right channels. + * + * @return the array of samples + * @throws IllegalArgumentException unless {@code startRecording} and + * {@code stopRecording} were previously called in the appropriate order + */ + public static double[] getRecordingMono() { + if (recordedSamplesRight != null) { + throw new IllegalStateException("getRecording() must be called after startRecording()"); + } + + if (isRecording) { + throw new IllegalStateException("getRecording() must be called after stopRecording()"); + } + + double[] left = getRecordingLeft(); + double[] right = getRecordingRight(); + int n = left.length; + double[] sum = new double[n]; + for (int i = 0; i < left.length; i++) { + sum[i] = 0.5 * (left[i] + right[i]); + } + return sum; + } + + /*************************************************************************** + * Helper class for reading and recording audio. + ***************************************************************************/ + private static class QueueOfDoubles { + private final static int INIT_CAPACITY = 16; + private double[] a; // array of doubles + private int n; // number of items in queue + + // create an empty queue + public QueueOfDoubles() { + a = new double[INIT_CAPACITY]; + n = 0; + } + + // resize the underlying array holding the items + private void resize(int capacity) { + assert capacity >= n; + double[] temp = new double[capacity]; + for (int i = 0; i < n; i++) + temp[i] = a[i]; + a = temp; + } + + // enqueue item onto the queue + public void enqueue(double item) { + if (n == a.length) resize(2*a.length); // double length of array if necessary + a[n++] = item; // add item + } + + + // number of items in queue + public int size() { + return n; + } + + // return the items as an array of length n + public double[] toArray() { + double[] result = new double[n]; + for (int i = 0; i < n; i++) + result[i] = a[i]; + return result; + } + + } + + + /** + * Test client - plays some sound files and concert A. + * + * @param args the command-line arguments (none should be specified) + */ + public static void main(String[] args) { + // 440 Hz (left speaker) and 220Hz (right speaker) for 1 second + double freq1 = 440.0; + double freq2 = 220.0; + for (int i = 0; i <= StdAudioStereo.SAMPLE_RATE; i++) { + double sampleLeft = 0.5 * Math.sin(2*Math.PI * freq1 * i / StdAudioStereo.SAMPLE_RATE); + double sampleRight = 0.5 * Math.sin(2*Math.PI * freq2 * i / StdAudioStereo.SAMPLE_RATE); + StdAudioStereo.play(sampleLeft, sampleRight); + } + + + // play some sound files + String base = "https://introcs.cs.princeton.edu/java/stdlib/"; + StdAudioStereo.play(base + "test.wav"); // helicopter + StdAudioStereo.play(StdAudioStereo.readLeft(base + "test.midi"), StdAudioStereo.readRight(base + "test.midi")); + + // a sound loop + for (int i = 0; i < 10; i++) { + StdAudioStereo.play(base + "BassDrum.wav"); + StdAudioStereo.play(base + "SnareDrum.wav"); + } + + // need to call this in non-interactive stuff so the program doesn't terminate + // until all the sound leaves the speaker. + StdAudioStereo.drain(); + } +} + +/****************************************************************************** + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. + * + * This file is part of algs4.jar, which accompanies the textbook + * + * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, + * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. + * http://algs4.cs.princeton.edu + * + * + * algs4.jar is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * algs4.jar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with algs4.jar. If not, see http://www.gnu.org/licenses. + ******************************************************************************/ diff --git a/src/main/java/edu/princeton/cs/algs4/StdDraw.java b/src/main/java/edu/princeton/cs/algs4/StdDraw.java index 7f65c805a..2c7156a18 100644 --- a/src/main/java/edu/princeton/cs/algs4/StdDraw.java +++ b/src/main/java/edu/princeton/cs/algs4/StdDraw.java @@ -5,13 +5,16 @@ * * Standard drawing library. This class provides a basic capability for * creating drawings with your programs. It uses a simple graphics model that - * allows you to create drawings consisting of points, lines, and curves - * in a window on your computer and to save the drawings to a file. + * allows you to create drawings consisting of geometric shapes (e.g., + * points, lines, circles, rectangles) in a window on your computer + * and to save the drawings to a file. * * Todo * ---- + * - Don't show window until first unbuffered drawing command or call to show() + * (with setVisible not set to false). * - Add support for gradient fill, etc. - * - Fix setCanvasSize() so that it can only be called once. + * - Fix setCanvasSize() so that it can be called only once. * - On some systems, drawing a line (or other shape) that extends way * beyond canvas (e.g., to infinity) dimensions does not get drawn. * @@ -26,9 +29,11 @@ import java.awt.BasicStroke; import java.awt.Color; +import java.awt.Component; import java.awt.FileDialog; import java.awt.Font; import java.awt.FontMetrics; +import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.MediaTracker; @@ -50,14 +55,14 @@ import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; -import java.awt.image.DirectColorModel; -import java.awt.image.WritableRaster; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.util.LinkedList; import java.util.TreeSet; @@ -73,22 +78,24 @@ import javax.swing.KeyStroke; /** - * The {@code StdDraw} class provides a basic capability for - * creating drawings with your programs. It uses a simple graphics model that - * allows you to create drawings consisting of points, lines, squares, + * The {@code StdDraw} class provides static methods for creating drawings + * with your programs. It uses a simple graphics model that + * allows you to create drawings consisting of points, lines, squares, * circles, and other geometric shapes in a window on your computer and * to save the drawings to a file. Standard drawing also includes * facilities for text, color, pictures, and animation, along with * user interaction via the keyboard and mouse. *

* Getting started. - * To use standard drawing, you must have {@code StdDraw.class} in your + * To use this class, you must have {@code StdDraw.class} in your * Java classpath. If you used our autoinstaller, you should be all set. - * Otherwise, download + * Otherwise, either download + * stdlib.jar + * and add to your Java classpath or download * StdDraw.java * and put a copy in your working directory. *

- * Now, type the following short program into your editor: + * Now, cut-and-paste the following short program into your editor: *

  *   public class TestStdDraw {
  *       public static void main(String[] args) {
@@ -125,7 +132,7 @@
  *  
    *
  • {@link #circle(double x, double y, double radius)} *
  • {@link #ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis)} - *
  • {@link #square(double x, double y, double radius)} + *
  • {@link #square(double x, double y, double halfLength)} *
  • {@link #rectangle(double x, double y, double halfWidth, double halfHeight)} *
*

@@ -179,18 +186,18 @@ * The pen is circular, so that when you set the pen radius to r * and draw a point, you get a circle of radius r. Also, lines are * of thickness 2r and have rounded ends. The default pen radius - * is 0.005 and is not affected by coordinate scaling. This default pen - * radius is about 1/200 the width of the default canvas, so that if - * you draw 100 points equally spaced along a horizontal or vertical line, - * you will be able to see individual circles, but if you draw 200 such + * is 0.002 and is not affected by coordinate scaling. This default pen + * radius is about 1/500 the width of the default canvas, so that if + * you draw 200 points equally spaced along a horizontal or vertical line, + * you will be able to see individual circles, but if you draw 250 such * points, the result will look like a line. *

    *
  • {@link #setPenRadius(double radius)} *
*

- * For example, {@code StdDraw.setPenRadius(0.025)} makes + * For example, {@code StdDraw.setPenRadius(0.01)} makes * the thickness of the lines and the size of the points to be five times - * the 0.005 default. + * the 0.002 default. * To draw points with the minimum possible radius (one pixel on typical * displays), set the pen radius to 0.0. *

@@ -206,16 +213,23 @@ * The first method allows you to specify colors using the RGB color system. * This color picker * is a convenient way to find a desired color. + *

* The second method allows you to specify colors using the - * {@link Color} data type that is discussed in Chapter 3. Until then, - * you can use this method with one of these predefined colors in standard drawing: - * {@link #BLACK}, {@link #BLUE}, {@link #CYAN}, {@link #DARK_GRAY}, {@link #GRAY}, - * {@link #GREEN}, {@link #LIGHT_GRAY}, {@link #MAGENTA}, {@link #ORANGE}, - * {@link #PINK}, {@link #RED}, {@link #WHITE}, {@link #YELLOW}, - * {@link #BOOK_BLUE}, {@link #BOOK_LIGHT_BLUE}, {@link #BOOK_RED}, and - * {@link #PRINCETON_ORANGE}. - * For example, {@code StdDraw.setPenColor(StdDraw.MAGENTA)} sets the - * pen color to magenta. + * {@link Color} data type, which is defined in Java's {@link java.awt} package. + * Standard drawing defines a number of predefined colors including + * {@link #BLACK}, {@link #WHITE}, {@link #RED}, {@link #GREEN}, + * and {@link #BLUE}. + * For example, {@code StdDraw.setPenColor(StdDraw.RED)} sets the + * pen color to red. + *

+ * Window title. + * By default, the standard drawing window title is "Standard Draw". + * You can change the title with the following method: + *

    + *
  • {@link #setTitle(String windowTitle)} + *
+ *

+ * This sets the standard drawing window title to the specified string. *

* Canvas size. * By default, all drawing takes places in a 512-by-512 canvas. @@ -226,9 +240,8 @@ * *

* This sets the canvas size to be width-by-height pixels. - * It also erases the current drawing and resets the coordinate system, - * pen radius, pen color, and font back to their default values. - * Ordinarly, this method is called once, at the very beginning of a program. + * It also clears the current drawing using the default background color (white). + * Ordinarily, this method is called only once, at the very beginning of a program. * For example, {@code StdDraw.setCanvasSize(800, 800)} * sets the canvas size to be 800-by-800 pixels. *

@@ -242,7 +255,7 @@ *

  • {@link #setScale(double min, double max)} * *

    - * The arguments are the coordinates of the minimum and maximum + * The arguments are the coordinates of the minimum and maximum * x- or y-coordinates that will appear in the canvas. * For example, if you wish to use the default coordinate system but * leave a small margin, you can call {@code StdDraw.setScale(-.05, 1.05)}. @@ -251,7 +264,7 @@ * commands; they do not affect previous drawings. * These methods do not change the canvas size; so, if the x- * and y-scales are different, squares will become rectangles - * and circles will become ellipsoidal. + * and circles will become ellipses. *

    * Text. * You can use the following methods to annotate your drawings with text: @@ -273,10 +286,16 @@ *

  • {@link #setFont(Font font)} * *

    - * You use the {@link Font} data type to specify the font. This allows you to + * To specify the font, you use the {@link Font} data type, + * which is defined in Java's {@link java.awt} package. + * This allows you to * choose the face, size, and style of the font. For example, the following * code fragment sets the font to Arial Bold, 60 point. + * The import statement allows you to refer to Font + * directly, without needing the fully qualified name java.awt.Font. *

    + *   import java.awt.Font;
    + *   ...
      *   Font font = new Font("Arial", Font.BOLD, 60);
      *   StdDraw.setFont(font);
      *   StdDraw.text(0.5, 0.5, "Hello, World");
    @@ -292,22 +311,33 @@
      *  
      *  

    * These methods draw the specified image, centered at (x, y). - * The supported image formats are JPEG, PNG, and GIF. + * The image must be in a supported file format (typically JPEG, PNG, GIF, TIFF, and BMP). * The image will display at its native size, independent of the coordinate system. * Optionally, you can rotate the image a specified number of degrees counterclockwise - * or rescale it to fit snugly inside a width-by-height bounding box. + * or rescale it to fit snugly inside a bounding box. *

    * Saving to a file. - * You save your image to a file using the File → Save menu option. - * You can also save a file programatically using the following method: + * You can save your image to a file using the File → Save menu option. + * You can also save a file programmatically using the following method: *

      *
    • {@link #save(String filename)} *
    *

    - * The supported image formats are JPEG and PNG. The filename must have either the - * extension .jpg or .png. - * We recommend using PNG for drawing that consist solely of geometric shapes and JPEG - * for drawings that contains pictures. + * You can save the drawing to a file in a supported file format + * (typically JPEG, PNG, GIF, TIFF, and BMP). + * + *

    File formats. + * The {@code StdDraw} class supports reading and writing images to any of the + * file formats supported by {@link javax.imageio} (typically JPEG, PNG, + * GIF, TIFF, and BMP). + * The file extensions corresponding to JPEG, PNG, GIF, TIFF, and BMP, + * are {@code .jpg}, {@code .png}, {@code .gif}, {@code .tif}, + * and {@code .bmp}, respectively. + *

    + * We recommend using PNG for drawing that consist solely of geometric shapes + * and JPEG for drawings that contains pictures. + * The JPEG file format does not support transparent backgrounds. + * *

    * Clearing the canvas. * To clear the entire drawing canvas, you can use the following methods: @@ -316,10 +346,12 @@ *

  • {@link #clear(Color color)} * *

    - * The first method clears the canvas to white; the second method - * allows you to specify a color of your choice. For example, + * The first method clears the canvas to the default background color (white); + * the second method allows you to specify the background color. For example, * {@code StdDraw.clear(StdDraw.LIGHT_GRAY)} clears the canvas to a shade - * of gray. + * of gray. To make the background transparent, + * call {@code StdDraw.clear(StdDraw.TRANSPARENT)}. + * *

    * Computer animations and double buffering. * Double buffering is one of the most powerful features of standard drawing, @@ -341,7 +373,7 @@ * all drawing takes place on the offscreen canvas. The offscreen canvas * is not displayed. Only when you call * {@link #show()} does your drawing get copied from the offscreen canvas to - * the onscreen canvas, where it is displayed in the standard drawing window. You + * the onscreen canvas, where it is displayed in the standard drawing window. You * can think of double buffering as collecting all of the lines, points, shapes, * and text that you tell it to draw, and then drawing them all * simultaneously, upon request. @@ -362,19 +394,20 @@ *

    * For example, this code fragment animates two balls moving in a circle. *

    - *   StdDraw.setScale(-2, +2);
    + *   StdDraw.setScale(-2.0, +2.0);
      *   StdDraw.enableDoubleBuffering();
      *
      *   for (double t = 0.0; true; t += 0.02) {
      *       double x = Math.sin(t);
      *       double y = Math.cos(t);
      *       StdDraw.clear();
    - *       StdDraw.filledCircle(x, y, 0.05);
    - *       StdDraw.filledCircle(-x, -y, 0.05);
    + *       StdDraw.filledCircle(x, y, 0.1);
    + *       StdDraw.filledCircle(-x, -y, 0.1);
      *       StdDraw.show();
      *       StdDraw.pause(20);
      *   }
      *  
    + * Without double buffering, the balls would flicker as they move. *

    * Keyboard and mouse inputs. * Standard drawing has very basic support for keyboard and mouse input. @@ -410,30 +443,29 @@ * and font: *

      *
    • {@link #getPenColor()} + *
    • {@link #getBackgroundColor()} *
    • {@link #getPenRadius()} *
    • {@link #getFont()} *
    *

    * These methods are useful when you want to temporarily change a - * control parameter and reset it back to its original value. + * control parameter and, later, reset it back to its original value. *

    * Corner cases. - * To avoid clutter, the API doesn't explicitly refer to arguments that are - * null, infinity, or NaN. + * Here are some corner cases. *

      - *
    • Any method that is passed a {@code null} argument will throw an - * {@link IllegalArgumentException}. - *
    • Except as noted in the APIs, drawing an object outside (or partly outside) - * the canvas is permitted—however, only the part of the object that - * appears inside the canvas will be visible. - *
    • Except as noted in the APIs, all methods accept {@link Double#NaN}, - * {@link Double#POSITIVE_INFINITY}, and {@link Double#NEGATIVE_INFINITY} - * as arugments. An object drawn with an x- or y-coordinate - * that is NaN will behave as if it is outside the canvas, and will not be visible. + *
    • Drawing an object outside (or partly outside) the canvas is permitted. + * However, only the part of the object that appears inside the canvas + * will be visible. *
    • Due to floating-point issues, an object drawn with an x- or * y-coordinate that is way outside the canvas (such as the line segment - * from (0.5, –∞) to (0.5, ∞) may not be visible even in the + * from (0.5, –10^308) to (0.5, 10^308) may not be visible even in the * part of the canvas where it should be. + *
    • Any method that is passed a {@code null} argument will throw an + * {@link IllegalArgumentException}. + *
    • Any method that is passed a {@link Double#NaN}, + * {@link Double#POSITIVE_INFINITY}, or {@link Double#NEGATIVE_INFINITY} + * argument will throw an {@link IllegalArgumentException}. *
    *

    * Performance tricks. @@ -459,9 +491,6 @@ * inside the canvas if the center point (x, y) is outside the * canvas. * This bug appears only on some systems. - *

  • Some methods may not draw the portion of the geometric object that is inside the - * canvas if the x- or y-coordinates are infinite. - * This bug appears only on some systems. * *

    * Reference. @@ -476,100 +505,159 @@ public final class StdDraw implements ActionListener, MouseListener, MouseMotionListener, KeyListener { /** - * The color black. + * The color aqua (0, 255, 255). + */ + public static final Color AQUA = new Color(0, 255, 255); + + /** + * The color black (0, 0, 0). */ public static final Color BLACK = Color.BLACK; /** - * The color blue. + * The color blue (0, 0, 255). */ public static final Color BLUE = Color.BLUE; /** - * The color cyan. + * The color cyan (0, 255, 255). */ public static final Color CYAN = Color.CYAN; /** - * The color dark gray. + * The color fuscia (255, 0, 255). + */ + public static final Color FUSCIA = new Color(255, 0, 255); + + /** + * The color dark gray (64, 64, 64). */ public static final Color DARK_GRAY = Color.DARK_GRAY; /** - * The color gray. + * The color gray (128, 128, 128). */ public static final Color GRAY = Color.GRAY; /** - * The color green. + * The color green (0, 128, 0). */ - public static final Color GREEN = Color.GREEN; + public static final Color GREEN = new Color(0, 128, 0); /** - * The color light gray. + * The color light gray (192, 192, 192). */ public static final Color LIGHT_GRAY = Color.LIGHT_GRAY; /** - * The color magenta. + * The color lime (0, 255, 0). + */ + public static final Color LIME = new Color(0, 255, 0); + + /** + * The color magenta (255, 0, 255). */ public static final Color MAGENTA = Color.MAGENTA; /** - * The color orange. + * The color maroon (128, 0, 0). + */ + public static final Color MAROON = new Color(128, 0, 0); + + /** + * The color navy (0, 0, 128). + */ + public static final Color NAVY = new Color(0, 0, 128); + + /** + * The color olive (128, 128, 0). + */ + public static final Color OLIVE = new Color(128, 128, 0); + + /** + * The color orange (255, 200, 0). */ public static final Color ORANGE = Color.ORANGE; /** - * The color pink. + * The color pink (255, 175, 175). */ public static final Color PINK = Color.PINK; /** - * The color red. + * The color purple (128, 0, 128). + */ + public static final Color PURPLE = new Color(128, 0, 128); + + /** + * The color red (255, 0, 0). */ public static final Color RED = Color.RED; /** - * The color white. + * The color silver (192, 192, 192). + */ + public static final Color SILVER = new Color(192, 192, 192); + + /** + * The color teal (0, 128, 128). + */ + public static final Color TEAL = new Color(0, 128, 128); + + /** + * The color white (255, 255, 255). */ public static final Color WHITE = Color.WHITE; /** - * The color yellow. + * The color yellow (255, 255, 0). */ public static final Color YELLOW = Color.YELLOW; /** - * Shade of blue used in Introduction to Programming in Java. + * A 100% transparent color, for a transparent background. + */ + public static final Color TRANSPARENT = new Color(0, 0, 0, 0); + + /** + * The shade of blue used in Introduction to Programming in Java. * It is Pantone 300U. The RGB values are approximately (9, 90, 166). */ public static final Color BOOK_BLUE = new Color(9, 90, 166); /** - * Shade of light blue used in Introduction to Programming in Java. + * The shade of light blue used in Introduction to Programming in Java. * The RGB values are approximately (103, 198, 243). */ public static final Color BOOK_LIGHT_BLUE = new Color(103, 198, 243); /** - * Shade of red used in Algorithms, 4th edition. + * The shade of red used in Algorithms, 4th edition. * It is Pantone 1805U. The RGB values are approximately (150, 35, 31). */ public static final Color BOOK_RED = new Color(150, 35, 31); /** - * Shade of orange used in Princeton University's identity. + * The shade of orange used in Princeton University's identity. * It is PMS 158. The RGB values are approximately (245, 128, 37). */ public static final Color PRINCETON_ORANGE = new Color(245, 128, 37); // default colors - private static final Color DEFAULT_PEN_COLOR = BLACK; - private static final Color DEFAULT_CLEAR_COLOR = WHITE; + private static final Color DEFAULT_PEN_COLOR = BLACK; + private static final Color DEFAULT_BACKGROUND_COLOR = WHITE; // current pen color - private static Color penColor; + private static Color penColor = DEFAULT_PEN_COLOR; + + // current background color + private static Color backgroundColor = DEFAULT_BACKGROUND_COLOR; + + // default title of standard drawing window + private static final String DEFAULT_WINDOW_TITLE = "Standard Draw"; + + // current title of standard drawing window + private static String windowTitle = DEFAULT_WINDOW_TITLE; // default canvas size is DEFAULT_SIZE-by-DEFAULT_SIZE private static final int DEFAULT_SIZE = 512; @@ -580,7 +668,7 @@ public final class StdDraw implements ActionListener, MouseListener, MouseMotion private static final double DEFAULT_PEN_RADIUS = 0.002; // current pen radius - private static double penRadius; + private static double penRadius = DEFAULT_PEN_RADIUS; // show we draw immediately or wait until next show? private static boolean defer = false; @@ -592,17 +680,21 @@ public final class StdDraw implements ActionListener, MouseListener, MouseMotion private static final double DEFAULT_XMAX = 1.0; private static final double DEFAULT_YMIN = 0.0; private static final double DEFAULT_YMAX = 1.0; - private static double xmin, ymin, xmax, ymax; + + private static double xmin = DEFAULT_XMIN; + private static double xmax = DEFAULT_XMAX; + private static double ymin = DEFAULT_YMIN; + private static double ymax = DEFAULT_YMAX; // for synchronization - private static Object mouseLock = new Object(); - private static Object keyLock = new Object(); + private static final Object MOUSE_LOCK = new Object(); + private static final Object KEY_LOCK = new Object(); // default font private static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 16); // current font - private static Font font; + private static Font font = DEFAULT_FONT; // double buffered graphics private static BufferedImage offscreenImage, onscreenImage; @@ -614,6 +706,9 @@ public final class StdDraw implements ActionListener, MouseListener, MouseMotion // the frame for drawing to the screen private static JFrame frame; + // is the JFrame visible (upon calling draw())? + private static boolean isJFrameVisible = true; + // mouse state private static boolean isMousePressed = false; private static double mouseX = 0; @@ -622,6 +717,7 @@ public final class StdDraw implements ActionListener, MouseListener, MouseMotion // queue of typed key characters private static LinkedList keysTyped = new LinkedList(); + // set of key codes currently pressed down private static TreeSet keysDown = new TreeSet(); @@ -631,14 +727,26 @@ private StdDraw() { } // static initializer static { - init(); + initCanvas(); + initGUI(); + } + + /** + * Makes the drawing window visible or invisible. + * + * @param isVisible if {@code true}, makes the drawing window visible, + * otherwise hides the drawing window. + */ + public static void setVisible(boolean isVisible) { + isJFrameVisible = isVisible; + frame.setVisible(isVisible); } /** * Sets the canvas (drawing area) to be 512-by-512 pixels. - * This also erases the current drawing and resets the coordinate system, - * pen radius, pen color, and font back to their default values. - * Ordinarly, this method is called once, at the very beginning + * This also clears the current drawing using the default background + * color (white). + * Ordinarily, this method is called once, at the very beginning * of a program. */ public static void setCanvasSize() { @@ -647,9 +755,9 @@ public static void setCanvasSize() { /** * Sets the canvas (drawing area) to be width-by-height pixels. - * This also erases the current drawing and resets the coordinate system, - * pen radius, pen color, and font back to their default values. - * Ordinarly, this method is called once, at the very beginning + * This also clears the current drawing using the default background + * color (white). + * Ordinarily, this method is called once, at the very beginning * of a program. * * @param canvasWidth the width as a number of pixels @@ -658,89 +766,150 @@ public static void setCanvasSize() { * {@code canvasHeight} are positive */ public static void setCanvasSize(int canvasWidth, int canvasHeight) { - if (canvasWidth <= 0 || canvasHeight <= 0) - throw new IllegalArgumentException("width and height must be positive"); + if (canvasWidth <= 0) throw new IllegalArgumentException("width must be positive"); + if (canvasHeight <= 0) throw new IllegalArgumentException("height must be positive"); width = canvasWidth; height = canvasHeight; - init(); + initCanvas(); + initGUI(); } - // init - private static void init() { - if (frame != null) frame.setVisible(false); - frame = new JFrame(); - offscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - onscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + // initialize the drawing canvas + private static void initCanvas() { + + // BufferedImage stuff + offscreenImage = new BufferedImage(2*width, 2*height, BufferedImage.TYPE_INT_ARGB); + onscreenImage = new BufferedImage(2*width, 2*height, BufferedImage.TYPE_INT_ARGB); offscreen = offscreenImage.createGraphics(); onscreen = onscreenImage.createGraphics(); - setXscale(); - setYscale(); - offscreen.setColor(DEFAULT_CLEAR_COLOR); - offscreen.fillRect(0, 0, width, height); - setPenColor(); - setPenRadius(); - setFont(); - clear(); + offscreen.scale(2.0, 2.0); // since we made it 2x as big + + // initialize drawing window + offscreen.setBackground(DEFAULT_BACKGROUND_COLOR); + offscreen.clearRect(0, 0, width, height); + onscreen.setBackground(DEFAULT_BACKGROUND_COLOR); + onscreen.clearRect(0, 0, 2*width, 2*height); + + // set the pen color + offscreen.setColor(DEFAULT_PEN_COLOR); // add antialiasing - RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); + RenderingHints hints = new RenderingHints(null); + hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); offscreen.addRenderingHints(hints); + } - // frame stuff - ImageIcon icon = new ImageIcon(onscreenImage); - JLabel draw = new JLabel(icon); + // initialize the GUI + private static void initGUI() { + + // create the JFrame (if necessary) + if (frame == null) { + frame = new JFrame(); + frame.addKeyListener(std); // JLabel cannot get keyboard focus + frame.setFocusTraversalKeysEnabled(false); // allow VK_TAB with isKeyPressed() + frame.setResizable(false); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closes all windows + // frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // closes only current window + frame.setTitle(windowTitle); + frame.setJMenuBar(createMenuBar()); + } + // create the ImageIcon + RetinaImageIcon icon = new RetinaImageIcon(onscreenImage); + JLabel draw = new JLabel(icon); draw.addMouseListener(std); draw.addMouseMotionListener(std); + // finsh up the JFrame frame.setContentPane(draw); - frame.addKeyListener(std); // JLabel cannot get keyboard focus - frame.setResizable(false); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closes all windows - // frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // closes only current window - frame.setTitle("Standard Draw"); - frame.setJMenuBar(createMenuBar()); frame.pack(); frame.requestFocusInWindow(); - frame.setVisible(true); + frame.setVisible(false); } - // create the menu bar (changed to private) + // create the menu bar private static JMenuBar createMenuBar() { JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu("File"); menuBar.add(menu); JMenuItem menuItem1 = new JMenuItem(" Save... "); menuItem1.addActionListener(std); + // Java 11: use getMenuShortcutKeyMaskEx() + // Java 8: use getMenuShortcutKeyMask() menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); menu.add(menuItem1); return menuBar; } + /** + * Closes the standard drawing window. + * This allows the client program to terminate instead of requiring + * the user to close the standard drawing window manually. + * Drawing after calling this method will restore the previous window state. + */ + public static void close() { + frame.dispose(); + } + + /*************************************************************************** + * Input validation helper methods. + ***************************************************************************/ + + // throw an IllegalArgumentException if x is NaN or infinite + private static void validate(double x, String name) { + if (Double.isNaN(x)) throw new IllegalArgumentException(name + " is NaN"); + if (Double.isInfinite(x)) throw new IllegalArgumentException(name + " is infinite"); + } + + // throw an IllegalArgumentException if s is null + private static void validateNonnegative(double x, String name) { + if (x < 0) throw new IllegalArgumentException(name + " negative"); + } + + // throw an IllegalArgumentException if s is null + private static void validateNotNull(Object x, String name) { + if (x == null) throw new IllegalArgumentException(name + " is null"); + } + + + /*************************************************************************** + * Set the title of standard drawing window. + ***************************************************************************/ + + /** + * Sets the title of the standard drawing window to the specified string. + * + * @param title the title + * @throws IllegalArgumentException if {@code title} is {@code null} + */ + public static void setTitle(String title) { + validateNotNull(title, "title"); + frame.setTitle(title); + windowTitle = title; + } /*************************************************************************** * User and screen coordinate systems. ***************************************************************************/ /** - * Sets the x-scale to be the default (between 0.0 and 1.0). + * Sets the x-scale to the default range (between 0.0 and 1.0). */ public static void setXscale() { setXscale(DEFAULT_XMIN, DEFAULT_XMAX); } /** - * Sets the y-scale to be the default (between 0.0 and 1.0). + * Sets the y-scale to the default range (between 0.0 and 1.0). */ public static void setYscale() { setYscale(DEFAULT_YMIN, DEFAULT_YMAX); } /** - * Sets the x-scale and y-scale to be the default + * Sets both the x-scale and y-scale to the default range * (between 0.0 and 1.0). */ public static void setScale() { @@ -754,11 +923,14 @@ public static void setScale() { * @param min the minimum value of the x-scale * @param max the maximum value of the x-scale * @throws IllegalArgumentException if {@code (max == min)} + * @throws IllegalArgumentException if either {@code min} or {@code max} is either NaN or infinite */ public static void setXscale(double min, double max) { + validate(min, "min"); + validate(max, "max"); double size = max - min; if (size == 0.0) throw new IllegalArgumentException("the min and max are the same"); - synchronized (mouseLock) { + synchronized (MOUSE_LOCK) { xmin = min - BORDER * size; xmax = max + BORDER * size; } @@ -770,11 +942,14 @@ public static void setXscale(double min, double max) { * @param min the minimum value of the y-scale * @param max the maximum value of the y-scale * @throws IllegalArgumentException if {@code (max == min)} + * @throws IllegalArgumentException if either {@code min} or {@code max} is either NaN or infinite */ public static void setYscale(double min, double max) { + validate(min, "min"); + validate(max, "max"); double size = max - min; if (size == 0.0) throw new IllegalArgumentException("the min and max are the same"); - synchronized (mouseLock) { + synchronized (MOUSE_LOCK) { ymin = min - BORDER * size; ymax = max + BORDER * size; } @@ -786,11 +961,14 @@ public static void setYscale(double min, double max) { * @param min the minimum value of the x- and y-scales * @param max the maximum value of the x- and y-scales * @throws IllegalArgumentException if {@code (max == min)} + * @throws IllegalArgumentException if either {@code min} or {@code max} is either NaN or infinite */ public static void setScale(double min, double max) { + validate(min, "min"); + validate(max, "max"); double size = max - min; if (size == 0.0) throw new IllegalArgumentException("the min and max are the same"); - synchronized (mouseLock) { + synchronized (MOUSE_LOCK) { xmin = min - BORDER * size; xmax = max + BORDER * size; ymin = min - BORDER * size; @@ -808,21 +986,29 @@ public static void setScale(double min, double max) { /** - * Clears the screen to the default color (white). + * Clears the screen using the default background color (white). */ public static void clear() { - clear(DEFAULT_CLEAR_COLOR); + clear(DEFAULT_BACKGROUND_COLOR); } /** - * Clears the screen to the specified color. + * Clears the screen using the specified background color. + * To make the background transparent, use {@code StdDraw.TRANSPARENT}. * * @param color the color to make the background + * @throws IllegalArgumentException if {@code color} is {@code null} */ public static void clear(Color color) { - offscreen.setColor(color); - offscreen.fillRect(0, 0, width, height); - offscreen.setColor(penColor); + validateNotNull(color, "color"); + + backgroundColor = color; + + offscreen.setBackground(backgroundColor); + offscreen.clearRect(0, 0, width, height); + onscreen.setBackground(backgroundColor); + onscreen.clearRect(0, 0, 2*width, 2*height); + draw(); } @@ -852,10 +1038,12 @@ public static void setPenRadius() { * The pen radius is not affected by coordinate scaling. * * @param radius the radius of the pen - * @throws IllegalArgumentException if {@code radius} is negative + * @throws IllegalArgumentException if {@code radius} is negative, NaN, or infinite */ public static void setPenRadius(double radius) { - if (!(radius >= 0)) throw new IllegalArgumentException("pen radius must be nonnegative"); + validate(radius, "pen radius"); + validateNonnegative(radius, "pen radius"); + penRadius = radius; float scaledPenRadius = (float) (radius * DEFAULT_SIZE); BasicStroke stroke = new BasicStroke(scaledPenRadius, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); @@ -873,7 +1061,16 @@ public static Color getPenColor() { } /** - * Set the pen color to the default color (black). + * Returns the current background color. + * + * @return the current background color + */ + public static Color getBackgroundColor() { + return backgroundColor; + } + + /** + * Sets the pen color to the default color (black). */ public static void setPenColor() { setPenColor(DEFAULT_PEN_COLOR); @@ -882,17 +1079,15 @@ public static void setPenColor() { /** * Sets the pen color to the specified color. *

    - * The predefined pen colors are - * {@code StdDraw.BLACK}, {@code StdDraw.BLUE}, {@code StdDraw.CYAN}, - * {@code StdDraw.DARK_GRAY}, {@code StdDraw.GRAY}, {@code StdDraw.GREEN}, - * {@code StdDraw.LIGHT_GRAY}, {@code StdDraw.MAGENTA}, {@code StdDraw.ORANGE}, - * {@code StdDraw.PINK}, {@code StdDraw.RED}, {@code StdDraw.WHITE}, and - * {@code StdDraw.YELLOW}. + * There are a number predefined pen colors, such as + * {@code StdDraw.BLACK}, {@code StdDraw.WHITE}, {@code StdDraw.RED}, + * {@code StdDraw.GREEN}, and {@code StdDraw.BLUE}. * * @param color the color to make the pen + * @throws IllegalArgumentException if {@code color} is {@code null} */ public static void setPenColor(Color color) { - if (color == null) throw new IllegalArgumentException(); + validateNotNull(color, "color"); penColor = color; offscreen.setColor(penColor); } @@ -907,9 +1102,9 @@ public static void setPenColor(Color color) { * or {@code blue} is outside its prescribed range */ public static void setPenColor(int red, int green, int blue) { - if (red < 0 || red >= 256) throw new IllegalArgumentException("amount of red must be between 0 and 255"); - if (green < 0 || green >= 256) throw new IllegalArgumentException("amount of green must be between 0 and 255"); - if (blue < 0 || blue >= 256) throw new IllegalArgumentException("amount of blue must be between 0 and 255"); + if (red < 0 || red >= 256) throw new IllegalArgumentException("red must be between 0 and 255"); + if (green < 0 || green >= 256) throw new IllegalArgumentException("green must be between 0 and 255"); + if (blue < 0 || blue >= 256) throw new IllegalArgumentException("blue must be between 0 and 255"); setPenColor(new Color(red, green, blue)); } @@ -933,9 +1128,10 @@ public static void setFont() { * Sets the font to the specified value. * * @param font the font + * @throws IllegalArgumentException if {@code font} is {@code null} */ public static void setFont(Font font) { - if (font == null) throw new IllegalArgumentException(); + validateNotNull(font, "font"); StdDraw.font = font; } @@ -952,8 +1148,13 @@ public static void setFont(Font font) { * @param y0 the y-coordinate of one endpoint * @param x1 the x-coordinate of the other endpoint * @param y1 the y-coordinate of the other endpoint + * @throws IllegalArgumentException if any coordinate is either NaN or infinite */ public static void line(double x0, double y0, double x1, double y1) { + validate(x0, "x0"); + validate(y0, "y0"); + validate(x1, "x1"); + validate(y1, "y1"); offscreen.draw(new Line2D.Double(scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1))); draw(); } @@ -965,8 +1166,11 @@ public static void line(double x0, double y0, double x1, double y1) { * * @param x the x-coordinate of the pixel * @param y the y-coordinate of the pixel + * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite */ private static void pixel(double x, double y) { + validate(x, "x"); + validate(y, "y"); offscreen.fillRect((int) Math.round(scaleX(x)), (int) Math.round(scaleY(y)), 1, 1); } @@ -977,8 +1181,12 @@ private static void pixel(double x, double y) { * * @param x the x-coordinate of the point * @param y the y-coordinate of the point + * @throws IllegalArgumentException if either {@code x} or {@code y} is either NaN or infinite */ public static void point(double x, double y) { + validate(x, "x"); + validate(y, "y"); + double xs = scaleX(x); double ys = scaleY(y); double r = penRadius; @@ -1000,9 +1208,14 @@ public static void point(double x, double y) { * @param y the y-coordinate of the center of the circle * @param radius the radius of the circle * @throws IllegalArgumentException if {@code radius} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ public static void circle(double x, double y, double radius) { - if (!(radius >= 0)) throw new IllegalArgumentException("radius must be nonnegative"); + validate(x, "x"); + validate(y, "y"); + validate(radius, "radius"); + validateNonnegative(radius, "radius"); + double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*radius); @@ -1019,9 +1232,14 @@ public static void circle(double x, double y, double radius) { * @param y the y-coordinate of the center of the circle * @param radius the radius of the circle * @throws IllegalArgumentException if {@code radius} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ public static void filledCircle(double x, double y, double radius) { - if (!(radius >= 0)) throw new IllegalArgumentException("radius must be nonnegative"); + validate(x, "x"); + validate(y, "y"); + validate(radius, "radius"); + validateNonnegative(radius, "radius"); + double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*radius); @@ -1042,10 +1260,16 @@ public static void filledCircle(double x, double y, double radius) { * @param semiMinorAxis is the semiminor axis of the ellipse * @throws IllegalArgumentException if either {@code semiMajorAxis} * or {@code semiMinorAxis} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ public static void ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis) { - if (!(semiMajorAxis >= 0)) throw new IllegalArgumentException("ellipse semimajor axis must be nonnegative"); - if (!(semiMinorAxis >= 0)) throw new IllegalArgumentException("ellipse semiminor axis must be nonnegative"); + validate(x, "x"); + validate(y, "y"); + validate(semiMajorAxis, "semimajor axis"); + validate(semiMinorAxis, "semiminor axis"); + validateNonnegative(semiMajorAxis, "semimajor axis"); + validateNonnegative(semiMinorAxis, "semiminor axis"); + double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*semiMajorAxis); @@ -1056,7 +1280,7 @@ public static void ellipse(double x, double y, double semiMajorAxis, double semi } /** - * Draws an ellipse with the specified semimajor and semiminor axes, + * Draws a filled ellipse with the specified semimajor and semiminor axes, * centered at (x, y). * * @param x the x-coordinate of the center of the ellipse @@ -1065,10 +1289,16 @@ public static void ellipse(double x, double y, double semiMajorAxis, double semi * @param semiMinorAxis is the semiminor axis of the ellipse * @throws IllegalArgumentException if either {@code semiMajorAxis} * or {@code semiMinorAxis} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ public static void filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis) { - if (!(semiMajorAxis >= 0)) throw new IllegalArgumentException("ellipse semimajor axis must be nonnegative"); - if (!(semiMinorAxis >= 0)) throw new IllegalArgumentException("ellipse semiminor axis must be nonnegative"); + validate(x, "x"); + validate(y, "y"); + validate(semiMajorAxis, "semimajor axis"); + validate(semiMinorAxis, "semiminor axis"); + validateNonnegative(semiMajorAxis, "semimajor axis"); + validateNonnegative(semiMinorAxis, "semiminor axis"); + double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*semiMajorAxis); @@ -1090,9 +1320,16 @@ public static void filledEllipse(double x, double y, double semiMajorAxis, doubl * @param angle2 the angle at the end of the arc. For example, if * you want a 90 degree arc, then angle2 should be angle1 + 90. * @throws IllegalArgumentException if {@code radius} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ public static void arc(double x, double y, double radius, double angle1, double angle2) { - if (radius < 0) throw new IllegalArgumentException("arc radius must be nonnegative"); + validate(x, "x"); + validate(y, "y"); + validate(radius, "arc radius"); + validate(angle1, "angle1"); + validate(angle2, "angle2"); + validateNonnegative(radius, "arc radius"); + while (angle2 < angle1) angle2 += 360; double xs = scaleX(x); double ys = scaleY(y); @@ -1104,15 +1341,20 @@ public static void arc(double x, double y, double radius, double angle1, double } /** - * Draws a square of side length 2r, centered at (x, y). + * Draws a square of the specified size, centered at (x, y). * * @param x the x-coordinate of the center of the square * @param y the y-coordinate of the center of the square * @param halfLength one half the length of any side of the square * @throws IllegalArgumentException if {@code halfLength} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ public static void square(double x, double y, double halfLength) { - if (!(halfLength >= 0)) throw new IllegalArgumentException("half length must be nonnegative"); + validate(x, "x"); + validate(y, "y"); + validate(halfLength, "halfLength"); + validateNonnegative(halfLength, "half length"); + double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*halfLength); @@ -1129,9 +1371,14 @@ public static void square(double x, double y, double halfLength) { * @param y the y-coordinate of the center of the square * @param halfLength one half the length of any side of the square * @throws IllegalArgumentException if {@code halfLength} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ public static void filledSquare(double x, double y, double halfLength) { - if (!(halfLength >= 0)) throw new IllegalArgumentException("half length must be nonnegative"); + validate(x, "x"); + validate(y, "y"); + validate(halfLength, "halfLength"); + validateNonnegative(halfLength, "half length"); + double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*halfLength); @@ -1150,10 +1397,16 @@ public static void filledSquare(double x, double y, double halfLength) { * @param halfWidth one half the width of the rectangle * @param halfHeight one half the height of the rectangle * @throws IllegalArgumentException if either {@code halfWidth} or {@code halfHeight} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ public static void rectangle(double x, double y, double halfWidth, double halfHeight) { - if (!(halfWidth >= 0)) throw new IllegalArgumentException("half width must be nonnegative"); - if (!(halfHeight >= 0)) throw new IllegalArgumentException("half height must be nonnegative"); + validate(x, "x"); + validate(y, "y"); + validate(halfWidth, "halfWidth"); + validate(halfHeight, "halfHeight"); + validateNonnegative(halfWidth, "half width"); + validateNonnegative(halfHeight, "half height"); + double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*halfWidth); @@ -1171,10 +1424,16 @@ public static void rectangle(double x, double y, double halfWidth, double halfHe * @param halfWidth one half the width of the rectangle * @param halfHeight one half the height of the rectangle * @throws IllegalArgumentException if either {@code halfWidth} or {@code halfHeight} is negative + * @throws IllegalArgumentException if any argument is either NaN or infinite */ public static void filledRectangle(double x, double y, double halfWidth, double halfHeight) { - if (!(halfWidth >= 0)) throw new IllegalArgumentException("half width must be nonnegative"); - if (!(halfHeight >= 0)) throw new IllegalArgumentException("half height must be nonnegative"); + validate(x, "x"); + validate(y, "y"); + validate(halfWidth, "halfWidth"); + validate(halfHeight, "halfHeight"); + validateNonnegative(halfWidth, "half width"); + validateNonnegative(halfHeight, "half height"); + double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*halfWidth); @@ -1186,7 +1445,7 @@ public static void filledRectangle(double x, double y, double halfWidth, double /** - * Draws a polygon with the vertices + * Draws a polygon with the vertices * (x0, y0), * (x1, y1), ..., * (xn–1, yn–1). @@ -1195,10 +1454,15 @@ public static void filledRectangle(double x, double y, double halfWidth, double * @param y an array of all the y-coordinates of the polygon * @throws IllegalArgumentException unless {@code x[]} and {@code y[]} * are of the same length + * @throws IllegalArgumentException if any coordinate is either NaN or infinite + * @throws IllegalArgumentException if either {@code x[]} or {@code y[]} is {@code null} */ public static void polygon(double[] x, double[] y) { - if (x == null) throw new IllegalArgumentException("x-coordinate array is null"); - if (y == null) throw new IllegalArgumentException("y-coordinate array is null"); + validateNotNull(x, "x-coordinate array"); + validateNotNull(y, "y-coordinate array"); + for (int i = 0; i < x.length; i++) validate(x[i], "x[" + i + "]"); + for (int i = 0; i < y.length; i++) validate(y[i], "y[" + i + "]"); + int n1 = x.length; int n2 = y.length; if (n1 != n2) throw new IllegalArgumentException("arrays must be of the same length"); @@ -1215,7 +1479,7 @@ public static void polygon(double[] x, double[] y) { } /** - * Draws a polygon with the vertices + * Draws a filled polygon with the vertices * (x0, y0), * (x1, y1), ..., * (xn–1, yn–1). @@ -1224,10 +1488,15 @@ public static void polygon(double[] x, double[] y) { * @param y an array of all the y-coordinates of the polygon * @throws IllegalArgumentException unless {@code x[]} and {@code y[]} * are of the same length + * @throws IllegalArgumentException if any coordinate is either NaN or infinite + * @throws IllegalArgumentException if either {@code x[]} or {@code y[]} is {@code null} */ public static void filledPolygon(double[] x, double[] y) { - if (x == null) throw new IllegalArgumentException("x-coordinate array is null"); - if (y == null) throw new IllegalArgumentException("y-coordinate array is null"); + validateNotNull(x, "x-coordinate array"); + validateNotNull(y, "y-coordinate array"); + for (int i = 0; i < x.length; i++) validate(x[i], "x[" + i + "]"); + for (int i = 0; i < y.length; i++) validate(y[i], "y[" + i + "]"); + int n1 = x.length; int n2 = y.length; if (n1 != n2) throw new IllegalArgumentException("arrays must be of the same length"); @@ -1255,27 +1524,30 @@ private static Image getImage(String filename) { ImageIcon icon = new ImageIcon(filename); // try to read from URL - if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) { + if (icon.getImageLoadStatus() != MediaTracker.COMPLETE) { try { - URL url = new URL(filename); - icon = new ImageIcon(url); + URI uri = new URI(filename); + if (uri.isAbsolute()) { + URL url = uri.toURL(); + icon = new ImageIcon(url); + } } - catch (MalformedURLException e) { + catch (MalformedURLException | URISyntaxException e) { /* not a url */ } } // in case file is inside a .jar (classpath relative to StdDraw) - if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) { + if (icon.getImageLoadStatus() != MediaTracker.COMPLETE) { URL url = StdDraw.class.getResource(filename); if (url != null) icon = new ImageIcon(url); } // in case file is inside a .jar (classpath relative to root of jar) - if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) { + if (icon.getImageLoadStatus() != MediaTracker.COMPLETE) { URL url = StdDraw.class.getResource("/" + filename); - if (url == null) throw new IllegalArgumentException("image " + filename + " not found"); + if (url == null) throw new IllegalArgumentException("could not read image: '" + filename + "'"); icon = new ImageIcon(url); } @@ -1298,7 +1570,7 @@ private static BufferedImage getImage(String filename) { URL url = new URL(filename); BufferedImage image = ImageIO.read(url); return image; - } + } catch (IOException e) { // ignore } @@ -1308,7 +1580,7 @@ private static BufferedImage getImage(String filename) { URL url = StdDraw.class.getResource(filename); BufferedImage image = ImageIO.read(url); return image; - } + } catch (IOException e) { // ignore } @@ -1318,7 +1590,7 @@ private static BufferedImage getImage(String filename) { URL url = StdDraw.class.getResource("/" + filename); BufferedImage image = ImageIO.read(url); return image; - } + } catch (IOException e) { // ignore } @@ -1327,7 +1599,7 @@ private static BufferedImage getImage(String filename) { */ /** * Draws the specified image centered at (x, y). - * The supported image formats are JPEG, PNG, and GIF. + * The supported image formats are typically JPEG, PNG, GIF, TIFF, and BMP. * As an optimization, the picture is cached, so there is no performance * penalty for redrawing the same image multiple times (e.g., in an animation). * However, if you change the picture file after drawing it, subsequent @@ -1337,8 +1609,13 @@ private static BufferedImage getImage(String filename) { * @param y the center y-coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" * @throws IllegalArgumentException if the image filename is invalid + * @throws IllegalArgumentException if either {@code x} or {@code y} is either NaN or infinite */ public static void picture(double x, double y, String filename) { + validate(x, "x"); + validate(y, "y"); + validateNotNull(filename, "filename"); + // BufferedImage image = getImage(filename); Image image = getImage(filename); double xs = scaleX(x); @@ -1356,15 +1633,22 @@ public static void picture(double x, double y, String filename) { /** * Draws the specified image centered at (x, y), * rotated given number of degrees. - * The supported image formats are JPEG, PNG, and GIF. + * The supported image formats are typically JPEG, PNG, GIF, TIFF, and BMP. * * @param x the center x-coordinate of the image * @param y the center y-coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" * @param degrees is the number of degrees to rotate counterclockwise * @throws IllegalArgumentException if the image filename is invalid + * @throws IllegalArgumentException if {@code x}, {@code y}, {@code degrees} is NaN or infinite + * @throws IllegalArgumentException if {@code filename} is {@code null} */ public static void picture(double x, double y, String filename, double degrees) { + validate(x, "x"); + validate(y, "y"); + validate(degrees, "degrees"); + validateNotNull(filename, "filename"); + // BufferedImage image = getImage(filename); Image image = getImage(filename); double xs = scaleX(x); @@ -1385,7 +1669,7 @@ public static void picture(double x, double y, String filename, double degrees) /** * Draws the specified image centered at (x, y), * rescaled to the specified bounding box. - * The supported image formats are JPEG, PNG, and GIF. + * The supported image formats are typically JPEG, PNG, GIF, TIFF, and BMP. * * @param x the center x-coordinate of the image * @param y the center y-coordinate of the image @@ -1395,11 +1679,19 @@ public static void picture(double x, double y, String filename, double degrees) * @throws IllegalArgumentException if either {@code scaledWidth} * or {@code scaledHeight} is negative * @throws IllegalArgumentException if the image filename is invalid + * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite + * @throws IllegalArgumentException if {@code filename} is {@code null} */ public static void picture(double x, double y, String filename, double scaledWidth, double scaledHeight) { + validate(x, "x"); + validate(y, "y"); + validate(scaledWidth, "scaled width"); + validate(scaledHeight, "scaled height"); + validateNotNull(filename, "filename"); + validateNonnegative(scaledWidth, "scaled width"); + validateNonnegative(scaledHeight, "scaled height"); + Image image = getImage(filename); - if (scaledWidth < 0) throw new IllegalArgumentException("width is negative: " + scaledWidth); - if (scaledHeight < 0) throw new IllegalArgumentException("height is negative: " + scaledHeight); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(scaledWidth); @@ -1419,7 +1711,7 @@ public static void picture(double x, double y, String filename, double scaledWid /** * Draws the specified image centered at (x, y), rotated * given number of degrees, and rescaled to the specified bounding box. - * The supported image formats are JPEG, PNG, and GIF. + * The supported image formats are typically JPEG, PNG, GIF, TIFF, and BMP. * * @param x the center x-coordinate of the image * @param y the center y-coordinate of the image @@ -1432,8 +1724,15 @@ public static void picture(double x, double y, String filename, double scaledWid * @throws IllegalArgumentException if the image filename is invalid */ public static void picture(double x, double y, String filename, double scaledWidth, double scaledHeight, double degrees) { - if (scaledWidth < 0) throw new IllegalArgumentException("width is negative: " + scaledWidth); - if (scaledHeight < 0) throw new IllegalArgumentException("height is negative: " + scaledHeight); + validate(x, "x"); + validate(y, "y"); + validate(scaledWidth, "scaled width"); + validate(scaledHeight, "scaled height"); + validate(degrees, "degrees"); + validateNotNull(filename, "filename"); + validateNonnegative(scaledWidth, "scaled width"); + validateNonnegative(scaledHeight, "scaled height"); + Image image = getImage(filename); double xs = scaleX(x); double ys = scaleY(y); @@ -1457,14 +1756,19 @@ public static void picture(double x, double y, String filename, double scaledWid ***************************************************************************/ /** - * Write the given text string in the current font, centered at (x, y). + * Writes the given text string in the current font, centered at (x, y). * * @param x the center x-coordinate of the text * @param y the center y-coordinate of the text * @param text the text to write + * @throws IllegalArgumentException if {@code text} is {@code null} + * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite */ public static void text(double x, double y, String text) { - if (text == null) throw new IllegalArgumentException(); + validate(x, "x"); + validate(y, "y"); + validateNotNull(text, "text"); + offscreen.setFont(font); FontMetrics metrics = offscreen.getFontMetrics(); double xs = scaleX(x); @@ -1476,15 +1780,21 @@ public static void text(double x, double y, String text) { } /** - * Write the given text string in the current font, centered at (x, y) and + * Writes the given text string in the current font, centered at (x, y) and * rotated by the specified number of degrees. * @param x the center x-coordinate of the text * @param y the center y-coordinate of the text * @param text the text to write * @param degrees is the number of degrees to rotate counterclockwise + * @throws IllegalArgumentException if {@code text} is {@code null} + * @throws IllegalArgumentException if {@code x}, {@code y}, or {@code degrees} is either NaN or infinite */ public static void text(double x, double y, String text, double degrees) { - if (text == null) throw new IllegalArgumentException(); + validate(x, "x"); + validate(y, "y"); + validate(degrees, "degrees"); + validateNotNull(text, "text"); + double xs = scaleX(x); double ys = scaleY(y); offscreen.rotate(Math.toRadians(-degrees), xs, ys); @@ -1494,13 +1804,18 @@ public static void text(double x, double y, String text, double degrees) { /** - * Write the given text string in the current font, left-aligned at (x, y). + * Writes the given text string in the current font, left-aligned at (x, y). * @param x the x-coordinate of the text * @param y the y-coordinate of the text * @param text the text + * @throws IllegalArgumentException if {@code text} is {@code null} + * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite */ public static void textLeft(double x, double y, String text) { - if (text == null) throw new IllegalArgumentException(); + validate(x, "x"); + validate(y, "y"); + validateNotNull(text, "text"); + offscreen.setFont(font); FontMetrics metrics = offscreen.getFontMetrics(); double xs = scaleX(x); @@ -1511,14 +1826,19 @@ public static void textLeft(double x, double y, String text) { } /** - * Write the given text string in the current font, right-aligned at (x, y). + * Writes the given text string in the current font, right-aligned at (x, y). * * @param x the x-coordinate of the text * @param y the y-coordinate of the text * @param text the text to write + * @throws IllegalArgumentException if {@code text} is {@code null} + * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite */ public static void textRight(double x, double y, String text) { - if (text == null) throw new IllegalArgumentException(); + validate(x, "x"); + validate(y, "y"); + validateNotNull(text, "text"); + offscreen.setFont(font); FontMetrics metrics = offscreen.getFontMetrics(); double xs = scaleX(x); @@ -1530,25 +1850,28 @@ public static void textRight(double x, double y, String text) { } - /** * Copies the offscreen buffer to the onscreen buffer, pauses for t milliseconds * and enables double buffering. * @param t number of milliseconds + * @throws IllegalArgumentException if {@code t} is negative * @deprecated replaced by {@link #enableDoubleBuffering()}, {@link #show()}, and {@link #pause(int t)} */ @Deprecated public static void show(int t) { + validateNonnegative(t, "t"); show(); pause(t); enableDoubleBuffering(); } /** - * Pause for t milliseconds. This method is intended to support computer animations. + * Pauses for t milliseconds. This method is intended to support computer animations. * @param t number of milliseconds + * @throws IllegalArgumentException if {@code t} is negative */ public static void pause(int t) { + validateNonnegative(t, "t"); try { Thread.sleep(t); } @@ -1563,6 +1886,12 @@ public static void pause(int t) { */ public static void show() { onscreen.drawImage(offscreenImage, 0, 0, null); + + // make frame visible upon first call to show() + if (frame.isVisible() != isJFrameVisible) { + frame.setVisible(isJFrameVisible); + } + frame.repaint(); } @@ -1572,9 +1901,9 @@ private static void draw() { } /** - * Enable double buffering. All subsequent calls to + * Enables double buffering. All subsequent calls to * drawing methods such as {@code line()}, {@code circle()}, - * and {@code square()} will be deffered until the next call + * and {@code square()} will be deferred until the next call * to show(). Useful for animations. */ public static void enableDoubleBuffering() { @@ -1582,7 +1911,7 @@ public static void enableDoubleBuffering() { } /** - * Disable double buffering. All subsequent calls to + * Disables double buffering. All subsequent calls to * drawing methods such as {@code line()}, {@code circle()}, * and {@code square()} will be displayed on screen when called. * This is the default. @@ -1597,49 +1926,43 @@ public static void disableDoubleBuffering() { ***************************************************************************/ /** - * Saves the drawing to using the specified filename. - * The supported image formats are JPEG and PNG; - * the filename suffix must be {@code .jpg} or {@code .png}. + * Saves the drawing to a file in a supported file format + * (typically JPEG, PNG, GIF, TIFF, and BMP). + * The filetype extension must be {@code .jpg}, {@code .png}, {@code .gif}, + * {@code .bmp}, or {@code .tif}. * - * @param filename the name of the file with one of the required suffixes + * @param filename the name of the file + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if {@code filename} is the empty string + * @throws IllegalArgumentException if {@code filename} has invalid filetype extension + * @throws IllegalArgumentException if cannot write the file {@code filename} */ public static void save(String filename) { - if (filename == null) throw new IllegalArgumentException(); + validateNotNull(filename, "filename"); + if (filename.length() == 0) { + throw new IllegalArgumentException("argument to save() is the empty string"); + } + File file = new File(filename); String suffix = filename.substring(filename.lastIndexOf('.') + 1); - - // png files - if ("png".equalsIgnoreCase(suffix)) { - try { - ImageIO.write(onscreenImage, suffix, file); - } - catch (IOException e) { - e.printStackTrace(); - } + if (!filename.contains(".") || suffix.length() == 0) { + throw new IllegalArgumentException("The filename '" + filename + "' has no filetype extension, such as .jpg or .png"); } - // need to change from ARGB to RGB for JPEG - // reference: http://archives.java.sun.com/cgi-bin/wa?A2=ind0404&L=java2d-interest&D=0&P=2727 - else if ("jpg".equalsIgnoreCase(suffix)) { - WritableRaster raster = onscreenImage.getRaster(); - WritableRaster newRaster; - newRaster = raster.createWritableChild(0, 0, width, height, 0, 0, new int[] {0, 1, 2}); - DirectColorModel cm = (DirectColorModel) onscreenImage.getColorModel(); - DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), - cm.getRedMask(), - cm.getGreenMask(), - cm.getBlueMask()); - BufferedImage rgbBuffer = new BufferedImage(newCM, newRaster, false, null); - try { - ImageIO.write(rgbBuffer, suffix, file); - } - catch (IOException e) { - e.printStackTrace(); - } - } + try { + // if the file format supports transparency (such as PNG or GIF) + if (ImageIO.write(onscreenImage, suffix, file)) return; - else { - System.out.println("Invalid image file type: " + suffix); + // if the file format does not support transparency (such as JPEG or BMP) + BufferedImage saveImage = new BufferedImage(2*width, 2*height, BufferedImage.TYPE_INT_RGB); + saveImage.createGraphics().drawImage(onscreenImage, 0, 0, Color.WHITE, null); + if (ImageIO.write(saveImage, suffix, file)) return; + + // failed to save the file; probably wrong format + throw new IllegalArgumentException("The filetype '" + suffix + "' is not supported"); + } + catch (IOException e) { + throw new IllegalArgumentException("could not write file '" + filename + "'", e); } } @@ -1648,12 +1971,18 @@ else if ("jpg".equalsIgnoreCase(suffix)) { * This method cannot be called directly. */ @Override - public void actionPerformed(ActionEvent e) { + public void actionPerformed(ActionEvent event) { FileDialog chooser = new FileDialog(StdDraw.frame, "Use a .png or .jpg extension", FileDialog.SAVE); chooser.setVisible(true); - String filename = chooser.getFile(); - if (filename != null) { - StdDraw.save(chooser.getDirectory() + File.separator + chooser.getFile()); + String selectedDirectory = chooser.getDirectory(); + String selectedFilename = chooser.getFile(); + if (selectedDirectory != null && selectedFilename != null) { + try { + StdDraw.save(selectedDirectory + selectedFilename); + } + catch (IllegalArgumentException e) { + System.err.println(e.getMessage()); + } } } @@ -1668,7 +1997,7 @@ public void actionPerformed(ActionEvent e) { * @return {@code true} if the mouse is being pressed; {@code false} otherwise */ public static boolean isMousePressed() { - synchronized (mouseLock) { + synchronized (MOUSE_LOCK) { return isMousePressed; } } @@ -1681,7 +2010,7 @@ public static boolean isMousePressed() { */ @Deprecated public static boolean mousePressed() { - synchronized (mouseLock) { + synchronized (MOUSE_LOCK) { return isMousePressed; } } @@ -1692,7 +2021,7 @@ public static boolean mousePressed() { * @return the x-coordinate of the mouse */ public static double mouseX() { - synchronized (mouseLock) { + synchronized (MOUSE_LOCK) { return mouseX; } } @@ -1703,7 +2032,7 @@ public static double mouseX() { * @return y-coordinate of the mouse */ public static double mouseY() { - synchronized (mouseLock) { + synchronized (MOUSE_LOCK) { return mouseY; } } @@ -1713,7 +2042,7 @@ public static double mouseY() { * This method cannot be called directly. */ @Override - public void mouseClicked(MouseEvent e) { + public void mouseClicked(MouseEvent event) { // this body is intentionally left empty } @@ -1721,7 +2050,7 @@ public void mouseClicked(MouseEvent e) { * This method cannot be called directly. */ @Override - public void mouseEntered(MouseEvent e) { + public void mouseEntered(MouseEvent event) { // this body is intentionally left empty } @@ -1729,7 +2058,7 @@ public void mouseEntered(MouseEvent e) { * This method cannot be called directly. */ @Override - public void mouseExited(MouseEvent e) { + public void mouseExited(MouseEvent event) { // this body is intentionally left empty } @@ -1737,10 +2066,10 @@ public void mouseExited(MouseEvent e) { * This method cannot be called directly. */ @Override - public void mousePressed(MouseEvent e) { - synchronized (mouseLock) { - mouseX = StdDraw.userX(e.getX()); - mouseY = StdDraw.userY(e.getY()); + public void mousePressed(MouseEvent event) { + synchronized (MOUSE_LOCK) { + mouseX = StdDraw.userX(event.getX()); + mouseY = StdDraw.userY(event.getY()); isMousePressed = true; } } @@ -1749,8 +2078,8 @@ public void mousePressed(MouseEvent e) { * This method cannot be called directly. */ @Override - public void mouseReleased(MouseEvent e) { - synchronized (mouseLock) { + public void mouseReleased(MouseEvent event) { + synchronized (MOUSE_LOCK) { isMousePressed = false; } } @@ -1759,10 +2088,10 @@ public void mouseReleased(MouseEvent e) { * This method cannot be called directly. */ @Override - public void mouseDragged(MouseEvent e) { - synchronized (mouseLock) { - mouseX = StdDraw.userX(e.getX()); - mouseY = StdDraw.userY(e.getY()); + public void mouseDragged(MouseEvent event) { + synchronized (MOUSE_LOCK) { + mouseX = StdDraw.userX(event.getX()); + mouseY = StdDraw.userY(event.getY()); } } @@ -1770,10 +2099,10 @@ public void mouseDragged(MouseEvent e) { * This method cannot be called directly. */ @Override - public void mouseMoved(MouseEvent e) { - synchronized (mouseLock) { - mouseX = StdDraw.userX(e.getX()); - mouseY = StdDraw.userY(e.getY()); + public void mouseMoved(MouseEvent event) { + synchronized (MOUSE_LOCK) { + mouseX = StdDraw.userX(event.getX()); + mouseY = StdDraw.userY(event.getY()); } } @@ -1789,7 +2118,7 @@ public void mouseMoved(MouseEvent e) { * by {@link #nextKeyTyped()}; {@code false} otherwise */ public static boolean hasNextKeyTyped() { - synchronized (keyLock) { + synchronized (KEY_LOCK) { return !keysTyped.isEmpty(); } } @@ -1807,7 +2136,7 @@ public static boolean hasNextKeyTyped() { * @throws NoSuchElementException if there is no remaining key */ public static char nextKeyTyped() { - synchronized (keyLock) { + synchronized (KEY_LOCK) { if (keysTyped.isEmpty()) { throw new NoSuchElementException("your program has already processed all keystrokes"); } @@ -1829,7 +2158,7 @@ public static char nextKeyTyped() { * {@code false} otherwise */ public static boolean isKeyPressed(int keycode) { - synchronized (keyLock) { + synchronized (KEY_LOCK) { return keysDown.contains(keycode); } } @@ -1839,9 +2168,9 @@ public static boolean isKeyPressed(int keycode) { * This method cannot be called directly. */ @Override - public void keyTyped(KeyEvent e) { - synchronized (keyLock) { - keysTyped.addFirst(e.getKeyChar()); + public void keyTyped(KeyEvent event) { + synchronized (KEY_LOCK) { + keysTyped.addFirst(event.getKeyChar()); } } @@ -1849,9 +2178,9 @@ public void keyTyped(KeyEvent e) { * This method cannot be called directly. */ @Override - public void keyPressed(KeyEvent e) { - synchronized (keyLock) { - keysDown.add(e.getKeyCode()); + public void keyPressed(KeyEvent event) { + synchronized (KEY_LOCK) { + keysDown.add(event.getKeyCode()); } } @@ -1859,13 +2188,46 @@ public void keyPressed(KeyEvent e) { * This method cannot be called directly. */ @Override - public void keyReleased(KeyEvent e) { - synchronized (keyLock) { - keysDown.remove(e.getKeyCode()); + public void keyReleased(KeyEvent event) { + synchronized (KEY_LOCK) { + keysDown.remove(event.getKeyCode()); } } + /*************************************************************************** + * For improved resolution on Mac Retina displays. + ***************************************************************************/ + + private static class RetinaImageIcon extends ImageIcon { + + public RetinaImageIcon(Image image) { + super(image); + } + + public int getIconWidth() { + return super.getIconWidth() / 2; + } + + /** + * Returns the height of the icon. + * + * @return the height in pixels of this icon + */ + public int getIconHeight() { + return super.getIconHeight() / 2; + } + + public synchronized void paintIcon(Component c, Graphics g, int x, int y) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g2.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); + g2.scale(0.5, 0.5); + super.paintIcon(c, g2, x * 2, y * 2); + g2.dispose(); + } + } /** @@ -1899,7 +2261,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/StdIn.java b/src/main/java/edu/princeton/cs/algs4/StdIn.java index e73e2b483..87460962b 100644 --- a/src/main/java/edu/princeton/cs/algs4/StdIn.java +++ b/src/main/java/edu/princeton/cs/algs4/StdIn.java @@ -30,7 +30,16 @@ * *

    * Generally, it is best not to mix functions from the different - * categories in the same program. + * categories in the same program. + *

    + * Getting started. + * To use this class, you must have {@code StdIn.class} in your + * Java classpath. If you used our autoinstaller, you should be all set. + * Otherwise, either download + * stdlib.jar + * and add to your Java classpath or download + * StdIn.java + * and put a copy in your working directory. *

    * Reading tokens from standard input and converting to numbers and strings. * You can use the following methods to read numbers, strings, and booleans @@ -47,7 +56,7 @@ *

  • {@link #readBoolean()} * *

    - * The first method returns true if standard input has more tokens. + * The first method returns true if standard input has no more tokens. * Each other method skips over any input that is whitespace. Then, it reads * the next token and attempts to convert it into a value of the specified * type. If it succeeds, it returns that value; otherwise, it @@ -71,7 +80,6 @@ * double value = StdIn.readDouble(); * StdOut.println(value); * } - * StdOut.println(sum); *

  • *

    * Reading characters from standard input. @@ -82,7 +90,7 @@ * *

    * The first method returns true if standard input has more input (including whitespace). - * The second method reads and returns the next character of input on standard + * The second method reads and returns the next character of input on standard * input (possibly a whitespace character). *

    * As an example, the following code fragment reads characters from standard input, @@ -102,7 +110,7 @@ * *

    * The first method returns true if standard input has more input (including whitespace). - * The second method reads and returns the remaining portion of + * The second method reads and returns the remaining portion of * the next line of input on standard input (possibly whitespace), * discarding the trailing line separator. *

    @@ -148,11 +156,11 @@ *

  • *

    * Differences with Scanner. - * {@code StdIn} and {@link Scanner} are both designed to parse + * {@code StdIn} and {@link Scanner} are both designed to parse * tokens and convert them to primitive types and strings. * The main differences are summarized below: *

      - *
    • {@code StdIn} is a set of static methods and reads + *
    • {@code StdIn} is a set of static methods and reads * reads input from only standard input. It is suitable for use before * a programmer knows about objects. * See {@link In} for an object-oriented version that handles @@ -175,26 +183,26 @@ * {@code Scanner} was introduced, this class was re-implemented to use {@code Scanner}. *

      * Using standard input. - * Standard input is fundamental operating system abstraction, on Mac OS X, + * Standard input is a fundamental operating system abstraction on Mac OS X, * Windows, and Linux. * The methods in {@code StdIn} are blocking, which means that they * will wait until you enter input on standard input. * If your program has a loop that repeats until standard input is empty, * you must signal that the input is finished. - * To do so, depending on your operating system and IDE, + * To do so, depending on your operating system and IDE, * use either {@code } or {@code }, on its own line. * If you are redirecting standard input from a file, you will not need * to do anything to signal that the input is finished. *

      * Known bugs. - * Java's UTF-8 encoding does not recognize the optional + * Java's UTF-8 encoding does not recognize the optional * byte-order mask. * If the input begins with the optional byte-order mask, {@code StdIn} * will have an extra character {@code \}{@code uFEFF} at the beginning. *

      - * Reference. + * Reference. * For additional documentation, - * see Section 1.5 of + * see Section 1.5 of * Computer Science: An Interdisciplinary Approach * by Robert Sedgewick and Kevin Wayne. * @@ -205,7 +213,7 @@ public final class StdIn { /*** begin: section (1 of 2) of code duplicated from In to StdIn. */ - + // assume Unicode UTF-8 encoding private static final String CHARSET_NAME = "UTF-8"; @@ -225,7 +233,7 @@ public final class StdIn { /*** end: section (1 of 2) of code duplicated from In to StdIn. */ private static Scanner scanner; - + // it doesn't make sense to instantiate this class private StdIn() { } @@ -234,8 +242,8 @@ private StdIn() { } /** * Returns true if standard input is empty (except possibly for whitespace). - * Use this method to know whether the next call to {@link #readString()}, - * {@link #readDouble()}, etc will succeed. + * Use this method to know whether the next call to {@link #readString()}, + * {@link #readDouble()}, etc. will succeed. * * @return {@code true} if standard input is empty (except possibly * for whitespace); {@code false} otherwise @@ -309,8 +317,7 @@ public static char readChar() { throw new NoSuchElementException("attempts to read a 'char' value from standard input, " + "but no more tokens are available"); } - } - + } /** * Reads and returns the remainder of the input, as a string. @@ -323,14 +330,14 @@ public static String readAll() { return ""; String result = scanner.useDelimiter(EVERYTHING_PATTERN).next(); - // not that important to reset delimeter, since now scanner is empty + // not that important to reset delimiter, since now scanner is empty scanner.useDelimiter(WHITESPACE_PATTERN); // but let's do it anyway return result; } /** - * Reads the next token and returns the {@code String}. + * Reads the next token from standard input and returns it as a {@code String}. * * @return the next {@code String} * @throws NoSuchElementException if standard input is empty @@ -533,7 +540,7 @@ public static String[] readAllLines() { while (hasNextLine()) { lines.add(readLine()); } - return lines.toArray(new String[lines.size()]); + return lines.toArray(new String[0]); } /** @@ -577,10 +584,9 @@ public static double[] readAllDoubles() { vals[i] = Double.parseDouble(fields[i]); return vals; } - + //// end: section (2 of 2) of code duplicated from In to StdIn - - + // do this once when StdIn is initialized static { resync(); @@ -592,7 +598,7 @@ public static double[] readAllDoubles() { private static void resync() { setScanner(new Scanner(new java.io.BufferedInputStream(System.in), CHARSET_NAME)); } - + private static void setScanner(Scanner scanner) { StdIn.scanner = scanner; StdIn.scanner.useLocale(LOCALE); @@ -664,7 +670,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/StdOut.java b/src/main/java/edu/princeton/cs/algs4/StdOut.java index 83261d8d2..376514db5 100644 --- a/src/main/java/edu/princeton/cs/algs4/StdOut.java +++ b/src/main/java/edu/princeton/cs/algs4/StdOut.java @@ -12,15 +12,20 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Locale; /** - * This class provides methods for printing strings and numbers to standard output. - *

      - * Getting started. + * The {@code StdOut} class provides static methods for printing strings + * and numbers to standard output. + * + *

      Getting started. * To use this class, you must have {@code StdOut.class} in your * Java classpath. If you used our autoinstaller, you should be all set. - * Otherwise, download + * Otherwise, either download + * stdlib.jar + * and add to your Java classpath or download * StdOut.java * and put a copy in your working directory. *

      @@ -62,7 +67,7 @@ public final class StdOut { // force Unicode UTF-8 encoding; otherwise it's system dependent - private static final String CHARSET_NAME = "UTF-8"; + private static final Charset CHARSET = StandardCharsets.UTF_8; // assume language = English, country = US for consistency with StdIn private static final Locale LOCALE = Locale.US; @@ -72,28 +77,12 @@ public final class StdOut { // this is called before invoking any methods static { - try { - out = new PrintWriter(new OutputStreamWriter(System.out, CHARSET_NAME), true); - } - catch (UnsupportedEncodingException e) { - System.out.println(e); - } + out = new PrintWriter(new OutputStreamWriter(System.out, CHARSET), true); } // don't instantiate private StdOut() { } - /** - * Closes standard output. - * @deprecated Calling close() permanently disables standard output; - * subsequent calls to StdOut.println() or System.out.println() - * will no longer produce output on standard output. - */ - @Deprecated - public static void close() { - out.close(); - } - /** * Terminates the current line by printing the line-separator string. */ @@ -193,7 +182,7 @@ public static void print() { /** * Prints an object to standard output and flushes standard output. - * + * * @param x the object to print */ public static void print(Object x) { @@ -203,7 +192,7 @@ public static void print(Object x) { /** * Prints a boolean to standard output and flushes standard output. - * + * * @param x the boolean to print */ public static void print(boolean x) { @@ -213,7 +202,7 @@ public static void print(boolean x) { /** * Prints a character to standard output and flushes standard output. - * + * * @param x the character to print */ public static void print(char x) { @@ -223,7 +212,7 @@ public static void print(char x) { /** * Prints a double to standard output and flushes standard output. - * + * * @param x the double to print */ public static void print(double x) { @@ -233,7 +222,7 @@ public static void print(double x) { /** * Prints a float to standard output and flushes standard output. - * + * * @param x the float to print */ public static void print(float x) { @@ -243,7 +232,7 @@ public static void print(float x) { /** * Prints an integer to standard output and flushes standard output. - * + * * @param x the integer to print */ public static void print(int x) { @@ -253,7 +242,7 @@ public static void print(int x) { /** * Prints a long integer to standard output and flushes standard output. - * + * * @param x the long integer to print */ public static void print(long x) { @@ -263,7 +252,7 @@ public static void print(long x) { /** * Prints a short integer to standard output and flushes standard output. - * + * * @param x the short integer to print */ public static void print(short x) { @@ -308,7 +297,7 @@ public static void printf(Locale locale, String format, Object... args) { } /** - * Unit tests some of the methods in {@code StdOut}. + * Unit tests some methods in {@code StdOut}. * * @param args the command-line arguments */ @@ -324,7 +313,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/StdPicture.java b/src/main/java/edu/princeton/cs/algs4/StdPicture.java new file mode 100644 index 000000000..00adf90fb --- /dev/null +++ b/src/main/java/edu/princeton/cs/algs4/StdPicture.java @@ -0,0 +1,507 @@ +/****************************************************************************** + * Compilation: javac StdPicture.java + * Execution: java StdPicture filename.jpg + * Dependencies: Picture.java + * + ******************************************************************************/ + +package edu.princeton.cs.algs4; + + +/** + * The {@code StdPicture} class provides static methods for manipulating + * the individual pixels of an image using the RGB color model. + * You can either initialize a blank image (of a given dimension) or read an + * image in a supported file format (typically JPEG, PNG, GIF, TIFF, and BMP). + * This class also includes methods for displaying the image in a window + * and saving it to a file. + * + *

      + * Use in the curriculum. + * The {@code StdPicture} class is intended for early usage in the + * curriculum, before objects. + * The {@link Picture} class is an object-oriented version that supports + * manipulating multiple pictures at the same time. + * + *

      + * Getting started. + * To use this class, you must have {@code StdPicture} in your Java classpath. + * Here are three possible ways to do this: + *

        + *
      • If you ran our autoinstaller, use the commands + * {@code javac-introcs} and {@code java-introcs} (or {@code javac-algs4} + * and {@code java-algs4}) when compiling and executing. These commands + * add {@code stdlib.jar} (or {@code algs4.jar}) to the Java classpath, which + * provides access to {@code StdPicture}. + * + *
      • Download stdlib.jar + * (or algs4.jar) + * and add it to the Java classpath. + * + *
      • Download StdPicture.java + * and + * Picture.java + * and put them in the working directory. + *
      + * + *

      + * As a test, cut-and-paste the following short program into your editor: + *

      + *   public class TestStdPicture {
      + *       public static void main(String[] args) {
      + *           StdPicture.read("https://introcs.cs.princeton.edu/java/stdlib/mandrill.jpg");
      + *           StdPicture.show();
      + *       }
      + *   }
      + *  
      + *

      + * If you compile and execute the program, you should see a picture of a mandrill + * (a colorful monkey native to west-central Africa) in a window. + * + *

      + * Anatomy of an image. + * An image is a width-by-height grid of pixels, with pixel (0, 0) + * in the upper-left corner. + * Each pixel has a color that is represented using the RGB color model, + * which specifies the levels of red (R), green (G), and blue (B) + * on an integer scale from 0 to 255. + * + *

      + * anatomy of an image + *
      + * + *

      + * Initializing the picture. + * You can use the following methods to initialize the picture: + *

        + *
      • {@link #read(String filename)} + *
      • {@link #init(int width, int height)} + *
      + *

      + * The first method reads an image in a supported file format + * (typically JPEG, PNG, GIF, TIFF, and BMP) + * and initializes the picture to that image. + * The second method initializes a width-by-height + * picture, with each pixel black. + * + *

      + * Getting and setting the colors of the individual pixels. + * You can use the following methods to retrieve the RGB components + * of a specified pixel: + *

        + *
      • {@link #getRed(int col, int row)} + *
      • {@link #getGreen(int col, int row)} + *
      • {@link #getBlue(int col, int row)} + *
      • {@link #setRGB(int col, int row, int r, int g, int b)} + *
      + *

      + * The first three methods return the red, green, and blue components of pixel + * (col, row). Each component is an integer between 0 and 255, + * with 0 corresponding to the absence of that component and 255 corresponding + * to full intensity of that component. + * The last method sets the red, green, and blue components of pixel + * (col, row) to the specified values. + * + *

      Iterating over the pixels. + * A common operation in image processing is to iterate over and process + * all of the pixels in an image. + * Here is a prototypical example that converts a color image + * to grayscale, using the NTSC formula + * Y = 0.299r + 0.587g + 0.114b. + * Note that if the red, green, and blue components are all equal, + * then the color is a shade of gray. + *

      + *  StdPicture.read("https://introcs.cs.princeton.edu/java/stdlib/mandrill.jpg");
      + *  for (int col = 0; col < StdPicture.width(); col++) {
      + *      for (int row = 0; row < StdPicture.height(); row++) {
      + *          int r = StdPicture.getRed(col, row);
      + *          int g = StdPicture.getGreen(col, row);
      + *          int b = StdPicture.getBlue(col, row);
      + *          int y = (int) (Math.round(0.299*r + 0.587*g + 0.114*b));
      + *          StdPicture.setRGB(col, row, y, y, y);
      + *      }
      + *  }
      + *  StdPicture.show();
      + *  
      + * + *

      Transparency. + * The {@code StdPicture} class supports transparent images, using the + * ARGB color model. The following methods are useful for this: + *

        + *
      • {@link #getAlpha(int col, int row)} + *
      • {@link #setARGB(int col, int row, int a, int r, int g, int b)} + *
      + *

      + * The first method gets the alpha component of pixel (col, row). + * The second methods sets the alpha, red, green, and bluecomponents of + * pixel (col, row). + * The alpha value defines the transparency of a color, with 0 corresponding to + * completely transparent and 255 to completely opaque. If transparency is not + * explicitly used, the alpha value is 255. + * + *

      Saving files. + * The {@code StdPicture} class supports writing images to a supported + * file format (typically JPEG, PNG, GIF, TIFF, and BMP). + * You can save the picture to a file two method: + *

        + *
      • {@link #save(String filename)} + *
      + * + *

      Alternatively, you can save the picture interactively + * by using the menu option File → Save from the picture window. + * + *

      File formats. + * The {@code StdPicture} class supports reading and writing images to any of the + * file formats supported by {@link javax.imageio} (typically JPEG, PNG, + * GIF, TIFF, and BMP). + * The file extensions corresponding to JPEG, PNG, GIF, TIFF, and BMP, + * are {@code .jpg}, {@code .png}, {@code .gif}, {@code .tif}, + * and {@code .bmp}, respectively. + * The file formats JPEG and BMP do not support transparency. + * + *

      Memory usage. + * A W-by-H picture uses ~ 4 W H bytes of memory, + * since the color of each pixel is encoded as a 32-bit int. + * + *

      Additional documentation. + * For additional documentation, see + * Section 3.1 of + * Computer Science: An Interdisciplinary Approach + * by Robert Sedgewick and Kevin Wayne. + * + * @author Robert Sedgewick + * @author Kevin Wayne + */ +public final class StdPicture { + + // the default picture width and height + private static final int DEFAULT_SIZE = 512; + + // the underlying picture + private static Picture picture = new Picture(DEFAULT_SIZE, DEFAULT_SIZE); + + // singleton pattern: client can't instantiate + private StdPicture() { } + + /** + * Initializes a {@code width}-by-{@code height} picture, with {@code width} columns + * and {@code height} rows, where each pixel is black. + * + * @param width the width of the picture + * @param height the height of the picture + * @throws IllegalArgumentException if {@code width} is negative or zero + * @throws IllegalArgumentException if {@code height} is negative or zero + */ + public static void init(int width, int height) { + if (picture.isVisible()) { + hide(); + picture = new Picture(width, height); + show(); + } + else { + picture = new Picture(width, height); + } + } + + /** + * Initializes the picture by reading a JPEG, PNG, GIF, BMP, or TIFF image + * from a file or URL. + * The filetype extension must be {@code .jpg}, {@code .png}, {@code .gif}, + * {@code .bmp}, or {@code .tif}. + * + * @param filename the name of the file or URL + * @throws IllegalArgumentException if cannot read image + * @throws IllegalArgumentException if {@code name} is {@code null} + */ + public static void read(String filename) { + Picture newPicture = new Picture(filename); + + // same dimension, so copy pixels instead of using new Picture and GUI + if (newPicture.width() == picture.width() && newPicture.height() == picture.height()) { + for (int col = 0; col < picture.width(); col++) { + for (int row = 0; row < picture.height(); row++) { + picture.setARGB(col, row, newPicture.getARGB(col, row)); + } + } + } + + // different dimension, so need to use new Picture and GUI + else if (picture.isVisible()) { + hide(); + picture = newPicture; + show(); + } + + // no GUI, so use the new Picture + else { + picture = newPicture; + } + } + + /** + * Is the window containing the picture visible? + * @return {@code true} if the picture is visible, and {@code false} otherwise + */ + public static boolean isVisible() { + return picture.isVisible(); + } + + /** + * Displays the picture in a window on the screen. + */ + public static void show() { + picture.show(); + } + + /** + * Hides the window containing the picture. + */ + public static void hide() { + picture.hide(); + } + + /** + * Pauses for t milliseconds. This method is intended to support computer animation. + * @param t number of milliseconds + * @throws IllegalArgumentException if {@code t} is negative + */ + public static void pause(int t) { + if (t < 0) throw new IllegalArgumentException("argument must be non-negative"); + try { + Thread.sleep(t); + } + catch (InterruptedException e) { + System.out.println("Error sleeping"); + } + } + + /** + * Returns the height of the picture. + * + * @return the height of the picture (in pixels) + */ + public static int height() { + return picture.height(); + } + + /** + * Returns the width of the picture. + * + * @return the width of the picture (in pixels) + */ + public static int width() { + return picture.width(); + } + + /** + * Returns the alpha component of the color of pixel ({@code col}, {@code row}). + * + * @param col the column index + * @param row the row index + * @return the alpha component of the color of pixel ({@code col}, {@code row}) + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + */ + public static int getAlpha(int col, int row) { + int rgb = picture.getARGB(col, row); + return (rgb >> 24) & 0xFF; + } + + /** + * Returns the red component of the color of pixel ({@code col}, {@code row}). + * + * @param col the column index + * @param row the row index + * @return the red component of the color of pixel ({@code col}, {@code row}) + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + */ + public static int getRed(int col, int row) { + int rgb = picture.getARGB(col, row); + return (rgb >> 16) & 0xFF; + } + + /** + * Returns the green component of the color of pixel ({@code col}, {@code row}). + * + * @param col the column index + * @param row the row index + * @return the green component of the color of pixel ({@code col}, {@code row}) + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + */ + public static int getGreen(int col, int row) { + int rgb = picture.getARGB(col, row); + return (rgb >> 8) & 0xFF; + } + + /** + * Returns the blue component of the color of pixel ({@code col}, {@code row}). + * + * @param col the column index + * @param row the row index + * @return the blue component of the color of pixel ({@code col}, {@code row}) + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + */ + public static int getBlue(int col, int row) { + int rgb = picture.getARGB(col, row); + return (rgb >> 0) & 0xFF; + } + + /** + * Returns the ARGB color of pixel ({@code col}, {@code row}). + * + * @param col the column index + * @param row the row index + * @return the ARGB color of pixel ({@code col}, {@code row} as a 32-bit integer + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + */ + public static int getARGB(int col, int row) { + int argb = picture.getARGB(col, row); + return argb; + } + + /** + * Sets the color of pixel ({@code col}, {@code row}) to the given RGB color using + * red, green, and blue components. The alpha component is set to 255 (no transparency). + * + * @param col the column index + * @param row the row index + * @param r the red component of the color + * @param g the green component of the color + * @param b the blue component of the color + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + * @throws IllegalArgumentException unless {@code 0 <= r < 256}, {@code 0 <= g < 256}, + * and {@code 0 <= b < 256}. + */ + public static void setRGB(int col, int row, int r, int g, int b) { + validateComponent(r, "red"); + validateComponent(g, "green"); + validateComponent(b, "blue"); + int a = 255; + int argb = (a << 24) | (r << 16) | (g << 8) | (b << 0); + picture.setARGB(col, row, argb); + } + + /** + * Sets the color of pixel ({@code col}, {@code row}) to the given ARGB color using + * alpha, red, green, and blue components. + * + * @param col the column index + * @param row the row index + * @param a the alpha component of the color + * @param r the red component of the color + * @param g the green component of the color + * @param b the blue component of the color + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + * @throws IllegalArgumentException unless {@code 0 <= a < 256}, {@code 0 <= r < 256}, + * {@code 0 <= g < 256}, and {@code 0 <= b < 256}. + */ + public static void setARGB(int col, int row, int a, int r, int g, int b) { + validateComponent(a, "alpha"); + validateComponent(r, "red"); + validateComponent(g, "green"); + validateComponent(b, "blue"); + + // internally represented using ARGB, not RGBA + int argb = (a << 24) | (r << 16) | (g << 8) | (b << 0); + picture.setARGB(col, row, argb); + } + + /** + * Sets the color of pixel ({@code col}, {@code row}) to the given RGB color. + * + * @param col the column index + * @param row the row index + * @param rgb the RGB color, represented as a 24-bit integer + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + * @throws IllegalArgumentException unless {@code 0 <= rgb < 2^24}. + */ +/* + public static void setRGB(int col, int row, int rgb) { + int a = 255; + int argb = (a << 24) | (rgb); + picture.setARGB(col, row, argb); + } +*/ + + /** + * Sets the color of pixel ({@code col}, {@code row}) to the given ARGB color. + * + * @param col the column index + * @param row the row index + * @param argb the ARGB color, represented as a 32-bit integer + * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} + */ + public static void setARGB(int col, int row, int argb) { + picture.setARGB(col, row, argb); + } + + + /** + * Sets the title of this picture. + * @param title the title + * @throws IllegalArgumentException if {@code title} is {@code null} + */ + public static void setTitle(String title) { + picture.setTitle(title); + } + + /** + * Saves the picture to a file in a supported file format + * (typically JPEG, PNG, GIF, TIFF, and BMP). + * The filetype extension must be {@code .jpg}, {@code .png}, {@code .gif}, + * {@code .bmp}, or {@code .tif}. + * If the file format does not support transparency (such as JPEG + * or BMP), it will be converted to be opaque (with purely + * transparent pixels converted to black). + * + * @param filename the name of the file + * @throws IllegalArgumentException if {@code filename} is {@code null} + * @throws IllegalArgumentException if {@code filename} is the empty string + */ + public static void save(String filename) { + picture.save(filename); + } + + private static void validateComponent(int val, String name) { + if (val < 0 || val >= 256) { + throw new IllegalArgumentException(name + " must be between 0 and 255"); + } + } + + /** + * Unit tests this {@code StdPicture} data type. + * Reads a picture specified by the command-line argument, + * and shows it in a window on the screen. + * + * @param args the command-line arguments + */ + public static void main(String[] args) { + StdPicture.read(args[0]); + System.out.printf("%d-by-%d\n", picture.width(), picture.height()); + StdPicture.show(); + } + +} + +/****************************************************************************** + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. + * + * This file is part of algs4.jar, which accompanies the textbook + * + * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, + * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. + * http://algs4.cs.princeton.edu + * + * + * algs4.jar is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * algs4.jar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with algs4.jar. If not, see http://www.gnu.org/licenses. + ******************************************************************************/ diff --git a/src/main/java/edu/princeton/cs/algs4/StdRandom.java b/src/main/java/edu/princeton/cs/algs4/StdRandom.java index aaf03281c..56a4975f3 100644 --- a/src/main/java/edu/princeton/cs/algs4/StdRandom.java +++ b/src/main/java/edu/princeton/cs/algs4/StdRandom.java @@ -10,27 +10,27 @@ * * % java StdRandom 5 * seed = 1316600602069 - * 59 16.81826 true 8.83954 0 - * 32 91.32098 true 9.11026 0 - * 35 10.11874 true 8.95396 3 - * 92 32.88401 true 8.87089 0 - * 72 92.55791 true 9.46241 0 + * 59 16.81826 true 8.83954 0 + * 32 91.32098 true 9.11026 0 + * 35 10.11874 true 8.95396 3 + * 92 32.88401 true 8.87089 0 + * 72 92.55791 true 9.46241 0 * * % java StdRandom 5 * seed = 1316600616575 - * 96 60.17070 true 8.72821 0 - * 79 32.01607 true 8.58159 0 - * 81 59.49065 true 9.10423 1 - * 96 51.65818 true 9.02102 0 - * 99 17.55771 true 8.99762 0 + * 96 60.17070 true 8.72821 0 + * 79 32.01607 true 8.58159 0 + * 81 59.49065 true 9.10423 1 + * 96 51.65818 true 9.02102 0 + * 99 17.55771 true 8.99762 0 * * % java StdRandom 5 1316600616575 * seed = 1316600616575 - * 96 60.17070 true 8.72821 0 - * 79 32.01607 true 8.58159 0 - * 81 59.49065 true 9.10423 1 - * 96 51.65818 true 9.02102 0 - * 99 17.55771 true 8.99762 0 + * 96 60.17070 true 8.72821 0 + * 79 32.01607 true 8.58159 0 + * 81 59.49065 true 9.10423 1 + * 96 51.65818 true 9.02102 0 + * 99 17.55771 true 8.99762 0 * * * Remark @@ -51,18 +51,26 @@ /** * The {@code StdRandom} class provides static methods for generating - * random number from various discrete and continuous distributions, + * random number from various discrete and continuous distributions, * including uniform, Bernoulli, geometric, Gaussian, exponential, Pareto, * Poisson, and Cauchy. It also provides method for shuffling an * array or subarray and generating random permutations. - *

      + * + *

      Conventions. * By convention, all intervals are half open. For example, - * uniform(-1.0, 1.0) returns a random number between + * uniformDouble(-1.0, 1.0) returns a random number between * -1.0 (inclusive) and 1.0 (exclusive). * Similarly, shuffle(a, lo, hi) shuffles the hi - lo * elements in the array a[], starting at index lo * (inclusive) and ending at index hi (exclusive). - *

      + * + *

      Performance. + * The methods all take constant expected time, except those that involve arrays. + * The shuffle method takes time linear in the subarray to be shuffled; + * the discrete methods take time linear in the length of the argument + * array. + * + *

      Additional information. * For additional documentation, * see Section 2.2 of * Computer Science: An Interdisciplinary Approach @@ -112,32 +120,68 @@ public static long getSeed() { * Returns a random real number uniformly in [0, 1). * * @return a random real number uniformly in [0, 1) + * @deprecated Replaced by {@link #uniformDouble()}. */ + @Deprecated public static double uniform() { + return uniformDouble(); + } + + /** + * Returns a random real number uniformly in [0, 1). + * + * @return a random real number uniformly in [0, 1) + */ + public static double uniformDouble() { return random.nextDouble(); } /** * Returns a random integer uniformly in [0, n). - * + * * @param n number of possible integers * @return a random integer uniformly between 0 (inclusive) and {@code n} (exclusive) * @throws IllegalArgumentException if {@code n <= 0} + * @deprecated Replaced by {@link #uniformInt(int n)}. */ + @Deprecated public static int uniform(int n) { + return uniformInt(n); + } + + /** + * Returns a random integer uniformly in [0, n). + * + * @param n number of possible integers + * @return a random integer uniformly between 0 (inclusive) and {@code n} (exclusive) + * @throws IllegalArgumentException if {@code n <= 0} + */ + public static int uniformInt(int n) { if (n <= 0) throw new IllegalArgumentException("argument must be positive: " + n); return random.nextInt(n); } - /** * Returns a random long integer uniformly in [0, n). - * + * * @param n number of possible {@code long} integers * @return a random long integer uniformly between 0 (inclusive) and {@code n} (exclusive) * @throws IllegalArgumentException if {@code n <= 0} + * @deprecated Replaced by {@link #uniformLong(long n)}. */ + @Deprecated public static long uniform(long n) { + return uniformLong(n); + } + + /** + * Returns a random long integer uniformly in [0, n). + * + * @param n number of possible {@code long} integers + * @return a random long integer uniformly between 0 (inclusive) and {@code n} (exclusive) + * @throws IllegalArgumentException if {@code n <= 0} + */ + public static long uniformLong(long n) { if (n <= 0L) throw new IllegalArgumentException("argument must be positive: " + n); // https://docs.oracle.com/javase/8/docs/api/java/util/Random.html#longs-long-long-long- @@ -164,25 +208,40 @@ public static long uniform(long n) { /** * Returns a random real number uniformly in [0, 1). - * + * * @return a random real number uniformly in [0, 1) - * @deprecated Replaced by {@link #uniform()}. + * @deprecated Replaced by {@link #uniformDouble()}. */ @Deprecated public static double random() { - return uniform(); + return uniformDouble(); } /** * Returns a random integer uniformly in [a, b). - * + * * @param a the left endpoint * @param b the right endpoint * @return a random integer uniformly in [a, b) * @throws IllegalArgumentException if {@code b <= a} * @throws IllegalArgumentException if {@code b - a >= Integer.MAX_VALUE} + * @deprecated Replaced by {@link #uniformInt(int a, int b)}. */ + @Deprecated public static int uniform(int a, int b) { + return uniformInt(a, b); + } + + /** + * Returns a random integer uniformly in [a, b). + * + * @param a the left endpoint + * @param b the right endpoint + * @return a random integer uniformly in [a, b) + * @throws IllegalArgumentException if {@code b <= a} + * @throws IllegalArgumentException if {@code b - a >= Integer.MAX_VALUE} + */ + public static int uniformInt(int a, int b) { if ((b <= a) || ((long) b - a >= Integer.MAX_VALUE)) { throw new IllegalArgumentException("invalid range: [" + a + ", " + b + ")"); } @@ -191,13 +250,27 @@ public static int uniform(int a, int b) { /** * Returns a random real number uniformly in [a, b). - * + * * @param a the left endpoint * @param b the right endpoint * @return a random real number uniformly in [a, b) * @throws IllegalArgumentException unless {@code a < b} + * @deprecated Replaced by {@link #uniformDouble(double a, double b)}. */ + @Deprecated public static double uniform(double a, double b) { + return uniformDouble(a, b); + } + + /** + * Returns a random real number uniformly in [a, b). + * + * @param a the left endpoint + * @param b the right endpoint + * @return a random real number uniformly in [a, b) + * @throws IllegalArgumentException unless {@code a < b} + */ + public static double uniformDouble(double a, double b) { if (!(a < b)) { throw new IllegalArgumentException("invalid range: [" + a + ", " + b + ")"); } @@ -216,13 +289,13 @@ public static double uniform(double a, double b) { public static boolean bernoulli(double p) { if (!(p >= 0.0 && p <= 1.0)) throw new IllegalArgumentException("probability p must be between 0.0 and 1.0: " + p); - return uniform() < p; + return uniformDouble() < p; } /** * Returns a random boolean from a Bernoulli distribution with success * probability 1/2. - * + * * @return {@code true} with probability 1/2 and * {@code false} with probability 1/2 */ @@ -232,7 +305,7 @@ public static boolean bernoulli() { /** * Returns a random real number from a standard Gaussian distribution. - * + * * @return a random real number from a standard Gaussian distribution * (mean 0 and standard deviation 1). */ @@ -240,8 +313,8 @@ public static double gaussian() { // use the polar form of the Box-Muller transform double r, x, y; do { - x = uniform(-1.0, 1.0); - y = uniform(-1.0, 1.0); + x = uniformDouble(-1.0, 1.0); + y = uniformDouble(-1.0, 1.0); r = x*x + y*y; } while (r >= 1 || r == 0); return x * Math.sqrt(-2 * Math.log(r) / r); @@ -253,7 +326,7 @@ public static double gaussian() { /** * Returns a random real number from a Gaussian distribution with mean μ * and standard deviation σ. - * + * * @param mu the mean * @param sigma the standard deviation * @return a real number distributed according to the Gaussian distribution @@ -268,7 +341,7 @@ public static double gaussian(double mu, double sigma) { * probability p. * The integer represents the number of independent trials * before the first success. - * + * * @param p the parameter of the geometric distribution * @return a random integer from a geometric distribution with success * probability {@code p}; or {@code Integer.MAX_VALUE} if @@ -283,7 +356,7 @@ public static int geometric(double p) { throw new IllegalArgumentException("probability p must not be larger than 1: " + p); } // using algorithm given by Knuth - return (int) Math.ceil(Math.log(uniform()) / Math.log(1.0 - p)); + return (int) Math.ceil(Math.log(uniformDouble()) / Math.log(1.0 - p)); } /** @@ -305,7 +378,7 @@ public static int poisson(double lambda) { double expLambda = Math.exp(-lambda); do { k++; - p *= uniform(); + p *= uniformDouble(); } while (p >= expLambda); return k-1; } @@ -331,7 +404,7 @@ public static double pareto() { public static double pareto(double alpha) { if (!(alpha > 0.0)) throw new IllegalArgumentException("alpha must be positive: " + alpha); - return Math.pow(1 - uniform(), -1.0/alpha) - 1.0; + return Math.pow(1 - uniformDouble(), -1.0 / alpha) - 1.0; } /** @@ -340,7 +413,7 @@ public static double pareto(double alpha) { * @return a random real number from the Cauchy distribution. */ public static double cauchy() { - return Math.tan(Math.PI * (uniform() - 0.5)); + return Math.tan(Math.PI * (uniformDouble() - 0.5)); } /** @@ -354,12 +427,12 @@ public static double cauchy() { * @throws IllegalArgumentException unless {@code probabilities[i] >= 0.0} for each index {@code i} */ public static int discrete(double[] probabilities) { - if (probabilities == null) throw new IllegalArgumentException("argument array is null"); - double EPSILON = 1E-14; + if (probabilities == null) throw new IllegalArgumentException("argument array must not be null"); + double EPSILON = 1.0E-14; double sum = 0.0; for (int i = 0; i < probabilities.length; i++) { if (!(probabilities[i] >= 0.0)) - throw new IllegalArgumentException("array entry " + i + " must be nonnegative: " + probabilities[i]); + throw new IllegalArgumentException("array entry " + i + " must be non-negative: " + probabilities[i]); sum += probabilities[i]; } if (sum > 1.0 + EPSILON || sum < 1.0 - EPSILON) @@ -368,7 +441,7 @@ public static int discrete(double[] probabilities) { // the for loop may not return a value when both r is (nearly) 1.0 and when the // cumulative sum is less than 1.0 (as a result of floating-point roundoff error) while (true) { - double r = uniform(); + double r = uniformDouble(); sum = 0.0; for (int i = 0; i < probabilities.length; i++) { sum = sum + probabilities[i]; @@ -389,11 +462,11 @@ public static int discrete(double[] probabilities) { * @throws IllegalArgumentException if sum of frequencies exceeds {@code Integer.MAX_VALUE} (231 - 1) */ public static int discrete(int[] frequencies) { - if (frequencies == null) throw new IllegalArgumentException("argument array is null"); + if (frequencies == null) throw new IllegalArgumentException("argument array must not be null"); long sum = 0; for (int i = 0; i < frequencies.length; i++) { if (frequencies[i] < 0) - throw new IllegalArgumentException("array entry " + i + " must be nonnegative: " + frequencies[i]); + throw new IllegalArgumentException("array entry " + i + " must be non-negative: " + frequencies[i]); sum += frequencies[i]; } if (sum == 0) @@ -401,8 +474,8 @@ public static int discrete(int[] frequencies) { if (sum >= Integer.MAX_VALUE) throw new IllegalArgumentException("sum of frequencies overflows an int"); - // pick index i with probabilitity proportional to frequency - double r = uniform((int) sum); + // pick index i with probability proportional to frequency + double r = uniformInt((int) sum); sum = 0; for (int i = 0; i < frequencies.length; i++) { sum += frequencies[i]; @@ -417,16 +490,31 @@ public static int discrete(int[] frequencies) { /** * Returns a random real number from an exponential distribution * with rate λ. - * + * * @param lambda the rate of the exponential distribution * @return a random real number from an exponential distribution with * rate {@code lambda} * @throws IllegalArgumentException unless {@code lambda > 0.0} */ - public static double exp(double lambda) { + public static double exponential(double lambda) { if (!(lambda > 0.0)) throw new IllegalArgumentException("lambda must be positive: " + lambda); - return -Math.log(1 - uniform()) / lambda; + return -Math.log(1 - uniformDouble()) / lambda; + } + + /** + * Returns a random real number from an exponential distribution + * with rate λ. + * + * @param lambda the rate of the exponential distribution + * @return a random real number from an exponential distribution with + * rate {@code lambda} + * @throws IllegalArgumentException unless {@code lambda > 0.0} + * @deprecated Replaced by {@link #exponential(double)}. + */ + @Deprecated + public static double exp(double lambda) { + return exponential(lambda); } /** @@ -439,7 +527,7 @@ public static void shuffle(Object[] a) { validateNotNull(a); int n = a.length; for (int i = 0; i < n; i++) { - int r = i + uniform(n-i); // between i and n-1 + int r = i + uniformInt(n-i); // between i and n-1 Object temp = a[i]; a[i] = a[r]; a[r] = temp; @@ -456,7 +544,7 @@ public static void shuffle(double[] a) { validateNotNull(a); int n = a.length; for (int i = 0; i < n; i++) { - int r = i + uniform(n-i); // between i and n-1 + int r = i + uniformInt(n-i); // between i and n-1 double temp = a[i]; a[i] = a[r]; a[r] = temp; @@ -473,7 +561,7 @@ public static void shuffle(int[] a) { validateNotNull(a); int n = a.length; for (int i = 0; i < n; i++) { - int r = i + uniform(n-i); // between i and n-1 + int r = i + uniformInt(n-i); // between i and n-1 int temp = a[i]; a[i] = a[r]; a[r] = temp; @@ -490,7 +578,7 @@ public static void shuffle(char[] a) { validateNotNull(a); int n = a.length; for (int i = 0; i < n; i++) { - int r = i + uniform(n-i); // between i and n-1 + int r = i + uniformInt(n-i); // between i and n-1 char temp = a[i]; a[i] = a[r]; a[r] = temp; @@ -505,14 +593,14 @@ public static void shuffle(char[] a) { * @param hi the right endpoint (exclusive) * @throws IllegalArgumentException if {@code a} is {@code null} * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)} - * + * */ public static void shuffle(Object[] a, int lo, int hi) { validateNotNull(a); validateSubarrayIndices(lo, hi, a.length); for (int i = lo; i < hi; i++) { - int r = i + uniform(hi-i); // between i and hi-1 + int r = i + uniformInt(hi-i); // between i and hi-1 Object temp = a[i]; a[i] = a[r]; a[r] = temp; @@ -533,7 +621,7 @@ public static void shuffle(double[] a, int lo, int hi) { validateSubarrayIndices(lo, hi, a.length); for (int i = lo; i < hi; i++) { - int r = i + uniform(hi-i); // between i and hi-1 + int r = i + uniformInt(hi-i); // between i and hi-1 double temp = a[i]; a[i] = a[r]; a[r] = temp; @@ -554,7 +642,7 @@ public static void shuffle(int[] a, int lo, int hi) { validateSubarrayIndices(lo, hi, a.length); for (int i = lo; i < hi; i++) { - int r = i + uniform(hi-i); // between i and hi-1 + int r = i + uniformInt(hi-i); // between i and hi-1 int temp = a[i]; a[i] = a[r]; a[r] = temp; @@ -570,7 +658,7 @@ public static void shuffle(int[] a, int lo, int hi) { * of {@code 0}, {@code 1}, ..., {@code n-1} */ public static int[] permutation(int n) { - if (n < 0) throw new IllegalArgumentException("argument is negative"); + if (n < 0) throw new IllegalArgumentException("n must be non-negative: " + n); int[] perm = new int[n]; for (int i = 0; i < n; i++) perm[i] = i; @@ -589,16 +677,16 @@ public static int[] permutation(int n) { * of {@code k} of the elements from {@code 0}, {@code 1}, ..., {@code n-1} */ public static int[] permutation(int n, int k) { - if (n < 0) throw new IllegalArgumentException("argument is negative"); - if (k < 0 || k > n) throw new IllegalArgumentException("k must be between 0 and n"); + if (n < 0) throw new IllegalArgumentException("n must be non-negative: " + n); + if (k < 0 || k > n) throw new IllegalArgumentException("k must be between 0 and n: " + k); int[] perm = new int[k]; for (int i = 0; i < k; i++) { - int r = uniform(i+1); // between 0 and i + int r = uniformInt(i+1); // between 0 and i perm[i] = perm[r]; perm[r] = i; } for (int i = k; i < n; i++) { - int r = uniform(i+1); // between 0 and i + int r = uniformInt(i+1); // between 0 and i if (r < k) perm[r] = i; } return perm; @@ -608,7 +696,7 @@ public static int[] permutation(int n, int k) { // (x can be of type Object[], double[], int[], ...) private static void validateNotNull(Object x) { if (x == null) { - throw new IllegalArgumentException("argument is null"); + throw new IllegalArgumentException("argument must not be null"); } } @@ -633,13 +721,13 @@ public static void main(String[] args) { StdOut.println("seed = " + StdRandom.getSeed()); for (int i = 0; i < n; i++) { - StdOut.printf("%2d ", uniform(100)); - StdOut.printf("%8.5f ", uniform(10.0, 99.0)); + StdOut.printf("%2d ", uniformInt(100)); + StdOut.printf("%8.5f ", uniformDouble(10.0, 99.0)); StdOut.printf("%5b ", bernoulli(0.5)); StdOut.printf("%7.5f ", gaussian(9.0, 0.2)); StdOut.printf("%1d ", discrete(probabilities)); StdOut.printf("%1d ", discrete(frequencies)); - StdOut.printf("%11d ", uniform(100000000000L)); + StdOut.printf("%11d ", uniformLong(100000000000L)); StdRandom.shuffle(a); for (String s : a) StdOut.print(s); @@ -650,7 +738,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/StdStats.java b/src/main/java/edu/princeton/cs/algs4/StdStats.java index 9907a8e23..0504f120a 100644 --- a/src/main/java/edu/princeton/cs/algs4/StdStats.java +++ b/src/main/java/edu/princeton/cs/algs4/StdStats.java @@ -27,7 +27,7 @@ * max 5.000 * std dev 1.581 * - * Should these funtions use varargs instead of array arguments? + * Should these functions use varargs instead of array arguments? * ******************************************************************************/ @@ -134,7 +134,7 @@ public static double min(double[] a) { * @param hi the right endpoint of the subarray (exclusive) * @return the maximum value in the subarray {@code a[lo..hi)}; * {@code Double.POSITIVE_INFINITY} if no such value - * @throws IllegalArgumentException if {@code a} is {@code null} + * @throws IllegalArgumentException if {@code a} is {@code null} * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)} */ public static double min(double[] a, int lo, int hi) { @@ -189,7 +189,7 @@ public static double mean(double[] a) { * @param hi the right endpoint of the subarray (exclusive) * @return the average value in the subarray {@code a[lo..hi)}; * {@code Double.NaN} if no such value - * @throws IllegalArgumentException if {@code a} is {@code null} + * @throws IllegalArgumentException if {@code a} is {@code null} * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)} */ public static double mean(double[] a, int lo, int hi) { @@ -245,7 +245,7 @@ public static double var(double[] a) { * @param hi the right endpoint of the subarray (exclusive) * @return the sample variance in the subarray {@code a[lo..hi)}; * {@code Double.NaN} if no such value - * @throws IllegalArgumentException if {@code a} is {@code null} + * @throws IllegalArgumentException if {@code a} is {@code null} * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)} */ public static double var(double[] a, int lo, int hi) { @@ -307,7 +307,7 @@ public static double varp(double[] a) { * @param hi the right endpoint of the subarray (exclusive) * @return the population variance in the subarray {@code a[lo..hi)}; * {@code Double.NaN} if no such value - * @throws IllegalArgumentException if {@code a} is {@code null} + * @throws IllegalArgumentException if {@code a} is {@code null} * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)} */ public static double varp(double[] a, int lo, int hi) { @@ -357,7 +357,7 @@ public static double stddev(int[] a) { * @param hi the right endpoint of the subarray (exclusive) * @return the sample standard deviation in the subarray {@code a[lo..hi)}; * {@code Double.NaN} if no such value - * @throws IllegalArgumentException if {@code a} is {@code null} + * @throws IllegalArgumentException if {@code a} is {@code null} * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)} */ public static double stddev(double[] a, int lo, int hi) { @@ -388,7 +388,7 @@ public static double stddevp(double[] a) { * @param hi the right endpoint of the subarray (exclusive) * @return the population standard deviation in the subarray {@code a[lo..hi)}; * {@code Double.NaN} if no such value - * @throws IllegalArgumentException if {@code a} is {@code null} + * @throws IllegalArgumentException if {@code a} is {@code null} * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)} */ public static double stddevp(double[] a, int lo, int hi) { @@ -422,7 +422,7 @@ private static double sum(double[] a) { * @param hi the right endpoint of the subarray (exclusive) * @return the sum of all values in the subarray {@code a[lo..hi)}; * {@code 0.0} if no such value - * @throws IllegalArgumentException if {@code a} is {@code null} + * @throws IllegalArgumentException if {@code a} is {@code null} * @throws IllegalArgumentException unless {@code (0 <= lo) && (lo < hi) && (hi <= a.length)} */ private static double sum(double[] a, int lo, int hi) { @@ -454,7 +454,7 @@ private static int sum(int[] a) { /** * Plots the points (0, a0), (1, a1), ..., - * (n–1, an–1) to standard draw. + * (n-1, an-1) to standard draw. * * @param a the array of values */ @@ -469,9 +469,9 @@ public static void plotPoints(double[] a) { } /** - * Plots the line segments connecting + * Plots the line segments connecting * (i, ai) to - * (i+1, ai+1) for + * (i+1, ai+1) for * each i to standard draw. * * @param a the array of values @@ -535,7 +535,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Stopwatch.java b/src/main/java/edu/princeton/cs/algs4/Stopwatch.java index 4a069f9dc..61e9afcc5 100644 --- a/src/main/java/edu/princeton/cs/algs4/Stopwatch.java +++ b/src/main/java/edu/princeton/cs/algs4/Stopwatch.java @@ -18,6 +18,20 @@ * the time that elapses between the start and end of a * programming task (wall-clock time). * + * To measure the running time of a code fragment, construct a + * Stopwatch object, execute the code you want to time, + * and then call the elapsedTime() method to get the + * elapsed time in seconds. + *

      + *
      + *   Stopwatch stopwatch = new Stopwatch();
      + *
      + *   // do some computationally intensive calculation here
      + *
      + *   double time = stopwatch.elapsedTime();
      + *  
      + *

      + * * See {@link StopwatchCPU} for a version that measures CPU time. * For additional documentation, * see Section 1.4 of @@ -28,7 +42,7 @@ */ -public class Stopwatch { +public class Stopwatch { private final long start; @@ -37,7 +51,7 @@ public class Stopwatch { */ public Stopwatch() { start = System.currentTimeMillis(); - } + } /** @@ -50,10 +64,10 @@ public double elapsedTime() { return (now - start) / 1000.0; } - + /** * Unit tests the {@code Stopwatch} data type. - * Takes a command-line argument {@code n} and computes the + * Takes a command-line argument {@code n} and computes the * sum of the square roots of the first {@code n} positive integers, * first using {@code Math.sqrt()}, then using {@code Math.pow()}. * It prints to standard output the sum and the amount of time to @@ -83,10 +97,10 @@ public static void main(String[] args) { double time2 = timer2.elapsedTime(); StdOut.printf("%e (%.2f seconds)\n", sum2, time2); } -} +} /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/StopwatchCPU.java b/src/main/java/edu/princeton/cs/algs4/StopwatchCPU.java index 2de851945..723c24ca3 100644 --- a/src/main/java/edu/princeton/cs/algs4/StopwatchCPU.java +++ b/src/main/java/edu/princeton/cs/algs4/StopwatchCPU.java @@ -1,6 +1,6 @@ /****************************************************************************** * Compilation: javac StopwatchCPU.java - * Execution: java StopwtachCPU n + * Execution: java StopwatchCPU n * Dependencies: none * * A version of Stopwatch.java that measures CPU time on a single @@ -34,15 +34,15 @@ public class StopwatchCPU { private final ThreadMXBean threadTimer; private final long start; - + /** * Initializes a new stopwatch. */ - public StopwatchCPU() { + public StopwatchCPU() { threadTimer = ManagementFactory.getThreadMXBean(); start = threadTimer.getCurrentThreadCpuTime(); - } - + } + /** * Returns the elapsed CPU time (in seconds) since the stopwatch was created. * @@ -55,7 +55,7 @@ public double elapsedTime() { /** * Unit tests the {@code StopwatchCPU} data type. - * Takes a command-line argument {@code n} and computes the + * Takes a command-line argument {@code n} and computes the * sum of the square roots of the first {@code n} positive integers, * first using {@code Math.sqrt()}, then using {@code Math.pow()}. * It prints to standard output the sum and the amount of time to @@ -88,7 +88,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/SuffixArray.java b/src/main/java/edu/princeton/cs/algs4/SuffixArray.java index 4ff643b8f..491a406f6 100644 --- a/src/main/java/edu/princeton/cs/algs4/SuffixArray.java +++ b/src/main/java/edu/princeton/cs/algs4/SuffixArray.java @@ -44,7 +44,7 @@ * This implementation uses a nested class {@code Suffix} to represent * a suffix of a string (using constant time and space) and * {@code Arrays.sort()} to sort the array of suffixes. - * The index and length operations takes constant time + * The index and length operations takes constant time * in the worst case. The lcp operation takes time proportional to the * length of the longest common prefix. * The select operation takes time proportional @@ -221,7 +221,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/SuffixArrayX.java b/src/main/java/edu/princeton/cs/algs4/SuffixArrayX.java index ae42a63ea..809fd3c53 100644 --- a/src/main/java/edu/princeton/cs/algs4/SuffixArrayX.java +++ b/src/main/java/edu/princeton/cs/algs4/SuffixArrayX.java @@ -3,11 +3,11 @@ * Execution: java SuffixArrayX < input.txt * Dependencies: StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/63suffix/abra.txt - * + * * A data type that computes the suffix array of a string using 3-way * radix quicksort. * - * % java SuffixArrayX < abra.txt + * % java SuffixArrayX < abra.txt * i ind lcp rnk select * --------------------------- * 0 11 - 0 ! @@ -47,13 +47,13 @@ * The select operation takes time proportional * to the length of the suffix and should be used primarily for debugging. *

      - * This implementation uses '\0' as a sentinel and assumes that the charater + * This implementation uses '\0' as a sentinel and assumes that the character * '\0' does not appear in the text. *

      * In practice, this algorithm runs very fast. However, in the worst-case * it can be very poor (e.g., a string consisting of N copies of the same - * character. We do not shuffle the array of suffixes before sorting because - * shuffling is relatively expensive and a pathologial input for which + * character). We do not shuffle the array of suffixes before sorting because + * shuffling is relatively expensive and a pathological input for which * the suffixes start out in a bad order (e.g., sorted) is likely to be * a bad input for this algorithm with or without the shuffle. *

      @@ -83,7 +83,7 @@ public SuffixArrayX(String text) { } // 3-way string quicksort lo..hi starting at dth character - private void sort(int lo, int hi, int d) { + private void sort(int lo, int hi, int d) { // cutoff to insertion sort for small subarrays if (hi <= lo + CUTOFF) { @@ -101,7 +101,7 @@ private void sort(int lo, int hi, int d) { else i++; } - // a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]. + // a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]. sort(lo, lt-1, d); if (v > 0) sort(lt, gt, d+1); sort(gt+1, hi, d); @@ -195,7 +195,7 @@ public String select(int i) { /** * Returns the number of suffixes strictly less than the {@code query} string. * We note that {@code rank(select(i))} equals {@code i} for each {@code i} - * between 0 and n-1. + * between 0 and n-1. * @param query the query string * @return the number of suffixes strictly less than {@code query} */ @@ -209,7 +209,7 @@ public int rank(String query) { else return mid; } return lo; - } + } // is query < text[i..n) ? private int compare(String query, int i) { @@ -228,7 +228,7 @@ private int compare(String query, int i) { /** - * Unit tests the {@code SuffixArrayx} data type. + * Unit tests the {@code SuffixArrayX} data type. * * @param args the command-line arguments */ @@ -271,7 +271,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/SymbolDigraph.java b/src/main/java/edu/princeton/cs/algs4/SymbolDigraph.java index b4164315e..fee0279de 100644 --- a/src/main/java/edu/princeton/cs/algs4/SymbolDigraph.java +++ b/src/main/java/edu/princeton/cs/algs4/SymbolDigraph.java @@ -3,7 +3,7 @@ * Execution: java SymbolDigraph * Dependencies: ST.java Digraph.java In.java * Data files: https://algs4.cs.princeton.edu/42digraph/routes.txt - * + * * % java SymbolDigraph routes.txt " " * JFK * MCO @@ -30,7 +30,7 @@ * This implementation uses an {@link ST} to map from strings to integers, * an array to map from integers to strings, and a {@link Digraph} to store * the underlying graph. - * The indexOf and contains operations take time + * The indexOf and contains operations take time * proportional to log V, where V is the number of vertices. * The nameOf operation takes constant time. *

      @@ -45,7 +45,7 @@ public class SymbolDigraph { private String[] keys; // index -> string private Digraph graph; // the underlying digraph - /** + /** * Initializes a digraph from a file using the specified delimiter. * Each line in the file contains * the name of a vertex, followed by a list of the names @@ -118,7 +118,7 @@ public int indexOf(String s) { /** * Returns the name of the vertex associated with the integer {@code v}. - * @param v the integer corresponding to a vertex (between 0 and V - 1) + * @param v the integer corresponding to a vertex (between 0 and V - 1) * @return the name of the vertex associated with the integer {@code v} * @throws IllegalArgumentException unless {@code 0 <= v < V} * @deprecated Replaced by {@link #nameOf(int)}. @@ -131,7 +131,7 @@ public String name(int v) { /** * Returns the name of the vertex associated with the integer {@code v}. - * @param v the integer corresponding to a vertex (between 0 and V - 1) + * @param v the integer corresponding to a vertex (between 0 and V - 1) * @return the name of the vertex associated with the integer {@code v} * @throws IllegalArgumentException unless {@code 0 <= v < V} */ @@ -141,7 +141,7 @@ public String nameOf(int v) { } /** - * Returns the digraph assoicated with the symbol graph. It is the client's responsibility + * Returns the digraph associated with the symbol graph. It is the client's responsibility * not to mutate the digraph. * * @return the digraph associated with the symbol digraph @@ -153,7 +153,7 @@ public Digraph G() { } /** - * Returns the digraph assoicated with the symbol graph. It is the client's responsibility + * Returns the digraph associated with the symbol graph. It is the client's responsibility * not to mutate the digraph. * * @return the digraph associated with the symbol digraph @@ -182,14 +182,14 @@ public static void main(String[] args) { while (!StdIn.isEmpty()) { String t = StdIn.readLine(); for (int v : graph.adj(sg.index(t))) { - StdOut.println(" " + sg.name(v)); + StdOut.println(" " + sg.nameOf(v)); } } } } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/SymbolGraph.java b/src/main/java/edu/princeton/cs/algs4/SymbolGraph.java index 789bb318b..ca54a3394 100644 --- a/src/main/java/edu/princeton/cs/algs4/SymbolGraph.java +++ b/src/main/java/edu/princeton/cs/algs4/SymbolGraph.java @@ -7,7 +7,7 @@ * https://algs4.cs.princeton.edu/41graph/moviestiny.txt * https://algs4.cs.princeton.edu/41graph/moviesG.txt * https://algs4.cs.princeton.edu/41graph/moviestopGrossing.txt - * + * * % java SymbolGraph routes.txt " " * JFK * MCO @@ -35,7 +35,7 @@ * Apollo 13 (1995) * Animal House (1978) * - * + * * Assumes that input file is encoded using UTF-8. * % iconv -f ISO-8859-1 -t UTF-8 movies-iso8859.txt > movies.txt * @@ -55,7 +55,7 @@ * This implementation uses an {@link ST} to map from strings to integers, * an array to map from integers to strings, and a {@link Graph} to store * the underlying graph. - * The indexOf and contains operations take time + * The indexOf and contains operations take time * proportional to log V, where V is the number of vertices. * The nameOf operation takes constant time. *

      @@ -70,7 +70,7 @@ public class SymbolGraph { private String[] keys; // index -> string private Graph graph; // the underlying graph - /** + /** * Initializes a graph from a file using the specified delimiter. * Each line in the file contains * the name of a vertex, followed by a list of the names @@ -145,7 +145,7 @@ public int indexOf(String s) { /** * Returns the name of the vertex associated with the integer {@code v}. - * @param v the integer corresponding to a vertex (between 0 and V - 1) + * @param v the integer corresponding to a vertex (between 0 and V - 1) * @return the name of the vertex associated with the integer {@code v} * @throws IllegalArgumentException unless {@code 0 <= v < V} * @deprecated Replaced by {@link #nameOf(int)}. @@ -158,7 +158,7 @@ public String name(int v) { /** * Returns the name of the vertex associated with the integer {@code v}. - * @param v the integer corresponding to a vertex (between 0 and V - 1) + * @param v the integer corresponding to a vertex (between 0 and V - 1) * @throws IllegalArgumentException unless {@code 0 <= v < V} * @return the name of the vertex associated with the integer {@code v} */ @@ -168,7 +168,7 @@ public String nameOf(int v) { } /** - * Returns the graph assoicated with the symbol graph. It is the client's responsibility + * Returns the graph associated with the symbol graph. It is the client's responsibility * not to mutate the graph. * @return the graph associated with the symbol graph * @deprecated Replaced by {@link #graph()}. @@ -179,7 +179,7 @@ public Graph G() { } /** - * Returns the graph assoicated with the symbol graph. It is the client's responsibility + * Returns the graph associated with the symbol graph. It is the client's responsibility * not to mutate the graph. * @return the graph associated with the symbol graph */ @@ -210,7 +210,7 @@ public static void main(String[] args) { if (sg.contains(source)) { int s = sg.index(source); for (int v : graph.adj(s)) { - StdOut.println(" " + sg.name(v)); + StdOut.println(" " + sg.nameOf(v)); } } else { @@ -221,7 +221,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/TST.java b/src/main/java/edu/princeton/cs/algs4/TST.java index 100301c60..c538c302e 100644 --- a/src/main/java/edu/princeton/cs/algs4/TST.java +++ b/src/main/java/edu/princeton/cs/algs4/TST.java @@ -39,7 +39,7 @@ package edu.princeton.cs.algs4; /** - * The {@code TST} class represents an symbol table of key-value + * The {@code TST} class represents a symbol table of key-value * pairs, with string keys and generic values. * It supports the usual put, get, contains, * delete, size, and is-empty methods. @@ -139,6 +139,7 @@ public void put(String key, Value val) { throw new IllegalArgumentException("calls put() with null key"); } if (!contains(key)) n++; + else if (val == null) n--; // delete existing key root = put(root, key, val, 0); } @@ -228,7 +229,7 @@ private void collect(Node x, StringBuilder prefix, Queue queue) { /** * Returns all of the keys in the symbol table that match {@code pattern}, - * where . symbol is treated as a wildcard character. + * where the character '.' is interpreted as a wildcard character. * @param pattern the pattern * @return all of the keys in the symbol table that match {@code pattern}, * as an iterable, where . is treated as a wildcard character. @@ -238,7 +239,7 @@ public Iterable keysThatMatch(String pattern) { collect(root, new StringBuilder(), 0, pattern, queue); return queue; } - + private void collect(Node x, StringBuilder prefix, int i, String pattern, Queue queue) { if (x == null) return; char c = pattern.charAt(i); @@ -297,7 +298,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/TarjanSCC.java b/src/main/java/edu/princeton/cs/algs4/TarjanSCC.java index bcf3d5c7f..b5c6e3144 100644 --- a/src/main/java/edu/princeton/cs/algs4/TarjanSCC.java +++ b/src/main/java/edu/princeton/cs/algs4/TarjanSCC.java @@ -6,44 +6,44 @@ * https://algs4.cs.princeton.edu/42digraph/mediumDG.txt * https://algs4.cs.princeton.edu/42digraph/largeDG.txt * - * Compute the strongly-connected components of a digraph using + * Compute the strongly-connected components of a digraph using * Tarjan's algorithm. * * Runs in O(E + V) time. * * % java TarjanSCC tinyDG.txt * 5 components - * 1 + * 1 * 0 2 3 4 5 * 9 10 11 12 * 6 8 - * 7 + * 7 * ******************************************************************************/ package edu.princeton.cs.algs4; /** - * The {@code TarjanSCC} class represents a data type for + * The {@code TarjanSCC} class represents a data type for * determining the strong components in a digraph. * The id operation determines in which strong component * a given vertex lies; the areStronglyConnected operation * determines whether two vertices are in the same strong component; * and the count operation determines the number of strong * components. - - * The component identifier of a component is one of the - * vertices in the strong component: two vertices have the same component - * identifier if and only if they are in the same strong component. - + *

      + * The component identifier of a vertex is an integer between + * 0 and k–1, where k is the number of strong components. + * Two vertices have the same component identifier if and only if they + * are in the same strong component. *

      * This implementation uses Tarjan's algorithm. - * The constructor takes time proportional to V + E - * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * Afterwards, the id, count, and areStronglyConnected - * operations take constant time. - * For alternate implementations of the same API, see + * The constructor takes Θ(V + E) time, + * where V is the number of vertices and E is the + * number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the digraph). + * For alternative implementations of the same API, see * {@link KosarajuSharirSCC} and {@link GabowSCC}. *

      * For additional documentation, @@ -70,7 +70,7 @@ public class TarjanSCC { public TarjanSCC(Digraph G) { marked = new boolean[G.V()]; stack = new Stack(); - id = new int[G.V()]; + id = new int[G.V()]; low = new int[G.V()]; for (int v = 0; v < G.V(); v++) { if (!marked[v]) dfs(G, v); @@ -80,7 +80,7 @@ public TarjanSCC(Digraph G) { assert check(G); } - private void dfs(Digraph G, int v) { + private void dfs(Digraph G, int v) { marked[v] = true; low[v] = pre++; int min = low[v]; @@ -193,7 +193,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/ThreeSum.java b/src/main/java/edu/princeton/cs/algs4/ThreeSum.java index caaaf16f1..c4fb292f9 100644 --- a/src/main/java/edu/princeton/cs/algs4/ThreeSum.java +++ b/src/main/java/edu/princeton/cs/algs4/ThreeSum.java @@ -14,13 +14,13 @@ * and counts the number of triples that sum to exactly 0 * (ignoring integer overflow). * - * % java ThreeSum 1Kints.txt + * % java ThreeSum 1Kints.txt * 70 * - * % java ThreeSum 2Kints.txt + * % java ThreeSum 2Kints.txt * 528 * - * % java ThreeSum 4Kints.txt + * % java ThreeSum 4Kints.txt * 4039 * ******************************************************************************/ @@ -63,7 +63,7 @@ public static void printAll(int[] a) { } } } - } + } /** * Returns the number of triples (i, j, k) with {@code i < j < k} @@ -86,7 +86,7 @@ public static int count(int[] a) { } } return count; - } + } /** * Reads in a sequence of integers from a file, specified as a command-line argument; @@ -95,7 +95,7 @@ public static int count(int[] a) { * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { In in = new In(args[0]); int[] a = in.readAllInts(); @@ -103,11 +103,11 @@ public static void main(String[] args) { int count = count(a); StdOut.println("elapsed time = " + timer.elapsedTime()); StdOut.println(count); - } -} + } +} /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/ThreeSumFast.java b/src/main/java/edu/princeton/cs/algs4/ThreeSumFast.java index ed3cfac6b..931db2b42 100644 --- a/src/main/java/edu/princeton/cs/algs4/ThreeSumFast.java +++ b/src/main/java/edu/princeton/cs/algs4/ThreeSumFast.java @@ -21,13 +21,13 @@ * * % java ThreeSumFast 1Kints.txt * 70 - * + * * % java ThreeSumFast 2Kints.txt * 528 - * + * * % java ThreeSumFast 4Kints.txt * 4039 - * + * * % java ThreeSumFast 8Kints.txt * 32074 * @@ -48,7 +48,7 @@ * and printing the number of triples in an array of distinct integers that * sum to 0 (ignoring integer overflow). *

      - * This implementation uses sorting and binary search and takes time + * This implementation uses sorting and binary search and takes time * proportional to n^2 log n, where n is the number of integers. *

      * For additional documentation, see Section 1.4 of @@ -86,7 +86,7 @@ public static void printAll(int[] a) { if (k > j) StdOut.println(a[i] + " " + a[j] + " " + a[k]); } } - } + } /** * Returns the number of triples (i, j, k) with {@code i < j < k} @@ -108,7 +108,7 @@ public static int count(int[] a) { } } return count; - } + } /** * Reads in a sequence of distinct integers from a file, specified as a command-line argument; @@ -117,16 +117,16 @@ public static int count(int[] a) { * * @param args the command-line arguments */ - public static void main(String[] args) { + public static void main(String[] args) { In in = new In(args[0]); int[] a = in.readAllInts(); int count = count(a); StdOut.println(count); - } -} + } +} /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/TopM.java b/src/main/java/edu/princeton/cs/algs4/TopM.java index 95a0b3781..02a386cf5 100644 --- a/src/main/java/edu/princeton/cs/algs4/TopM.java +++ b/src/main/java/edu/princeton/cs/algs4/TopM.java @@ -3,12 +3,12 @@ * Execution: java TopM m < input.txt * Dependencies: MinPQ.java Transaction.java StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/24pq/tinyBatch.txt - * + * * Given an integer m from the command line and an input stream where * each line contains a String and a long value, this MinPQ client * prints the m lines whose numbers are the highest. - * - * % java TopM 5 < tinyBatch.txt + * + * % java TopM 5 < tinyBatch.txt * Thompson 2/27/2000 4747.08 * vonNeumann 2/12/1994 4732.35 * vonNeumann 1/11/1999 4409.74 @@ -32,7 +32,7 @@ * @author Robert Sedgewick * @author Kevin Wayne */ -public class TopM { +public class TopM { // This class should not be instantiated. private TopM() { } @@ -45,17 +45,17 @@ private TopM() { } * @param args the command-line arguments */ public static void main(String[] args) { - int m = Integer.parseInt(args[0]); + int m = Integer.parseInt(args[0]); MinPQ pq = new MinPQ(m+1); while (StdIn.hasNextLine()) { - // Create an entry from the next line and put on the PQ. + // Create an entry from the next line and put on the PQ. String line = StdIn.readLine(); Transaction transaction = new Transaction(line); - pq.insert(transaction); + pq.insert(transaction); // remove minimum if m+1 entries on the PQ - if (pq.size() > m) + if (pq.size() > m) pq.delMin(); } // top m entries are on the PQ @@ -65,12 +65,12 @@ public static void main(String[] args) { stack.push(transaction); for (Transaction transaction : stack) StdOut.println(transaction); - } -} + } +} /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Topological.java b/src/main/java/edu/princeton/cs/algs4/Topological.java index 266c8f36c..d100ca742 100644 --- a/src/main/java/edu/princeton/cs/algs4/Topological.java +++ b/src/main/java/edu/princeton/cs/algs4/Topological.java @@ -29,25 +29,25 @@ package edu.princeton.cs.algs4; /** - * The {@code Topological} class represents a data type for - * determining a topological order of a directed acyclic graph (DAG). - * Recall, a digraph has a topological order if and only if it is a DAG. + * The {@code Topological} class represents a data type for + * determining a topological order of a directed acyclic graph (DAG). + * A digraph has a topological order if and only if it is a DAG. * The hasOrder operation determines whether the digraph has * a topological order, and if so, the order operation * returns one. *

      * This implementation uses depth-first search. - * The constructor takes time proportional to V + E - * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * Afterwards, the hasOrder and rank operations takes constant time; - * the order operation takes time proportional to V. + * The constructor takes Θ(V + E) time in the + * worst case, where V is the number of vertices and E + * is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the digraph). *

      * See {@link DirectedCycle}, {@link DirectedCycleX}, and - * {@link EdgeWeightedDirectedCycle} to compute a - * directed cycle if the digraph is not a DAG. + * {@link EdgeWeightedDirectedCycle} for computing a directed cycle + * if the digraph is not a DAG. * See {@link TopologicalX} for a nonrecursive queue-based algorithm - * to compute a topological order of a DAG. + * for computing a topological order of a DAG. *

      * For additional documentation, * see Section 4.2 of @@ -91,9 +91,9 @@ public Topological(EdgeWeightedDigraph G) { } /** - * Returns a topological order if the digraph has a topologial order, + * Returns a topological order if the digraph has a topological order, * and {@code null} otherwise. - * @return a topological order of the vertices (as an interable) if the + * @return a topological order of the vertices (as an iterable) if the * digraph has a topological order (or equivalently, if the digraph is a DAG), * and {@code null} otherwise */ @@ -122,7 +122,7 @@ public boolean isDAG() { } /** - * The the rank of vertex {@code v} in the topological order; + * The rank of vertex {@code v} in the topological order; * -1 if the digraph is not a DAG * * @param v the vertex @@ -161,7 +161,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/TopologicalX.java b/src/main/java/edu/princeton/cs/algs4/TopologicalX.java index 45f5bc2e6..34dddde90 100644 --- a/src/main/java/edu/princeton/cs/algs4/TopologicalX.java +++ b/src/main/java/edu/princeton/cs/algs4/TopologicalX.java @@ -11,19 +11,19 @@ package edu.princeton.cs.algs4; /** - * The {@code TopologicalX} class represents a data type for - * determining a topological order of a directed acyclic graph (DAG). - * Recall, a digraph has a topological order if and only if it is a DAG. + * The {@code TopologicalX} class represents a data type for + * determining a topological order of a directed acyclic graph (DAG). + * A digraph has a topological order if and only if it is a DAG. * The hasOrder operation determines whether the digraph has * a topological order, and if so, the order operation * returns one. *

      * This implementation uses a nonrecursive, queue-based algorithm. - * The constructor takes time proportional to V + E - * (in the worst case), - * where V is the number of vertices and E is the number of edges. - * Afterwards, the hasOrder and rank operations takes constant time; - * the order operation takes time proportional to V. + * The constructor takes Θ(V + E) time in the worst + * case, where V is the number of vertices and E + * is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V) extra space (not including the digraph). *

      * See {@link DirectedCycle}, {@link DirectedCycleX}, and * {@link EdgeWeightedDirectedCycle} to compute a @@ -39,7 +39,7 @@ */ public class TopologicalX { private Queue order; // vertices in topological order - private int[] ranks; // ranks[v] = order where vertex v appers in order + private int[] ranks; // ranks[v] = order where vertex v appears in order /** * Determines whether the digraph {@code G} has a topological order and, if so, @@ -54,8 +54,8 @@ public TopologicalX(Digraph G) { indegree[v] = G.indegree(v); } - // initialize - ranks = new int[G.V()]; + // initialize + ranks = new int[G.V()]; order = new Queue(); int count = 0; @@ -95,8 +95,8 @@ public TopologicalX(EdgeWeightedDigraph G) { indegree[v] = G.indegree(v); } - // initialize - ranks = new int[G.V()]; + // initialize + ranks = new int[G.V()]; order = new Queue(); int count = 0; @@ -125,9 +125,9 @@ public TopologicalX(EdgeWeightedDigraph G) { } /** - * Returns a topological order if the digraph has a topologial order, + * Returns a topological order if the digraph has a topological order, * and {@code null} otherwise. - * @return a topological order of the vertices (as an interable) if the + * @return a topological order of the vertices (as an iterable) if the * digraph has a topological order (or equivalently, if the digraph is a DAG), * and {@code null} otherwise */ @@ -145,7 +145,7 @@ public boolean hasOrder() { } /** - * The the rank of vertex {@code v} in the topological order; + * The rank of vertex {@code v} in the topological order; * -1 if the digraph is not a DAG * * @param v vertex @@ -275,8 +275,8 @@ public static void main(String[] args) { // add F extra edges for (int i = 0; i < F; i++) { - int v = StdRandom.uniform(V); - int w = StdRandom.uniform(V); + int v = StdRandom.uniformInt(V); + int w = StdRandom.uniformInt(V); G1.addEdge(v, w); G2.addEdge(new DirectedEdge(v, w, 0.0)); } @@ -291,7 +291,7 @@ public static void main(String[] args) { StdOut.println("Not a DAG"); } - // or give topologial sort + // or give topological sort else { StdOut.print("Topological order: "); for (int v : topological1.order()) { @@ -306,7 +306,7 @@ public static void main(String[] args) { StdOut.println("Not a DAG"); } - // or give topologial sort + // or give topological sort else { StdOut.print("Topological order: "); for (int v : topological2.order()) { @@ -319,7 +319,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Transaction.java b/src/main/java/edu/princeton/cs/algs4/Transaction.java index bd56efd89..7af482c2d 100644 --- a/src/main/java/edu/princeton/cs/algs4/Transaction.java +++ b/src/main/java/edu/princeton/cs/algs4/Transaction.java @@ -2,7 +2,7 @@ * Compilation: javac Transaction.java * Execution: java Transaction * Dependencies: StdOut.java - * + * * Data type for commercial transactions. * ******************************************************************************/ @@ -16,9 +16,9 @@ * The {@code Transaction} class is an immutable data type to encapsulate a * commercial transaction with a customer name, date, and amount. *

      - * For additional documentation, - * see Section 1.2 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 1.2 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne @@ -35,7 +35,7 @@ public class Transaction implements Comparable { * @param who the person involved in this transaction * @param when the date of this transaction * @param amount the amount of this transaction - * @throws IllegalArgumentException if {@code amount} + * @throws IllegalArgumentException if {@code amount} * is {@code Double.NaN}, {@code Double.POSITIVE_INFINITY}, * or {@code Double.NEGATIVE_INFINITY} */ @@ -51,7 +51,7 @@ public Transaction(String who, Date when, double amount) { * Initializes a new transaction by parsing a string of the form NAME DATE AMOUNT. * * @param transaction the string to parse - * @throws IllegalArgumentException if {@code amount} + * @throws IllegalArgumentException if {@code amount} * is {@code Double.NaN}, {@code Double.POSITIVE_INFINITY}, * or {@code Double.NEGATIVE_INFINITY} */ @@ -72,7 +72,7 @@ public Transaction(String transaction) { public String who() { return who; } - + /** * Returns the date of this transaction. * @@ -81,7 +81,7 @@ public String who() { public Date when() { return when; } - + /** * Returns the amount of this transaction. * @@ -111,7 +111,7 @@ public String toString() { */ public int compareTo(Transaction that) { return Double.compare(this.amount, that.amount); - } + } /** * Compares this transaction to the specified object. @@ -194,7 +194,7 @@ public static void main(String[] args) { for (int i = 0; i < a.length; i++) StdOut.println(a[i]); StdOut.println(); - + StdOut.println("Sort by date"); Arrays.sort(a, new Transaction.WhenOrder()); for (int i = 0; i < a.length; i++) @@ -220,7 +220,7 @@ public static void main(String[] args) { /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/TransitiveClosure.java b/src/main/java/edu/princeton/cs/algs4/TransitiveClosure.java index d86b878e1..4debb4e54 100644 --- a/src/main/java/edu/princeton/cs/algs4/TransitiveClosure.java +++ b/src/main/java/edu/princeton/cs/algs4/TransitiveClosure.java @@ -14,12 +14,12 @@ * % java TransitiveClosure tinyDG.txt * 0 1 2 3 4 5 6 7 8 9 10 11 12 * -------------------------------------------- - * 0: T T T T T T - * 1: T - * 2: T T T T T T - * 3: T T T T T T - * 4: T T T T T T - * 5: T T T T T T + * 0: T T T T T T + * 1: T + * 2: T T T T T T + * 3: T T T T T T + * 4: T T T T T T + * 5: T T T T T T * 6: T T T T T T T T T T T * 7: T T T T T T T T T T T T T * 8: T T T T T T T T T T T T T @@ -33,18 +33,20 @@ package edu.princeton.cs.algs4; /** - * The {@code TransitiveClosure} class represents a data type for + * The {@code TransitiveClosure} class represents a data type for * computing the transitive closure of a digraph. *

      * This implementation runs depth-first search from each vertex. - * The constructor takes time proportional to V(V + E) - * (in the worst case) and uses space proportional to V2, - * where V is the number of vertices and E is the number of edges. + * The constructor takes Θ(V(V + E)) + * in the worst case, where V is the number of vertices and + * E is the number of edges. + * Each instance method takes Θ(1) time. + * It uses Θ(V2) extra space (not including the digraph). *

      * For large digraphs, you may want to consider a more sophisticated algorithm. * Nuutila proposes two * algorithm for the problem (based on strong components and an interval representation) - * that runs in E + V time on typical digraphs. + * that runs in Θ(E + V) time on typical digraphs. * * For additional documentation, * see Section 4.2 of @@ -120,7 +122,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/TrieSET.java b/src/main/java/edu/princeton/cs/algs4/TrieSET.java index 1c779871f..e75b1f036 100644 --- a/src/main/java/edu/princeton/cs/algs4/TrieSET.java +++ b/src/main/java/edu/princeton/cs/algs4/TrieSET.java @@ -4,7 +4,7 @@ * Dependencies: StdIn.java * Data files: https://algs4.cs.princeton.edu/52trie/shellsST.txt * - * An set for extended ASCII strings, implemented using a 256-way trie. + * A set for extended ASCII strings, implemented using a 256-way trie. * * Sample client reads in a list of words from standard input and * prints out each word, removing any duplicates. @@ -149,18 +149,18 @@ private void collect(Node x, StringBuilder prefix, Queue results) { /** * Returns all of the keys in the set that match {@code pattern}, - * where . symbol is treated as a wildcard character. + * where the character '.' is interpreted as a wildcard character. * @param pattern the pattern * @return all of the keys in the set that match {@code pattern}, * as an iterable, where . is treated as a wildcard character. - */ + */ public Iterable keysThatMatch(String pattern) { Queue results = new Queue(); StringBuilder prefix = new StringBuilder(); collect(root, prefix, pattern, results); return results; } - + private void collect(Node x, StringBuilder prefix, String pattern, Queue results) { if (x == null) return; int d = prefix.length(); @@ -201,7 +201,7 @@ public String longestPrefixOf(String query) { // returns the length of the longest string key in the subtrie // rooted at x that is a prefix of the query string, // assuming the first d character match and we have already - // found a prefix match of length length + // found a prefix match of the specified length private int longestPrefixOf(Node x, String query, int d, int length) { if (x == null) return length; if (x.isString) length = d; @@ -286,7 +286,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/TrieST.java b/src/main/java/edu/princeton/cs/algs4/TrieST.java index 2235c9819..eba9beea8 100644 --- a/src/main/java/edu/princeton/cs/algs4/TrieST.java +++ b/src/main/java/edu/princeton/cs/algs4/TrieST.java @@ -7,7 +7,7 @@ * A string symbol table for extended ASCII strings, implemented * using a 256-way trie. * - * % java TrieST < shellsST.txt + * % java TrieST < shellsST.txt * by 4 * sea 6 * sells 1 @@ -21,7 +21,7 @@ package edu.princeton.cs.algs4; /** - * The {@code TrieST} class represents an symbol table of key-value + * The {@code TrieST} class represents a symbol table of key-value * pairs, with string keys and generic values. * It supports the usual put, get, contains, * delete, size, and is-empty methods. @@ -177,7 +177,7 @@ private void collect(Node x, StringBuilder prefix, Queue results) { /** * Returns all of the keys in the symbol table that match {@code pattern}, - * where . symbol is treated as a wildcard character. + * where the character '.' is interpreted as a wildcard character. * @param pattern the pattern * @return all of the keys in the symbol table that match {@code pattern}, * as an iterable, where . is treated as a wildcard character. @@ -309,7 +309,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/TwoPersonZeroSumGame.java b/src/main/java/edu/princeton/cs/algs4/TwoPersonZeroSumGame.java index 1b446234d..3a1dd3595 100644 --- a/src/main/java/edu/princeton/cs/algs4/TwoPersonZeroSumGame.java +++ b/src/main/java/edu/princeton/cs/algs4/TwoPersonZeroSumGame.java @@ -5,7 +5,7 @@ * * Solve an m-by-n two-person zero-sum game by reducing it to * linear programming. Assuming A is a strictly positive payoff - * matrix, the optimal row and column player strategies are x* an y*, + * matrix, the optimal row and column player strategies are x* and y*, * scaled to be probability distributions. * * (P) max y^T 1 (D) min 1^T x @@ -51,20 +51,20 @@ * @author Kevin Wayne */ public class TwoPersonZeroSumGame { - private static final double EPSILON = 1E-8; + private static final double EPSILON = 1.0E-8; private final int m; // number of rows private final int n; // number of columns private LinearProgramming lp; // linear program solver private double constant; // constant added to each entry in payoff matrix // (0 if all entries are strictly positive) - + /** * Determines an optimal solution to the two-sum zero-sum game * with the specified payoff matrix. * * @param payoff the m-by-n payoff matrix - */ + */ public TwoPersonZeroSumGame(double[][] payoff) { m = payoff.length; n = payoff[0].length; @@ -255,7 +255,7 @@ private static void test(String description, double[][] payoff) { StdOut.printf("%8.4f, ", y[i]); StdOut.printf("%8.4f]\n", y[m-1]); StdOut.println("value = " + zerosum.value()); - + } // row = { 4/7, 3/7 }, column = { 0, 4/7, 3/7 }, value = 20/7 @@ -346,14 +346,14 @@ public static void main(String[] args) { double[][] payoff = new double[m][n]; for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) - payoff[i][j] = StdRandom.uniform(-0.5, 0.5); + payoff[i][j] = StdRandom.uniformDouble(-0.5, 0.5); test("random " + m + "-by-" + n, payoff); } } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/UF.java b/src/main/java/edu/princeton/cs/algs4/UF.java index 3411960b5..c67a4a701 100644 --- a/src/main/java/edu/princeton/cs/algs4/UF.java +++ b/src/main/java/edu/princeton/cs/algs4/UF.java @@ -27,68 +27,53 @@ /** * The {@code UF} class represents a union–find data type * (also known as the disjoint-sets data type). - * It supports the union and find operations, - * along with a connected operation for determining whether - * two sites are in the same component and a count operation that - * returns the total number of components. + * It supports the classic union and find operations, + * along with a count operation that returns the total number + * of sets. *

      - * The union–find data type models connectivity among a set of n - * sites, named 0 through n–1. - * The is-connected-to relation must be an - * equivalence relation: + * The union–find data type models a collection of sets containing + * n elements, with each element in exactly one set. + * The elements are named 0 through n–1. + * Initially, there are n sets, with each element in its + * own set. The canonical element of a set + * (also known as the root, identifier, + * leader, or set representative) + * is one distinguished element in the set. Here is a summary of + * the operations: *

        - *
      • Reflexive: p is connected to p. - *
      • Symmetric: If p is connected to q, - * then q is connected to p. - *
      • Transitive: If p is connected to q - * and q is connected to r, then - * p is connected to r. + *
      • find(p) returns the canonical element + * of the set containing p. The find operation + * returns the same value for two elements if and only if + * they are in the same set. + *
      • union(p, q) merges the set + * containing element p with the set containing + * element q. That is, if p and q + * are in different sets, replace these two sets + * with a new set that is the union of the two. + *
      • count() returns the number of sets. *
      *

      - * An equivalence relation partitions the sites into - * equivalence classes (or components). In this case, - * two sites are in the same component if and only if they are connected. - * Both sites and components are identified with integers between 0 and - * n–1. - * Initially, there are n components, with each site in its - * own component. The component identifier of a component - * (also known as the root, canonical element, leader, - * or set representative) is one of the sites in the component: - * two sites have the same component identifier if and only if they are - * in the same component. - *

        - *
      • union(p, q) adds a - * connection between the two sites p and q. - * If p and q are in different components, - * then it replaces - * these two components with a new component that is the union of - * the two. - *
      • find(p) returns the component - * identifier of the component containing p. - *
      • connected(p, q) - * returns true if both p and q - * are in the same component, and false otherwise. - *
      • count() returns the number of components. - *
      + * The canonical element of a set can change only when the set + * itself changes during a call to union—it cannot + * change during a call to either find or count. *

      - * The component identifier of a component can change - * only when the component itself changes during a call to - * union—it cannot change during a call - * to find, connected, or count. + * This implementation uses weighted quick union by rank + * with path compression by halving. + * The constructor takes Θ(n) time, where + * n is the number of elements. + * The union and find operations take + * Θ(log n) time in the worst case. + * The count operation takes Θ(1) time. + * Moreover, starting from an empty data structure with n sites, + * any intermixed sequence of m union and find + * operations takes O(m α(n)) time, + * where α(n) is the inverse of + * Ackermann's function. *

      - * This implementation uses weighted quick union by rank with path compression - * by halving. - * Initializing a data structure with n sites takes linear time. - * Afterwards, the union, find, and connected - * operations take logarithmic time (in the worst case) and the - * count operation takes constant time. - * Moreover, the amortized time per union, find, - * and connected operation has inverse Ackermann complexity. - * For alternate implementations of the same API, see + * For alternative implementations of the same API, see * {@link QuickUnionUF}, {@link QuickFindUF}, and {@link WeightedQuickUnionUF}. - * - *

      - * For additional documentation, see Section 1.5 of + * For additional documentation, see + * Section 1.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -102,11 +87,11 @@ public class UF { private int count; // number of components /** - * Initializes an empty union–find data structure with {@code n} sites - * {@code 0} through {@code n-1}. Each site is initially in its own - * component. + * Initializes an empty union-find data structure with + * {@code n} elements {@code 0} through {@code n-1}. + * Initially, each element is in its own set. * - * @param n the number of sites + * @param n the number of elements * @throws IllegalArgumentException if {@code n < 0} */ public UF(int n) { @@ -121,10 +106,10 @@ public UF(int n) { } /** - * Returns the component identifier for the component containing site {@code p}. + * Returns the canonical element of the set containing element {@code p}. * - * @param p the integer representing one site - * @return the component identifier for the component containing site {@code p} + * @param p an element + * @return the canonical element of the set containing {@code p} * @throws IllegalArgumentException unless {@code 0 <= p < n} */ public int find(int p) { @@ -137,34 +122,36 @@ public int find(int p) { } /** - * Returns the number of components. + * Returns the number of sets. * - * @return the number of components (between {@code 1} and {@code n}) + * @return the number of sets (between {@code 1} and {@code n}) */ public int count() { return count; } - + /** - * Returns true if the the two sites are in the same component. + * Returns true if the two elements are in the same set. * - * @param p the integer representing one site - * @param q the integer representing the other site - * @return {@code true} if the two sites {@code p} and {@code q} are in the same component; + * @param p one element + * @param q the other element + * @return {@code true} if {@code p} and {@code q} are in the same set; * {@code false} otherwise * @throws IllegalArgumentException unless * both {@code 0 <= p < n} and {@code 0 <= q < n} + * @deprecated Replace with two calls to {@link #find(int)}. */ + @Deprecated public boolean connected(int p, int q) { return find(p) == find(q); } - + /** - * Merges the component containing site {@code p} with the - * the component containing site {@code q}. + * Merges the set containing element {@code p} with the set + * containing element {@code q}. * - * @param p the integer representing one site - * @param q the integer representing the other site + * @param p one element + * @param q the other element * @throws IllegalArgumentException unless * both {@code 0 <= p < n} and {@code 0 <= q < n} */ @@ -187,15 +174,15 @@ public void union(int p, int q) { private void validate(int p) { int n = parent.length; if (p < 0 || p >= n) { - throw new IllegalArgumentException("index " + p + " is not between 0 and " + (n-1)); + throw new IllegalArgumentException("index " + p + " is not between 0 and " + (n-1)); } } /** - * Reads in a an integer {@code n} and a sequence of pairs of integers + * Reads an integer {@code n} and a sequence of pairs of integers * (between {@code 0} and {@code n-1}) from standard input, where each integer - * in the pair represents some site; - * if the sites are in different components, merge the two components + * in the pair represents some element; + * if the elements are in different sets, merge the two sets * and print the pair to standard output. * * @param args the command-line arguments @@ -206,7 +193,7 @@ public static void main(String[] args) { while (!StdIn.isEmpty()) { int p = StdIn.readInt(); int q = StdIn.readInt(); - if (uf.connected(p, q)) continue; + if (uf.find(p) == uf.find(q)) continue; uf.union(p, q); StdOut.println(p + " " + q); } @@ -216,7 +203,7 @@ public static void main(String[] args) { /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/Vector.java b/src/main/java/edu/princeton/cs/algs4/Vector.java index 415c3e6d0..7fd89334c 100644 --- a/src/main/java/edu/princeton/cs/algs4/Vector.java +++ b/src/main/java/edu/princeton/cs/algs4/Vector.java @@ -17,7 +17,7 @@ * 10z = [ 60.0 40.0 70.0 50.0 ] * |x| = 5.477225575051661 * = 25.0 - * + * * * Note that Vector is also the name of an unrelated Java library class * in the package java.util. @@ -33,14 +33,14 @@ * dot product, scalar product, unit vector, Euclidean norm, and the Euclidean * distance between two vectors. *

      - * For additional documentation, - * see Section 1.2 of - * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. + * For additional documentation, + * see Section 1.2 of + * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ -public class Vector { +public class Vector { private int d; // dimension of the vector private double[] data; // array of vector's components @@ -59,7 +59,7 @@ public Vector(int d) { /** * Initializes a vector from either an array or a vararg list. * The vararg syntax supports a constructor that takes a variable number of - * arugments such as Vector x = new Vector(1.0, 2.0, 3.0, 4.0). + * arguments such as Vector x = new Vector(1.0, 2.0, 3.0, 4.0). * * @param a the array or vararg list */ @@ -93,7 +93,7 @@ public int dimension() { } /** - * Returns the do product of this vector with the specified vector. + * Returns the dot product of this vector with the specified vector. * * @param that the other vector * @return the dot product of this vector and that vector @@ -120,7 +120,7 @@ public double magnitude() { /** * Returns the Euclidean distance between this vector and the specified vector. * - * @param that the other vector + * @param that the other vector * @return the Euclidean distance between this vector and that vector * @throws IllegalArgumentException if the dimensions of the two vectors are not equal */ @@ -212,7 +212,7 @@ public Vector direction() { /** * Returns a string representation of this vector. * - * @return a string representation of this vector, which consists of the + * @return a string representation of this vector, which consists of * the vector entries, separates by single spaces */ public String toString() { @@ -251,7 +251,7 @@ public static void main(String[] args) { } /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * diff --git a/src/main/java/edu/princeton/cs/algs4/WeightedQuickUnionUF.java b/src/main/java/edu/princeton/cs/algs4/WeightedQuickUnionUF.java index 3d0c2efc2..fe2f27d1d 100644 --- a/src/main/java/edu/princeton/cs/algs4/WeightedQuickUnionUF.java +++ b/src/main/java/edu/princeton/cs/algs4/WeightedQuickUnionUF.java @@ -15,65 +15,48 @@ /** * The {@code WeightedQuickUnionUF} class represents a union–find data type * (also known as the disjoint-sets data type). - * It supports the union and find operations, - * along with a connected operation for determining whether - * two sites are in the same component and a count operation that - * returns the total number of components. + * It supports the classic union and find operations, + * along with a count operation that returns the total number + * of sets. *

      - * The union–find data type models connectivity among a set of n - * sites, named 0 through n–1. - * The is-connected-to relation must be an - * equivalence relation: + * The union–find data type models a collection of sets containing + * n elements, with each element in exactly one set. + * The elements are named 0 through n–1. + * Initially, there are n sets, with each element in its + * own set. The canonical element of a set + * (also known as the root, identifier, + * leader, or set representative) + * is one distinguished element in the set. Here is a summary of + * the operations: *

        - *
      • Reflexive: p is connected to p. - *
      • Symmetric: If p is connected to q, - * then q is connected to p. - *
      • Transitive: If p is connected to q - * and q is connected to r, then - * p is connected to r. + *
      • find(p) returns the canonical element + * of the set containing p. The find operation + * returns the same value for two elements if and only if + * they are in the same set. + *
      • union(p, q) merges the set + * containing element p with the set containing + * element q. That is, if p and q + * are in different sets, replace these two sets + * with a new set that is the union of the two. + *
      • count() returns the number of sets. *
      *

      - * An equivalence relation partitions the sites into - * equivalence classes (or components). In this case, - * two sites are in the same component if and only if they are connected. - * Both sites and components are identified with integers between 0 and - * n–1. - * Initially, there are n components, with each site in its - * own component. The component identifier of a component - * (also known as the root, canonical element, leader, - * or set representative) is one of the sites in the component: - * two sites have the same component identifier if and only if they are - * in the same component. - *

        - *
      • union(p, q) adds a - * connection between the two sites p and q. - * If p and q are in different components, - * then it replaces - * these two components with a new component that is the union of - * the two. - *
      • find(p) returns the component - * identifier of the component containing p. - *
      • connected(p, q) - * returns true if both p and q - * are in the same component, and false otherwise. - *
      • count() returns the number of components. - *
      + * The canonical element of a set can change only when the set + * itself changes during a call to union—it cannot + * change during a call to either find or count. *

      - * The component identifier of a component can change - * only when the component itself changes during a call to - * union—it cannot change during a call - * to find, connected, or count. + * This implementation uses weighted quick union by size + * (without path compression). + * The constructor takes Θ(n), where n + * is the number of elements. + * The union and find + * operations take Θ(log n) time in the worst + * case. The count operation takes Θ(1) time. *

      - * This implementation uses weighted quick union by size (without path compression). - * Initializing a data structure with n sites takes linear time. - * Afterwards, the union, find, and connected - * operations take logarithmic time (in the worst case) and the - * count operation takes constant time. - * For alternate implementations of the same API, see + * For alternative implementations of the same API, see * {@link UF}, {@link QuickFindUF}, and {@link QuickUnionUF}. - * - *

      - * For additional documentation, see Section 1.5 of + * For additional documentation, see + * Section 1.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick @@ -81,15 +64,15 @@ */ public class WeightedQuickUnionUF { private int[] parent; // parent[i] = parent of i - private int[] size; // size[i] = number of sites in subtree rooted at i + private int[] size; // size[i] = number of elements in subtree rooted at i private int count; // number of components /** - * Initializes an empty union–find data structure with {@code n} sites - * {@code 0} through {@code n-1}. Each site is initially in its own - * component. + * Initializes an empty union-find data structure with + * {@code n} elements {@code 0} through {@code n-1}. + * Initially, each element is in its own set. * - * @param n the number of sites + * @param n the number of elements * @throws IllegalArgumentException if {@code n < 0} */ public WeightedQuickUnionUF(int n) { @@ -103,19 +86,19 @@ public WeightedQuickUnionUF(int n) { } /** - * Returns the number of components. + * Returns the number of sets. * - * @return the number of components (between {@code 1} and {@code n}) + * @return the number of sets (between {@code 1} and {@code n}) */ public int count() { return count; } - + /** - * Returns the component identifier for the component containing site {@code p}. + * Returns the canonical element of the set containing element {@code p}. * - * @param p the integer representing one object - * @return the component identifier for the component containing site {@code p} + * @param p an element + * @return the canonical element of the set containing {@code p} * @throws IllegalArgumentException unless {@code 0 <= p < n} */ public int find(int p) { @@ -125,34 +108,36 @@ public int find(int p) { return p; } - // validate that p is a valid index - private void validate(int p) { - int n = parent.length; - if (p < 0 || p >= n) { - throw new IllegalArgumentException("index " + p + " is not between 0 and " + (n-1)); - } - } - /** - * Returns true if the the two sites are in the same component. + * Returns true if the two elements are in the same set. * - * @param p the integer representing one site - * @param q the integer representing the other site - * @return {@code true} if the two sites {@code p} and {@code q} are in the same component; + * @param p one element + * @param q the other element + * @return {@code true} if {@code p} and {@code q} are in the same set; * {@code false} otherwise * @throws IllegalArgumentException unless * both {@code 0 <= p < n} and {@code 0 <= q < n} + * @deprecated Replace with two calls to {@link #find(int)}. */ + @Deprecated public boolean connected(int p, int q) { return find(p) == find(q); } + // validate that p is a valid index + private void validate(int p) { + int n = parent.length; + if (p < 0 || p >= n) { + throw new IllegalArgumentException("index " + p + " is not between 0 and " + (n-1)); + } + } + /** - * Merges the component containing site {@code p} with the - * the component containing site {@code q}. + * Merges the set containing element {@code p} with the set + * containing element {@code q}. * - * @param p the integer representing one site - * @param q the integer representing the other site + * @param p one element + * @param q the other element * @throws IllegalArgumentException unless * both {@code 0 <= p < n} and {@code 0 <= q < n} */ @@ -175,9 +160,10 @@ public void union(int p, int q) { /** - * Reads in a sequence of pairs of integers (between 0 and n-1) from standard input, - * where each integer represents some object; - * if the sites are in different components, merge the two components + * Reads an integer {@code n} and a sequence of pairs of integers + * (between {@code 0} and {@code n-1}) from standard input, where each integer + * in the pair represents some element; + * if the elements are in different sets, merge the two sets * and print the pair to standard output. * * @param args the command-line arguments @@ -188,7 +174,7 @@ public static void main(String[] args) { while (!StdIn.isEmpty()) { int p = StdIn.readInt(); int q = StdIn.readInt(); - if (uf.connected(p, q)) continue; + if (uf.find(p) == uf.find(q)) continue; uf.union(p, q); StdOut.println(p + " " + q); } @@ -199,7 +185,7 @@ public static void main(String[] args) { /****************************************************************************** - * Copyright 2002-2018, Robert Sedgewick and Kevin Wayne. + * Copyright 2002-2025, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook *