├── LICENSE ├── src ├── test │ └── java │ │ └── blogspot │ │ └── software_and_algorithms │ │ └── stern_library │ │ ├── string │ │ ├── KnuthMorrisPrattAlgorithmTest.java │ │ └── DamerauLevenshteinAlgorithmTest.java │ │ ├── data_structure │ │ ├── OrderLinkedRedBlackTreeTest.java │ │ ├── RedBlackTreeTest.java │ │ ├── IntervalTest.java │ │ ├── DynamicIntervalTreeTest.java │ │ ├── StaticIntervalTreeTest.java │ │ └── ThriftyListTest.java │ │ ├── geometry │ │ └── ClosestPointPairAlgorithmTest.java │ │ └── optimization │ │ └── HungarianAlgorithmTest.java └── main │ └── java │ └── blogspot │ └── software_and_algorithms │ └── stern_library │ ├── string │ ├── KnuthMorrisPrattAlgorithm.java │ └── DamerauLevenshteinAlgorithm.java │ ├── data_structure │ ├── OrderLinkedRedBlackTree.java │ ├── Interval.java │ ├── DynamicIntervalTree.java │ ├── RedBlackTree.java │ └── StaticIntervalTree.java │ ├── geometry │ └── ClosestPointPairAlgorithm.java │ └── optimization │ └── HungarianAlgorithm.java └── pom.xml /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Kevin L. Stern 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/test/java/blogspot/software_and_algorithms/stern_library/string/KnuthMorrisPrattAlgorithmTest.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.string; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /* Copyright (c) 2012 Kevin L. Stern 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /** 28 | * Test class for KnuthMorrisPrattAlgorithm. 29 | * 30 | * @author Kevin L. Stern 31 | */ 32 | public class KnuthMorrisPrattAlgorithmTest { 33 | @Test 34 | public void test1() { 35 | String needle = "needle"; 36 | String haystack = "It's like searching for a needle in a haystack."; 37 | Assert 38 | .assertEquals(haystack.indexOf(needle), new KnuthMorrisPrattAlgorithm( 39 | needle).execute(haystack)); 40 | } 41 | 42 | @Test 43 | public void test2() { 44 | String needle = "01012"; 45 | String haystack = "010101012"; 46 | Assert 47 | .assertEquals(haystack.indexOf(needle), new KnuthMorrisPrattAlgorithm( 48 | needle).execute(haystack)); 49 | } 50 | 51 | @Test 52 | public void test3() { 53 | String needle = "0101"; 54 | String haystack = "0102020101"; 55 | Assert 56 | .assertEquals(haystack.indexOf(needle), new KnuthMorrisPrattAlgorithm( 57 | needle).execute(haystack)); 58 | } 59 | 60 | @Test 61 | public void test4() { 62 | String needle = "aaaaaaa"; 63 | String haystack = "aaaaaab"; 64 | Assert 65 | .assertEquals(haystack.indexOf(needle), new KnuthMorrisPrattAlgorithm( 66 | needle).execute(haystack)); 67 | } 68 | 69 | @Test 70 | public void test5() { 71 | String needle = "aaaaaaa"; 72 | String haystack = "aaaaaaa"; 73 | Assert.assertEquals(haystack.indexOf(needle, 1), 74 | new KnuthMorrisPrattAlgorithm(needle).execute(haystack, 75 | 1)); 76 | } 77 | 78 | @Test 79 | public void test6() { 80 | String needle = "aa"; 81 | String haystack = "aaaaaaa"; 82 | Assert.assertEquals(haystack.indexOf(needle, 1), 83 | new KnuthMorrisPrattAlgorithm(needle).execute(haystack, 84 | 1)); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.github.kevinstern 4 | software-and-algorithms 5 | 1.0 6 | 7 | Software and Algorithms 8 | Neat algorithm implementations in Java 9 | https://www.github.com/KevinStern/software-and-algorithms 10 | 11 | 12 | 13 | MIT License 14 | http://www.opensource.org/licenses/mit-license.php 15 | repo 16 | 17 | 18 | 19 | 20 | 21 | Kevin L. Stern 22 | https://www.github.com/KevinStern 23 | 24 | 25 | 26 | 27 | scm:git:git://github.com/KevinStern/software-and-algorithms.git 28 | scm:git:ssh://github.com/KevinStern/software-and-algorithms.git 29 | https://www.github.com/KevinStern/software-and-algorithms 30 | 31 | 32 | 33 | 34 | ossrh 35 | https://oss.sonatype.org/content/repositories/snapshots 36 | 37 | 38 | ossrh 39 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 40 | 41 | 42 | 43 | 44 | 45 | junit 46 | junit 47 | 4.13.1 48 | test 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-gpg-plugin 57 | 1.5 58 | 59 | 60 | sign-artifacts 61 | verify 62 | 63 | sign 64 | 65 | 66 | 67 | 68 | 69 | org.apache.maven.plugins 70 | maven-source-plugin 71 | 2.2.1 72 | 73 | 74 | attach-sources 75 | 76 | jar-no-fork 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-javadoc-plugin 84 | 2.9.1 85 | 86 | 87 | attach-javadocs 88 | 89 | jar 90 | 91 | 92 | 93 | 94 | 95 | org.apache.maven.plugins 96 | maven-jar-plugin 97 | 98 | 99 | maven-surefire-plugin 100 | 2.4.3 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-compiler-plugin 105 | 106 | 1.8 107 | 1.8 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /src/test/java/blogspot/software_and_algorithms/stern_library/string/DamerauLevenshteinAlgorithmTest.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.string; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /* Copyright (c) 2012 Kevin L. Stern 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /** 28 | * Test class for DamerauLevenshteinAlgorithm. 29 | * 30 | * @author Kevin L. Stern 31 | */ 32 | public class DamerauLevenshteinAlgorithmTest { 33 | @Test 34 | public void test() { 35 | Assert.assertEquals(7, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) 36 | .execute("NawKtYu", "")); 37 | 38 | Assert.assertEquals(7, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) 39 | .execute("", "NawKtYu")); 40 | 41 | Assert.assertEquals(0, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) 42 | .execute("NawKtYu", "NawKtYu")); 43 | 44 | Assert.assertEquals(6, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) 45 | .execute("NawKtYu", "tKNwYua")); 46 | 47 | Assert.assertEquals(1, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) 48 | .execute("Jdc", "dJc")); 49 | 50 | Assert.assertEquals(5, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) 51 | .execute("sUzSOwx", "zsSxUwO")); 52 | 53 | Assert.assertEquals(7, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) 54 | .execute("eOqoHAta", "tAeaqHoO")); 55 | 56 | Assert.assertEquals(1, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) 57 | .execute("glSbo", "lgSbo")); 58 | 59 | Assert.assertEquals(4, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) 60 | .execute("NJtQKcJE", "cJEtQKJN")); 61 | 62 | Assert.assertEquals(5, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) 63 | .execute("GitIEVs", "EGItVis")); 64 | 65 | Assert.assertEquals(4, new DamerauLevenshteinAlgorithm(1, 1, 1, 1) 66 | .execute("MiWK", "WKiM")); 67 | } 68 | 69 | @Test 70 | public void testCosts() { 71 | /* 72 | * Test replace cost. 73 | */ 74 | Assert.assertEquals(1, new DamerauLevenshteinAlgorithm(100, 100, 1, 100) 75 | .execute("a", "b")); 76 | /* 77 | * Test swap cost. 78 | */ 79 | Assert.assertEquals(200, 80 | new DamerauLevenshteinAlgorithm(100, 100, 100, 200) 81 | .execute("ab", "ba")); 82 | /* 83 | * Test delete cost. 84 | */ 85 | Assert.assertEquals(1, new DamerauLevenshteinAlgorithm(1, 100, 100, 100) 86 | .execute("aa", "a")); 87 | /* 88 | * Test insert cost. 89 | */ 90 | Assert.assertEquals(1, new DamerauLevenshteinAlgorithm(100, 1, 100, 100) 91 | .execute("a", "aa")); 92 | } 93 | 94 | @Test 95 | public void testInvalidCosts() { 96 | try { 97 | new DamerauLevenshteinAlgorithm(1, 1, 1, 0); 98 | Assert.fail(); 99 | } catch (IllegalArgumentException e) { 100 | 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/blogspot/software_and_algorithms/stern_library/string/KnuthMorrisPrattAlgorithm.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.string; 2 | 3 | /* Copyright (c) 2012 Kevin L. Stern 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | /** 25 | * An implementation of the Knuth Morris Pratt substring search algorithm. An 26 | * instance of the algorithm is constructed around a needle string of length m, 27 | * a process which consumes O(m) time as well as O(m) space. Once an instance is 28 | * constructed, it is capable of searching for the needle string in any number 29 | * of haystack strings. The search process consumes O(n) time in a haystack 30 | * string of length n. 31 | * 32 | * @author Kevin L. Stern 33 | */ 34 | public class KnuthMorrisPrattAlgorithm { 35 | private final String needle; 36 | private final int[] stateTransitionTable; 37 | 38 | /** 39 | * Constructor. 40 | * 41 | * @param needle 42 | * the search string for which the instance will be constructed. 43 | */ 44 | public KnuthMorrisPrattAlgorithm(String needle) { 45 | this.needle = needle; 46 | this.stateTransitionTable = new int[needle.length()]; 47 | stateTransitionTable[0] = -1; 48 | int state = 0; 49 | for (int i = 1; i < needle.length(); i++) { 50 | int transition = state; 51 | if (needle.charAt(transition) == needle.charAt(i)) { 52 | transition = stateTransitionTable[transition]; 53 | } 54 | stateTransitionTable[i] = transition; 55 | if (needle.charAt(i) == needle.charAt(state)) { 56 | state += 1; 57 | } else { 58 | state = 0; 59 | } 60 | } 61 | } 62 | 63 | /** 64 | * Execute the search algorithm. 65 | * 66 | * @param haystack 67 | * the string in which to search for the needle specified at 68 | * construction time. 69 | * @return the index of the first occurrence of the needle string within the 70 | * specified haystack string, -1 if none. 71 | */ 72 | public int execute(String haystack) { 73 | return execute(haystack, 0); 74 | } 75 | 76 | /** 77 | * Execute the search algorithm. 78 | * 79 | * @param haystack 80 | * the string in which to search for the needle specified at 81 | * construction time. 82 | * @param index 83 | * the index at which to begin the search within the haystack string. 84 | * @return the index of the first occurrence of the needle string within the 85 | * specified portion of the haystack string, -1 if none. 86 | */ 87 | public int execute(String haystack, int index) { 88 | int state = 0; 89 | for (int i = index; i < haystack.length(); i++) { 90 | if (haystack.charAt(i) == needle.charAt(state)) { 91 | state += 1; 92 | if (state == needle.length()) { 93 | return i - needle.length() + 1; 94 | } 95 | } else { 96 | do { 97 | state = stateTransitionTable[state]; 98 | } while (state >= 0 && haystack.charAt(i) != needle.charAt(state)); 99 | state += 1; 100 | } 101 | } 102 | return -1; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/test/java/blogspot/software_and_algorithms/stern_library/data_structure/OrderLinkedRedBlackTreeTest.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.data_structure; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | /* Copyright (C) 2012 Kevin L. Stern. 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a copy 12 | * of this software and associated documentation files (the "Software"), to deal 13 | * in the Software without restriction, including without limitation the rights 14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | * copies of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be included in 19 | * all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | */ 29 | 30 | /** 31 | * Test class for OrderLinkedRedBlackTree. 32 | * 33 | * @author Kevin L. Stern 34 | */ 35 | public class OrderLinkedRedBlackTreeTest { 36 | @Test 37 | public void testNull() { 38 | OrderLinkedRedBlackTree tree = new OrderLinkedRedBlackTree(); 39 | try { 40 | tree.insert(null); 41 | Assert.fail(); 42 | } catch (NullPointerException e) { 43 | 44 | } 45 | try { 46 | tree.getPredecessor(null); 47 | Assert.fail(); 48 | } catch (NullPointerException e) { 49 | 50 | } 51 | try { 52 | tree.getSuccessor(null); 53 | Assert.fail(); 54 | } catch (NullPointerException e) { 55 | 56 | } 57 | Assert.assertNull(tree.delete(null)); 58 | Assert.assertNull(tree.getNode(null)); 59 | Assert.assertFalse(tree.contains(null)); 60 | 61 | tree.insert(0); 62 | try { 63 | tree.insert(null); 64 | Assert.fail(); 65 | } catch (NullPointerException e) { 66 | 67 | } 68 | try { 69 | tree.getPredecessor(null); 70 | Assert.fail(); 71 | } catch (NullPointerException e) { 72 | 73 | } 74 | try { 75 | tree.getSuccessor(null); 76 | Assert.fail(); 77 | } catch (NullPointerException e) { 78 | 79 | } 80 | Assert.assertNull(tree.delete(null)); 81 | Assert.assertNull(tree.getNode(null)); 82 | Assert.assertFalse(tree.contains(null)); 83 | } 84 | 85 | @Test 86 | public void testPredecessor() { 87 | List master = new ArrayList(); 88 | OrderLinkedRedBlackTree tree = new OrderLinkedRedBlackTree(); 89 | for (int j = 0; j < 100; j++) { 90 | tree.insert(j); 91 | master.add(j); 92 | } 93 | while (!master.isEmpty()) { 94 | for (int j = 1; j < master.size(); j++) { 95 | Assert.assertEquals(master.get(j - 1), 96 | tree.getPredecessor(tree.getNode(master.get(j))) 97 | .getValue()); 98 | } 99 | Assert.assertNull(tree.getPredecessor(tree.getNode(master.get(0)))); 100 | int index = master.size() >> 1; 101 | tree.delete(master.get(index)); 102 | master.remove(index); 103 | } 104 | } 105 | 106 | @Test 107 | public void testSuccessor() { 108 | List master = new ArrayList(); 109 | OrderLinkedRedBlackTree tree = new OrderLinkedRedBlackTree(); 110 | for (int j = 0; j < 100; j++) { 111 | tree.insert(j); 112 | master.add(j); 113 | } 114 | while (!master.isEmpty()) { 115 | for (int j = 0; j < master.size() - 1; j++) { 116 | Assert.assertEquals(master.get(j + 1), 117 | tree.getSuccessor(tree.getNode(master.get(j))) 118 | .getValue()); 119 | } 120 | Assert 121 | .assertNull(tree.getSuccessor(tree.getNode(master.get(master.size() - 1)))); 122 | int index = master.size() >> 1; 123 | tree.delete(master.get(index)); 124 | master.remove(index); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/test/java/blogspot/software_and_algorithms/stern_library/geometry/ClosestPointPairAlgorithmTest.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.geometry; 2 | 3 | import java.awt.geom.Point2D; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | 11 | /* Copyright (c) 2012 Kevin L. Stern 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining a copy 14 | * of this software and associated documentation files (the "Software"), to deal 15 | * in the Software without restriction, including without limitation the rights 16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | * copies of the Software, and to permit persons to whom the Software is 18 | * furnished to do so, subject to the following conditions: 19 | * 20 | * The above copyright notice and this permission notice shall be included in 21 | * all copies or substantial portions of the Software. 22 | * 23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | * SOFTWARE. 30 | */ 31 | 32 | /** 33 | * Test class for ClosestPointPairAlgorithm. 34 | * 35 | * @author Kevin L. Stern 36 | */ 37 | public class ClosestPointPairAlgorithmTest { 38 | private static Point2D[] sort(Point2D[] points) { 39 | Arrays.sort(points, (o1, o2) -> { 40 | double d = o1.getX() - o2.getX(); 41 | if (d == 0) { 42 | d = o1.getY() - o2.getY(); 43 | } 44 | return d < 0 ? -1 : d > 0 ? 1 : 0; 45 | }); 46 | return points; 47 | } 48 | 49 | @Test 50 | public void testBaseCase2Points() { 51 | Point2D p1 = new Point2D.Double(.49, .5); 52 | Point2D p2 = new Point2D.Double(.5, .2); 53 | 54 | List list = new ArrayList(); 55 | list.add(p1); 56 | list.add(p2); 57 | Assert.assertTrue(Arrays.equals( 58 | new Point2D[] { p1, p2 }, 59 | sort(new ClosestPointPairAlgorithm(list).execute()))); 60 | 61 | } 62 | 63 | @Test 64 | public void testBaseCase3Points() { 65 | Point2D p1 = new Point2D.Double(.49, .5); 66 | Point2D p2 = new Point2D.Double(.5, .2); 67 | Point2D p3 = new Point2D.Double(.51, .5); 68 | 69 | List list = new ArrayList(); 70 | list.add(p1); 71 | list.add(p2); 72 | list.add(p3); 73 | Assert.assertTrue(Arrays.equals( 74 | new Point2D[] { p1, p3 }, 75 | sort(new ClosestPointPairAlgorithm(list).execute()))); 76 | 77 | } 78 | 79 | @Test 80 | public void testBasic() { 81 | Point2D p1 = new Point2D.Double(.49, .5); 82 | Point2D p2 = new Point2D.Double(.51, .5); 83 | 84 | List list = new ArrayList(); 85 | list.add(p1); 86 | list.add(p2); 87 | list.add(new Point2D.Double(.6, .5)); 88 | list.add(new Point2D.Double(.7, .5)); 89 | list.add(new Point2D.Double(.8, .5)); 90 | Assert.assertTrue(Arrays.equals( 91 | new Point2D[] { p1, p2 }, 92 | sort(new ClosestPointPairAlgorithm(list).execute()))); 93 | } 94 | 95 | @Test 96 | public void testMidCalculation() { 97 | List list = new ArrayList<>(); 98 | Point2D p1 = new Point2D.Double(0.92, 0.38); 99 | Point2D p2 = new Point2D.Double(0.94, 0.50); 100 | list.add(new Point2D.Double(0.34, 0.91)); 101 | list.add(new Point2D.Double(0.46, 0.55)); 102 | list.add(p1); 103 | list.add(p2); 104 | list.add(new Point2D.Double(0.15, 0.96)); 105 | list.add(new Point2D.Double(0.09, 0.72)); 106 | list.add(new Point2D.Double(0.76, 0.20)); 107 | Assert.assertTrue(Arrays.equals( 108 | new Point2D[] { p1, p2 }, 109 | sort(new ClosestPointPairAlgorithm(list).execute()))); 110 | } 111 | 112 | @Test 113 | public void testSplitMergeProcedure() { 114 | Point2D p1 = new Point2D.Double(.49, .5); 115 | Point2D p2 = new Point2D.Double(.51, .5); 116 | 117 | List list = new ArrayList(); 118 | list.add(p1); 119 | list.add(p2); 120 | list.add(new Point2D.Double(.5, .2)); 121 | list.add(new Point2D.Double(.4, .5)); 122 | list.add(new Point2D.Double(.6, .5)); 123 | Assert.assertTrue(Arrays.equals( 124 | new Point2D[] { p1, p2 }, 125 | sort(new ClosestPointPairAlgorithm(list).execute()))); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/test/java/blogspot/software_and_algorithms/stern_library/optimization/HungarianAlgorithmTest.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.optimization; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | /* Copyright (c) 2012 Kevin L. Stern 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in 20 | * all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | */ 30 | 31 | /** 32 | * Test class for HungarianAlgorithm. 33 | * 34 | * @author Kevin L. Stern 35 | */ 36 | public class HungarianAlgorithmTest { 37 | private static double computeCost(double[][] matrix, int[] match) { 38 | double result = 0; 39 | Set visited = new HashSet(); 40 | for (int i = 0; i < matrix.length; i++) { 41 | if (match[i] == -1) { 42 | continue; 43 | } 44 | if (!visited.add(match[i])) { 45 | Assert.fail(); 46 | } 47 | result += matrix[i][match[i]]; 48 | } 49 | return result; 50 | } 51 | 52 | @Test 53 | public void test1() { 54 | double[][] matrix = new double[][] { new double[] { 4.0, 1.5, 4.0 }, 55 | new double[] { 4.0, 4.5, 6.0 }, new double[] { 3.0, 2.25, 3.0 } }; 56 | HungarianAlgorithm b = new HungarianAlgorithm(matrix); 57 | int[] match = b.execute(); 58 | Assert.assertTrue(Arrays.equals(new int[] { 1, 0, 2 }, match)); 59 | Assert.assertEquals(8.5, computeCost(matrix, match), 0.0000001); 60 | } 61 | 62 | @Test 63 | public void test2() { 64 | double[][] matrix = new double[][] { new double[] { 1.0, 1.0, 0.8 }, 65 | new double[] { 0.9, 0.8, 0.1 }, new double[] { 0.9, 0.7, 0.4 } }; 66 | HungarianAlgorithm b = new HungarianAlgorithm(matrix); 67 | int[] match = b.execute(); 68 | Assert.assertTrue(Arrays.equals(new int[] { 0, 2, 1 }, match)); 69 | Assert.assertEquals(1.8, computeCost(matrix, match), 0.0000001); 70 | } 71 | 72 | @Test 73 | public void test3() { 74 | double[][] matrix = new double[][] { new double[] { 6.0, 0.0, 7.0, 5.0 }, 75 | new double[] { 2.0, 6.0, 2.0, 6.0 }, 76 | new double[] { 2.0, 7.0, 2.0, 1.0 }, 77 | new double[] { 9.0, 4.0, 7.0, 1.0 } }; 78 | HungarianAlgorithm b = new HungarianAlgorithm(matrix); 79 | int[] match = b.execute(); 80 | Assert.assertTrue(Arrays.equals(new int[] { 1, 0, 2, 3 }, match)); 81 | Assert.assertEquals(5, computeCost(matrix, match), 0.0000001); 82 | } 83 | 84 | @Test 85 | public void testInvalidInput() { 86 | try { 87 | new HungarianAlgorithm(new double[][] { new double[] { 1, 2 }, 88 | new double[] { 3 } }); 89 | Assert.fail(); 90 | } catch (IllegalArgumentException e) {} 91 | try { 92 | new HungarianAlgorithm(new double[][] { new double[] { 1, 2 }, 93 | new double[] { 3, Double.POSITIVE_INFINITY } }); 94 | Assert.fail(); 95 | } catch (IllegalArgumentException e) {} 96 | try { 97 | new HungarianAlgorithm(new double[][] { new double[] { 1, 2 }, 98 | new double[] { 3, 1.0 / 0.0 } }); 99 | Assert.fail(); 100 | } catch (IllegalArgumentException e) {} 101 | try { 102 | new HungarianAlgorithm(null); 103 | Assert.fail(); 104 | } catch (NullPointerException e) {} 105 | } 106 | 107 | @Test 108 | public void testUnassignedJob() { 109 | double[][] matrix = new double[][] { 110 | new double[] { 6.0, 0.0, 7.0, 5.0, 2.0 }, 111 | new double[] { 2.0, 6.0, 2.0, 6.0, 7.0 }, 112 | new double[] { 2.0, 7.0, 2.0, 1.0, 1.0 }, 113 | new double[] { 9.0, 4.0, 7.0, 1.0, 0.0 } }; 114 | HungarianAlgorithm b = new HungarianAlgorithm(matrix); 115 | int[] match = b.execute(); 116 | Assert.assertTrue(Arrays.equals(new int[] { 1, 0, 3, 4 }, match)); 117 | Assert.assertEquals(3, computeCost(matrix, match), 0.0000001); 118 | } 119 | 120 | @Test 121 | public void testUnassignedWorker() { 122 | double[][] matrix = new double[][] { new double[] { 6.0, 0.0, 7.0, 5.0 }, 123 | new double[] { 2.0, 6.0, 2.0, 6.0 }, 124 | new double[] { 2.0, 7.0, 2.0, 1.0 }, 125 | new double[] { 9.0, 4.0, 7.0, 1.0 }, 126 | new double[] { 0.0, 0.0, 0.0, 0.0 } }; 127 | HungarianAlgorithm b = new HungarianAlgorithm(matrix); 128 | int[] match = b.execute(); 129 | Assert.assertTrue(Arrays.equals(new int[] { 1, -1, 2, 3, 0 }, match)); 130 | Assert.assertEquals(3, computeCost(matrix, match), 0.0000001); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/blogspot/software_and_algorithms/stern_library/data_structure/OrderLinkedRedBlackTree.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.data_structure; 2 | 3 | import java.util.Comparator; 4 | 5 | /* Copyright (c) 2012 Kevin L. Stern 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /** 27 | * A red black tree that has been augmented to support linear time partial 28 | * iteration by storing pointers to a node's predecessor and successor within 29 | * the node itself. 30 | * 31 | * @author Kevin L. Stern 32 | */ 33 | public class OrderLinkedRedBlackTree extends RedBlackTree { 34 | private RedBlackTree.Node head; 35 | 36 | /** 37 | * Default constructor. 38 | */ 39 | public OrderLinkedRedBlackTree() { 40 | this(null); 41 | } 42 | 43 | /** 44 | * Construct a new instance which uses the specified comparator. 45 | * 46 | * @param comparator 47 | * the comparator to use when ordering elements. 48 | */ 49 | public OrderLinkedRedBlackTree(Comparator comparator) { 50 | super(comparator); 51 | } 52 | 53 | /** 54 | * {@inheritDoc} 55 | */ 56 | @Override 57 | public void clear() { 58 | super.clear(); 59 | head = null; 60 | } 61 | 62 | /** 63 | * {@inheritDoc} 64 | */ 65 | @Override 66 | protected RedBlackTree.Node createNewNode(T value) { 67 | return new Node(value); 68 | } 69 | 70 | /** 71 | * {@inheritDoc} 72 | */ 73 | @Override 74 | public RedBlackTree.Node delete(T value) { 75 | if (head != null && head.getValue().equals(value)) { 76 | head = getSuccessor(head); 77 | } 78 | RedBlackTree.Node result = super.delete(value); 79 | if (result != null) { 80 | Node linkedNode = (Node) result; 81 | if (linkedNode.getPredecessor() != null) 82 | linkedNode.getPredecessor().setSuccessor(linkedNode.getSuccessor()); 83 | if (linkedNode.getSuccessor() != null) 84 | linkedNode.getSuccessor().setPredecessor(linkedNode.getPredecessor()); 85 | } 86 | 87 | return result; 88 | } 89 | 90 | /** 91 | * {@inheritDoc} 92 | */ 93 | @Override 94 | protected void exchangeValues(RedBlackTree.Node node, 95 | RedBlackTree.Node successor) { 96 | super.exchangeValues(node, successor); 97 | Node linkedNode = (Node) node; 98 | Node linkedSuccessor = (Node) successor; 99 | linkedNode.setSuccessor(linkedSuccessor.getSuccessor()); 100 | if (linkedNode.getSuccessor() != null) 101 | linkedNode.getSuccessor().setPredecessor(linkedNode); 102 | linkedSuccessor.setPredecessor(null); 103 | linkedSuccessor.setSuccessor(null); 104 | } 105 | 106 | /** 107 | * {@inheritDoc} 108 | */ 109 | @Override 110 | public RedBlackTree.Node getFirstNode() { 111 | return head; 112 | } 113 | 114 | /** 115 | * {@inheritDoc} 116 | */ 117 | @Override 118 | public RedBlackTree.Node getPredecessor(RedBlackTree.Node node) { 119 | return ((Node) node).getPredecessor(); 120 | } 121 | 122 | /** 123 | * {@inheritDoc} 124 | */ 125 | @Override 126 | public RedBlackTree.Node getSuccessor(RedBlackTree.Node node) { 127 | return ((Node) node).getSuccessor(); 128 | } 129 | 130 | /** 131 | * {@inheritDoc} 132 | */ 133 | @Override 134 | public RedBlackTree.Node insert(T value) { 135 | RedBlackTree.Node result = super.insert(value); 136 | 137 | if (result != null) { 138 | Node linkedNode = (Node) result; 139 | Node pred = (Node) super.getPredecessor(result); 140 | linkedNode.setPredecessor(pred); 141 | if (pred != null) { 142 | pred.setSuccessor(linkedNode); 143 | } 144 | Node succ = (Node) super.getSuccessor(result); 145 | linkedNode.setSuccessor(succ); 146 | if (succ != null) { 147 | succ.setPredecessor(linkedNode); 148 | } 149 | 150 | if (head == null) { 151 | head = getRoot(); 152 | } else { 153 | RedBlackTree.Node node = getPredecessor(head); 154 | if (node != null) { 155 | head = node; 156 | } 157 | } 158 | } 159 | 160 | return result; 161 | } 162 | 163 | /** 164 | * A red-black tree node augmented to store pointers to its predecessor and 165 | * successor. 166 | * 167 | * @author Kevin L. Stern 168 | */ 169 | public static class Node extends RedBlackTree.Node { 170 | private Node predecessor, successor; 171 | 172 | /** 173 | * Construct a node with the specified value. 174 | * 175 | * @param value 176 | * the value to associate with this node. 177 | */ 178 | public Node(T value) { 179 | super(value); 180 | } 181 | 182 | /** 183 | * Get the predecessor node. 184 | * 185 | * @return the predecessor of this node in the tree. 186 | */ 187 | public Node getPredecessor() { 188 | return predecessor; 189 | } 190 | 191 | /** 192 | * Get the successor node. 193 | * 194 | * @return the successor of this node in the tree. 195 | */ 196 | public Node getSuccessor() { 197 | return successor; 198 | } 199 | 200 | /** 201 | * Set the predecessor node. 202 | * 203 | * @param node 204 | * the predecessor of this node in the tree. 205 | */ 206 | protected void setPredecessor(Node node) { 207 | predecessor = node; 208 | } 209 | 210 | /** 211 | * Set the successor node. 212 | * 213 | * @param node 214 | * the successor of this node in the tree. 215 | */ 216 | protected void setSuccessor(Node node) { 217 | successor = node; 218 | } 219 | } 220 | } -------------------------------------------------------------------------------- /src/test/java/blogspot/software_and_algorithms/stern_library/data_structure/RedBlackTreeTest.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.data_structure; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | import java.util.NoSuchElementException; 8 | import java.util.TreeSet; 9 | 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | 13 | /* Copyright (C) 2012 Kevin L. Stern. 14 | * 15 | * Permission is hereby granted, free of charge, to any person obtaining a copy 16 | * of this software and associated documentation files (the "Software"), to deal 17 | * in the Software without restriction, including without limitation the rights 18 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | * copies of the Software, and to permit persons to whom the Software is 20 | * furnished to do so, subject to the following conditions: 21 | * 22 | * The above copyright notice and this permission notice shall be included in 23 | * all copies or substantial portions of the Software. 24 | * 25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | * SOFTWARE. 32 | */ 33 | 34 | /** 35 | * Test class for RedBlackTree. 36 | * 37 | * @author Kevin L. Stern 38 | */ 39 | public class RedBlackTreeTest { 40 | private static void equalsHelper(Collection master, 41 | RedBlackTree tree) { 42 | Assert.assertEquals(master.size(), tree.getSize()); 43 | Iterator iMaster = master.iterator(); 44 | Iterator iTree = tree.iterator(); 45 | while (iMaster.hasNext()) { 46 | Assert.assertTrue(iTree.hasNext()); 47 | Assert.assertEquals(iMaster.next(), iTree.next()); 48 | } 49 | Assert.assertFalse(iTree.hasNext()); 50 | } 51 | 52 | @Test 53 | public void testContains() { 54 | RedBlackTree tree = new RedBlackTree(); 55 | for (int j = 0; j < 100; j++) { 56 | Assert.assertFalse(tree.contains(j)); 57 | tree.insert(j); 58 | Assert.assertTrue(tree.contains(j)); 59 | } 60 | } 61 | 62 | @Test 63 | public void testDelete() { 64 | List master = new ArrayList(); 65 | RedBlackTree tree = new RedBlackTree(); 66 | for (int j = 0; j < 100; j++) { 67 | tree.insert(j); 68 | master.add(j); 69 | } 70 | while (!master.isEmpty()) { 71 | Integer val = master.get(master.size() >> 1); 72 | master.remove(val); 73 | tree.delete(val); 74 | equalsHelper(master, tree); 75 | } 76 | } 77 | 78 | @Test 79 | public void testInsert() { 80 | TreeSet master = new TreeSet(); 81 | RedBlackTree tree = new RedBlackTree(); 82 | for (int j = 99; j >= 0; j--) { 83 | tree.insert(j); 84 | master.add(j); 85 | } 86 | equalsHelper(master, tree); 87 | } 88 | 89 | @Test 90 | public void testIterator() { 91 | RedBlackTree tree = new RedBlackTree(); 92 | for (int j = 0; j < 100; j++) { 93 | tree.insert(j); 94 | } 95 | Iterator i = tree.iterator(); 96 | try { 97 | i.remove(); 98 | Assert.fail(); 99 | } catch (NoSuchElementException e) { 100 | 101 | } 102 | Assert.assertTrue(i.hasNext()); 103 | int test = 0; 104 | for (; i.hasNext();) { 105 | Integer val = i.next(); 106 | Assert.assertEquals(Integer.valueOf(test++), val); 107 | if ((val & 1) == 0) { 108 | i.remove(); 109 | } 110 | } 111 | try { 112 | i.next(); 113 | Assert.fail(); 114 | } catch (NoSuchElementException e) { 115 | 116 | } 117 | i = tree.iterator(); 118 | for (int j = 1; j < 100; j += 2) { 119 | Assert.assertEquals(Integer.valueOf(j), i.next()); 120 | i.remove(); 121 | } 122 | try { 123 | i.remove(); 124 | Assert.fail(); 125 | } catch (NoSuchElementException e) { 126 | 127 | } 128 | Assert.assertTrue(tree.isEmpty()); 129 | } 130 | 131 | @Test 132 | public void testNull() { 133 | RedBlackTree tree = new RedBlackTree(); 134 | try { 135 | tree.insert(null); 136 | Assert.fail(); 137 | } catch (NullPointerException e) { 138 | 139 | } 140 | try { 141 | tree.getPredecessor(null); 142 | Assert.fail(); 143 | } catch (NullPointerException e) { 144 | 145 | } 146 | try { 147 | tree.getSuccessor(null); 148 | Assert.fail(); 149 | } catch (NullPointerException e) { 150 | 151 | } 152 | Assert.assertNull(tree.delete(null)); 153 | Assert.assertNull(tree.getNode(null)); 154 | Assert.assertFalse(tree.contains(null)); 155 | 156 | tree.insert(0); 157 | try { 158 | tree.insert(null); 159 | Assert.fail(); 160 | } catch (NullPointerException e) { 161 | 162 | } 163 | try { 164 | tree.getPredecessor(null); 165 | Assert.fail(); 166 | } catch (NullPointerException e) { 167 | 168 | } 169 | try { 170 | tree.getSuccessor(null); 171 | Assert.fail(); 172 | } catch (NullPointerException e) { 173 | 174 | } 175 | Assert.assertNull(tree.delete(null)); 176 | Assert.assertNull(tree.getNode(null)); 177 | Assert.assertFalse(tree.contains(null)); 178 | } 179 | 180 | @Test 181 | public void testPredecessor() { 182 | RedBlackTree tree = new RedBlackTree(); 183 | for (int j = 0; j < 100; j++) { 184 | tree.insert(j); 185 | } 186 | for (int j = 1; j < 100; j++) { 187 | Assert.assertEquals(Integer.valueOf(j - 1), 188 | tree.getPredecessor(tree.getNode(j)).getValue()); 189 | } 190 | Assert.assertNull(tree.getPredecessor(tree.getNode(0))); 191 | } 192 | 193 | @Test 194 | public void testSuccessor() { 195 | RedBlackTree tree = new RedBlackTree(); 196 | for (int j = 0; j < 100; j++) { 197 | tree.insert(j); 198 | } 199 | for (int j = 0; j < 99; j++) { 200 | Assert.assertEquals(Integer.valueOf(j + 1), 201 | tree.getSuccessor(tree.getNode(j)).getValue()); 202 | } 203 | Assert.assertNull(tree.getSuccessor(tree.getNode(99))); 204 | } 205 | 206 | @Test 207 | public void testToString() { 208 | RedBlackTree tree = new RedBlackTree(); 209 | Assert.assertEquals("{}", tree.toString()); 210 | tree.insert(2); 211 | Assert.assertEquals("{2}", tree.toString()); 212 | tree.insert(3); 213 | Assert.assertEquals("{2, 3}", tree.toString()); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/test/java/blogspot/software_and_algorithms/stern_library/data_structure/IntervalTest.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.data_structure; 2 | 3 | import junit.framework.Assert; 4 | 5 | import org.junit.Test; 6 | 7 | /* Copyright (c) 2012 Kevin L. Stern 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | /** 29 | * Test class for Interval. 30 | * 31 | * @author Kevin L. Stern 32 | */ 33 | public class IntervalTest { 34 | @Test 35 | public void testCompareTo() { 36 | Interval i1 = new Interval(0.0, true, 10.0, true); 37 | Interval i2 = new Interval(0.0, true, 10.0, false); 38 | Interval i3 = new Interval(0.0, false, 10.0, true); 39 | Interval i4 = new Interval(1.0, true, 10.0, true); 40 | Interval i5 = new Interval(0.0, true, 11.0, true); 41 | 42 | Assert.assertTrue(i1.compareTo(i1) == 0); 43 | 44 | Assert.assertTrue(i1.compareTo(i2) < 0); 45 | Assert.assertTrue(i1.compareTo(i3) < 0); 46 | Assert.assertTrue(i1.compareTo(i4) < 0); 47 | Assert.assertTrue(i1.compareTo(i5) < 0); 48 | 49 | Assert.assertTrue(i2.compareTo(i1) > 0); 50 | Assert.assertTrue(i3.compareTo(i1) > 0); 51 | Assert.assertTrue(i4.compareTo(i1) > 0); 52 | Assert.assertTrue(i5.compareTo(i1) > 0); 53 | } 54 | 55 | @Test 56 | public void testContainsInterval() { 57 | Interval i1 = new Interval(0.0, true, 10.0, true); 58 | Interval i2 = new Interval(0.0, true, 10.0, false); 59 | Interval i3 = new Interval(0.0, false, 10.0, true); 60 | Interval i4 = new Interval(0.0, false, 10.0, false); 61 | Interval i5 = new Interval(1.0, true, 9.0, true); 62 | 63 | Assert.assertTrue(i1.contains(i1)); 64 | Assert.assertTrue(i1.contains(i2)); 65 | Assert.assertTrue(i1.contains(i3)); 66 | Assert.assertTrue(i1.contains(i4)); 67 | Assert.assertTrue(i1.contains(i5)); 68 | 69 | Assert.assertFalse(i2.contains(i1)); 70 | Assert.assertFalse(i3.contains(i1)); 71 | Assert.assertFalse(i4.contains(i1)); 72 | Assert.assertFalse(i5.contains(i1)); 73 | } 74 | 75 | @Test 76 | public void testContainsPoint() { 77 | Interval i; 78 | i = new Interval(0.0, true, 10.0, true); 79 | for (int j = 0; j < 10; j++) { 80 | Assert.assertTrue(i.contains((double) j)); 81 | } 82 | i = new Interval(0.0, false, 10.0, true); 83 | Assert.assertFalse(i.contains((double) 0)); 84 | for (int j = 1; j < 10; j++) { 85 | Assert.assertTrue(i.contains((double) j)); 86 | } 87 | i = new Interval(0.0, false, 10.0, false); 88 | Assert.assertFalse(i.contains((double) 0)); 89 | Assert.assertFalse(i.contains((double) 10)); 90 | for (int j = 1; j < 9; j++) { 91 | Assert.assertTrue(i.contains((double) j)); 92 | } 93 | } 94 | 95 | @Test 96 | public void testEquals() { 97 | Interval i1 = new Interval(0.0, true, 10.0, true); 98 | Interval i2 = new Interval(0.0, true, 10.0, false); 99 | Interval i3 = new Interval(0.0, false, 10.0, true); 100 | Interval i4 = new Interval(0.0, false, 10.0, false); 101 | Interval i5 = new Interval(1.0, true, 9.0, true); 102 | 103 | Assert.assertTrue(i1.equals(new Interval(i1.getLow(), i1 104 | .isClosedOnLow(), i1.getHigh(), i1.isClosedOnHigh()))); 105 | 106 | Assert.assertFalse(i1.equals(i2)); 107 | Assert.assertFalse(i1.equals(i3)); 108 | Assert.assertFalse(i1.equals(i4)); 109 | Assert.assertFalse(i1.equals(i5)); 110 | 111 | Assert.assertFalse(i2.equals(i1)); 112 | Assert.assertFalse(i3.equals(i1)); 113 | Assert.assertFalse(i4.equals(i1)); 114 | Assert.assertFalse(i5.equals(i1)); 115 | } 116 | 117 | @Test 118 | public void testHashcode() { 119 | Interval i = new Interval(0.0, true, 10.0, true); 120 | Assert.assertFalse(i.hashCode() == new Interval(1.0, true, 10.0, 121 | true).hashCode()); 122 | Assert.assertFalse(i.hashCode() == new Interval(0.0, true, 11.0, 123 | true).hashCode()); 124 | Assert.assertFalse(i.hashCode() == new Interval(0.0, true, 10.0, 125 | false).hashCode()); 126 | Assert.assertFalse(i.hashCode() == new Interval(0.0, false, 10.0, 127 | true).hashCode()); 128 | Assert.assertFalse(i.hashCode() == new Interval(0.0, false, 10.0, 129 | false).hashCode()); 130 | } 131 | 132 | @Test 133 | public void testOverlaps() { 134 | Interval i1 = new Interval(5.0, true, 10.0, true); 135 | Interval i2 = new Interval(10.0, true, 15.0, true); 136 | Interval i3 = new Interval(10.0, false, 15.0, true); 137 | Interval i4 = new Interval(0.0, true, 5.0, true); 138 | Interval i5 = new Interval(0.0, true, 5.0, false); 139 | Interval i6 = new Interval(6.0, false, 9.0, false); 140 | 141 | Assert.assertTrue(i1.overlaps(i1)); 142 | 143 | Assert.assertTrue(i1.overlaps(i2)); 144 | Assert.assertFalse(i1.overlaps(i3)); 145 | Assert.assertTrue(i1.overlaps(i4)); 146 | Assert.assertFalse(i1.overlaps(i5)); 147 | Assert.assertTrue(i1.overlaps(i6)); 148 | 149 | Assert.assertTrue(i2.overlaps(i1)); 150 | Assert.assertFalse(i3.overlaps(i1)); 151 | Assert.assertTrue(i4.overlaps(i1)); 152 | Assert.assertFalse(i5.overlaps(i1)); 153 | Assert.assertTrue(i6.overlaps(i1)); 154 | } 155 | 156 | @Test 157 | public void testToString() { 158 | Assert.assertEquals("(0, 1)", new Interval(0, false, 1, false).toString()); 159 | Assert.assertEquals("(0, 1]", new Interval(0, false, 1, true).toString()); 160 | Assert.assertEquals("[0, 1)", new Interval(0, true, 1, false).toString()); 161 | Assert.assertEquals("[0, 1]", new Interval(0, true, 1, true).toString()); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/blogspot/software_and_algorithms/stern_library/string/DamerauLevenshteinAlgorithm.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.string; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /* Copyright (c) 2012 Kevin L. Stern 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /** 28 | * The Damerau-Levenshtein Algorithm is an extension to the Levenshtein 29 | * Algorithm which solves the edit distance problem between a source string and 30 | * a target string with the following operations: 31 | * 32 | *
    33 | *
  • Character Insertion
  • 34 | *
  • Character Deletion
  • 35 | *
  • Character Replacement
  • 36 | *
  • Adjacent Character Swap
  • 37 | *
38 | * 39 | * Note that the adjacent character swap operation is an edit that may be 40 | * applied when two adjacent characters in the source string match two adjacent 41 | * characters in the target string, but in reverse order, rather than a general 42 | * allowance for adjacent character swaps. 43 | *

44 | * 45 | * This implementation allows the client to specify the costs of the various 46 | * edit operations with the restriction that the cost of two swap operations 47 | * must not be less than the cost of a delete operation followed by an insert 48 | * operation. This restriction is required to preclude two swaps involving the 49 | * same character being required for optimality which, in turn, enables a fast 50 | * dynamic programming solution. 51 | *

52 | * 53 | * The running time of the Damerau-Levenshtein algorithm is O(n*m) where n is 54 | * the length of the source string and m is the length of the target string. 55 | * This implementation consumes O(n*m) space. 56 | * 57 | * @author Kevin L. Stern 58 | */ 59 | public class DamerauLevenshteinAlgorithm { 60 | private final int deleteCost, insertCost, replaceCost, swapCost; 61 | 62 | /** 63 | * Constructor. 64 | * 65 | * @param deleteCost 66 | * the cost of deleting a character. 67 | * @param insertCost 68 | * the cost of inserting a character. 69 | * @param replaceCost 70 | * the cost of replacing a character. 71 | * @param swapCost 72 | * the cost of swapping two adjacent characters. 73 | */ 74 | public DamerauLevenshteinAlgorithm(int deleteCost, int insertCost, 75 | int replaceCost, int swapCost) { 76 | /* 77 | * Required to facilitate the premise to the algorithm that two swaps of the 78 | * same character are never required for optimality. 79 | */ 80 | if (2 * swapCost < insertCost + deleteCost) { 81 | throw new IllegalArgumentException("Unsupported cost assignment"); 82 | } 83 | this.deleteCost = deleteCost; 84 | this.insertCost = insertCost; 85 | this.replaceCost = replaceCost; 86 | this.swapCost = swapCost; 87 | } 88 | 89 | /** 90 | * Compute the Damerau-Levenshtein distance between the specified source 91 | * string and the specified target string. 92 | */ 93 | public int execute(String source, String target) { 94 | if (source.length() == 0) { 95 | return target.length() * insertCost; 96 | } 97 | if (target.length() == 0) { 98 | return source.length() * deleteCost; 99 | } 100 | int[][] table = new int[source.length()][target.length()]; 101 | Map sourceIndexByCharacter = new HashMap(); 102 | if (source.charAt(0) != target.charAt(0)) { 103 | table[0][0] = Math.min(replaceCost, deleteCost + insertCost); 104 | } 105 | sourceIndexByCharacter.put(source.charAt(0), 0); 106 | for (int i = 1; i < source.length(); i++) { 107 | int deleteDistance = table[i - 1][0] + deleteCost; 108 | int insertDistance = (i + 1) * deleteCost + insertCost; 109 | int matchDistance = i * deleteCost 110 | + (source.charAt(i) == target.charAt(0) ? 0 : replaceCost); 111 | table[i][0] = Math.min(Math.min(deleteDistance, insertDistance), 112 | matchDistance); 113 | } 114 | for (int j = 1; j < target.length(); j++) { 115 | int deleteDistance = (j + 1) * insertCost + deleteCost; 116 | int insertDistance = table[0][j - 1] + insertCost; 117 | int matchDistance = j * insertCost 118 | + (source.charAt(0) == target.charAt(j) ? 0 : replaceCost); 119 | table[0][j] = Math.min(Math.min(deleteDistance, insertDistance), 120 | matchDistance); 121 | } 122 | for (int i = 1; i < source.length(); i++) { 123 | int maxSourceLetterMatchIndex = source.charAt(i) == target.charAt(0) ? 0 124 | : -1; 125 | for (int j = 1; j < target.length(); j++) { 126 | Integer candidateSwapIndex = sourceIndexByCharacter.get(target 127 | .charAt(j)); 128 | int jSwap = maxSourceLetterMatchIndex; 129 | int deleteDistance = table[i - 1][j] + deleteCost; 130 | int insertDistance = table[i][j - 1] + insertCost; 131 | int matchDistance = table[i - 1][j - 1]; 132 | if (source.charAt(i) != target.charAt(j)) { 133 | matchDistance += replaceCost; 134 | } else { 135 | maxSourceLetterMatchIndex = j; 136 | } 137 | int swapDistance; 138 | if (candidateSwapIndex != null && jSwap != -1) { 139 | int iSwap = candidateSwapIndex; 140 | int preSwapCost; 141 | if (iSwap == 0 && jSwap == 0) { 142 | preSwapCost = 0; 143 | } else { 144 | preSwapCost = table[Math.max(0, iSwap - 1)][Math.max(0, jSwap - 1)]; 145 | } 146 | swapDistance = preSwapCost + (i - iSwap - 1) * deleteCost 147 | + (j - jSwap - 1) * insertCost + swapCost; 148 | } else { 149 | swapDistance = Integer.MAX_VALUE; 150 | } 151 | table[i][j] = Math.min(Math.min(Math 152 | .min(deleteDistance, insertDistance), matchDistance), swapDistance); 153 | } 154 | sourceIndexByCharacter.put(source.charAt(i), i); 155 | } 156 | return table[source.length() - 1][target.length() - 1]; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/blogspot/software_and_algorithms/stern_library/data_structure/Interval.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.data_structure; 2 | 3 | /* Copyright (c) 2012 Kevin L. Stern 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | /** 25 | * An interval is the subset of elements which fall between (with 26 | * respect to a total order) two endpoint elements of a set. An interval that 27 | * contains its endpoints is closed, an interval that contains one of 28 | * its endpoints but not the other is half open and an interval that 29 | * does not contain either of its endpoints is open. This class 30 | * encapsulates the concept of an interval and uses a class's natural order. 31 | * 32 | * @author Kevin L. Stern 33 | */ 34 | public class Interval> implements 35 | Comparable> { 36 | private T low, high; 37 | private boolean isClosedOnLow, isClosedOnHigh; 38 | private int hashCode = 0; 39 | 40 | /** 41 | * Construct a new instance with the specified low and high endpoints. 42 | * 43 | * @param low 44 | * the low endpoint. 45 | * @param isClosedOnLow 46 | * true if this interval contains its low endpoint, false otherwise. 47 | * @param high 48 | * the high endpoint. 49 | * @param isClosedOnHigh 50 | * true if this interval contains its high endpoint, false otherwise. 51 | */ 52 | public Interval(T low, boolean isClosedOnLow, T high, boolean isClosedOnHigh) { 53 | if (low == null) { 54 | throw new NullPointerException("low endpoint is null"); 55 | } else if (high == null) { 56 | throw new NullPointerException("high endpoint is null"); 57 | } 58 | this.low = low; 59 | this.isClosedOnLow = isClosedOnLow; 60 | this.high = high; 61 | this.isClosedOnHigh = isClosedOnHigh; 62 | } 63 | 64 | /** 65 | * {@inheritDoc} 66 | */ 67 | @Override 68 | public int compareTo(Interval o) { 69 | int result = low.compareTo(o.low); 70 | if (result == 0) { 71 | if (isClosedOnLow != o.isClosedOnLow) { 72 | result = isClosedOnLow ? -1 : 1; 73 | } else { 74 | result = high.compareTo(o.high); 75 | if (result == 0) { 76 | if (isClosedOnHigh != o.isClosedOnHigh) { 77 | result = isClosedOnHigh ? -1 : 1; 78 | } 79 | } 80 | } 81 | } 82 | return result; 83 | } 84 | 85 | /** 86 | * Test whether or not this interval contains the specified interval. An 87 | * interval is contained by another precisely when all of its values are 88 | * contained by the other. 89 | * 90 | * @param interval 91 | * the query interval, non-null. 92 | * @return true if this interval contains the specified interval, false 93 | * otherwise. 94 | */ 95 | public boolean contains(Interval interval) { 96 | boolean lowIsLowerBound = low.equals(interval.low) 97 | && (isClosedOnLow || !interval.isClosedOnLow) 98 | || low.compareTo(interval.low) < 0; 99 | boolean highIsUpperBound = high.equals(interval.high) 100 | && (isClosedOnHigh || !interval.isClosedOnHigh) 101 | || high.compareTo(interval.high) > 0; 102 | return lowIsLowerBound && highIsUpperBound; 103 | } 104 | 105 | /** 106 | * Test whether or not this interval contains the specified value. 107 | * 108 | * @param value 109 | * the query value, non-null. 110 | * @return true if this interval contains the specified value, false 111 | * otherwise. 112 | */ 113 | public boolean contains(T value) { 114 | return value.equals(low) && isClosedOnLow || value.equals(high) 115 | && isClosedOnHigh || low.compareTo(value) < 0 116 | && value.compareTo(high) < 0; 117 | } 118 | 119 | /** 120 | * {@inheritDoc} 121 | */ 122 | @Override 123 | public boolean equals(Object obj) { 124 | if (this == obj) 125 | return true; 126 | if (obj == null) 127 | return false; 128 | if (getClass() != obj.getClass()) 129 | return false; 130 | Interval other = (Interval) obj; 131 | if (high == null) { 132 | if (other.high != null) 133 | return false; 134 | } else if (!high.equals(other.high)) 135 | return false; 136 | if (isClosedOnHigh != other.isClosedOnHigh) 137 | return false; 138 | if (low == null) { 139 | if (other.low != null) 140 | return false; 141 | } else if (!low.equals(other.low)) 142 | return false; 143 | if (isClosedOnLow != other.isClosedOnLow) 144 | return false; 145 | return true; 146 | } 147 | 148 | /** 149 | * Get the high endpoint. 150 | * 151 | * @return the high endpoint. 152 | */ 153 | public T getHigh() { 154 | return high; 155 | } 156 | 157 | /** 158 | * Get the low endpoint. 159 | * 160 | * @return the low endpoint. 161 | */ 162 | public T getLow() { 163 | return low; 164 | } 165 | 166 | /** 167 | * {@inheritDoc} 168 | */ 169 | @Override 170 | public int hashCode() { 171 | if (hashCode == 0) { 172 | final int prime = 31; 173 | int result = 1; 174 | result = prime * result + ((high == null) ? 0 : high.hashCode()); 175 | result = prime * result + (isClosedOnHigh ? 1231 : 1237); 176 | result = prime * result + ((low == null) ? 0 : low.hashCode()); 177 | result = prime * result + (isClosedOnLow ? 1231 : 1237); 178 | hashCode = result; 179 | } 180 | return hashCode; 181 | } 182 | 183 | /** 184 | * 185 | * @return true if this interval is closed at its high endpoint, false 186 | * otherwise. 187 | */ 188 | public boolean isClosedOnHigh() { 189 | return isClosedOnHigh; 190 | } 191 | 192 | /** 193 | * 194 | * @return true if the interval is closed at its low endpoint, false 195 | * otherwise. 196 | */ 197 | public boolean isClosedOnLow() { 198 | return isClosedOnLow; 199 | } 200 | 201 | /** 202 | * Test whether or not this interval and the specified interval overlap. Two 203 | * intervals overlap precisely when their intersection is non-empty. 204 | * 205 | * @param interval 206 | * the query interval. 207 | * @return true if this interval and the specified interval overlap, false 208 | * otherwise. 209 | */ 210 | public boolean overlaps(Interval interval) { 211 | if (interval.isClosedOnLow && contains(interval.low) || isClosedOnLow 212 | && interval.contains(low)) { 213 | return true; 214 | } 215 | if (!interval.isClosedOnLow && low.compareTo(interval.low) <= 0 216 | && interval.low.compareTo(high) < 0 || !isClosedOnLow 217 | && interval.low.compareTo(low) <= 0 && low.compareTo(interval.high) < 0) { 218 | return true; 219 | } 220 | return false; 221 | } 222 | 223 | /** 224 | * {@inheritDoc} 225 | */ 226 | @Override 227 | public String toString() { 228 | String format; 229 | if (isClosedOnLow) { 230 | if (isClosedOnHigh) { 231 | format = "[%s, %s]"; 232 | } else { 233 | format = "[%s, %s)"; 234 | } 235 | } else { 236 | if (isClosedOnHigh) { 237 | format = "(%s, %s]"; 238 | } else { 239 | format = "(%s, %s)"; 240 | } 241 | } 242 | return String.format(format, low.toString(), high.toString()); 243 | } 244 | } -------------------------------------------------------------------------------- /src/main/java/blogspot/software_and_algorithms/stern_library/geometry/ClosestPointPairAlgorithm.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.geometry; 2 | 3 | import java.awt.geom.Point2D; 4 | import java.util.ArrayList; 5 | import java.util.Collection; 6 | import java.util.Collections; 7 | import java.util.HashSet; 8 | import java.util.List; 9 | import java.util.Set; 10 | 11 | /* Copyright (c) 2012 Kevin L. Stern 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining a copy 14 | * of this software and associated documentation files (the "Software"), to deal 15 | * in the Software without restriction, including without limitation the rights 16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | * copies of the Software, and to permit persons to whom the Software is 18 | * furnished to do so, subject to the following conditions: 19 | * 20 | * The above copyright notice and this permission notice shall be included in 21 | * all copies or substantial portions of the Software. 22 | * 23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | * SOFTWARE. 30 | */ 31 | 32 | /** 33 | * An implementation of a divide-and-conquer algorithm for computing the closest 34 | * pair of elements of a set of points. The algorithm consists of constructing 35 | * an ordered list of points, then recursively dividing the list into a left and 36 | * right sublist towards finding the closest point pairs for each sublist. The 37 | * two sub-results are merged by selecting the optimal among them and all closer 38 | * point pairs that cross the boundary of separation. Happily, only a linear 39 | * amount of work is required to find all closer point pairs that cross the 40 | * boundary, giving a total runtime of O(n*log(n)) for the algorithm. 41 | * 42 | * @author Kevin L. Stern 43 | */ 44 | public class ClosestPointPairAlgorithm { 45 | /** 46 | * Find the closest pair of points among p1, p2 and p3. 47 | */ 48 | private static PairStructure closestPair(Point2D p1, Point2D p2, Point2D p3) { 49 | double d1 = p1.distanceSq(p2); 50 | double d2 = p2.distanceSq(p3); 51 | double d3 = p1.distanceSq(p3); 52 | if (d1 < d2) { 53 | if (d1 < d3) { 54 | return new PairStructure(p1, p2, d1); 55 | } else { 56 | return new PairStructure(p1, p3, d3); 57 | } 58 | } else { 59 | if (d2 < d3) { 60 | return new PairStructure(p2, p3, d2); 61 | } else { 62 | return new PairStructure(p1, p3, d3); 63 | } 64 | } 65 | } 66 | 67 | private List pointsOrderedByXCoordinate, pointsOrderedByYCoordinate; 68 | 69 | /** 70 | * Construct an instance of the algorithm for the specified point Collection. 71 | * 72 | * @param points 73 | * the Collection of points through which to search for the closest 74 | * pair. 75 | */ 76 | public ClosestPointPairAlgorithm(Collection points) { 77 | if (points == null) { 78 | throw new NullPointerException("points is null"); 79 | } 80 | if (points.size() < 2) { 81 | throw new IllegalArgumentException("points is too small"); 82 | } 83 | pointsOrderedByXCoordinate = new ArrayList(points); 84 | Collections.sort(pointsOrderedByXCoordinate, (o1, o2) -> { 85 | double delta = o1.getX() - o2.getX(); 86 | if (delta == 0.0) { 87 | delta = o1.getY() - o2.getY(); 88 | } 89 | return delta < 0 ? -1 : delta > 0 ? 1 : 0; 90 | }); 91 | pointsOrderedByYCoordinate = new ArrayList(points); 92 | Collections.sort(pointsOrderedByYCoordinate, (o1, o2) -> { 93 | double delta = o1.getY() - o2.getY(); 94 | if (delta == 0.0) { 95 | delta = o1.getX() - o2.getX(); 96 | } 97 | return delta < 0 ? -1 : delta > 0 ? 1 : 0; 98 | }); 99 | } 100 | 101 | /** 102 | * Internal helper method which implements the closest point pair algorithm. 103 | * 104 | * @param low 105 | * the starting index, inclusive, of the sublist in which to search 106 | * for the closest point pair. 107 | * @param high 108 | * the ending index, exclusive, of the sublist in which to search for 109 | * the closest point pair. 110 | * @param localPointsOrderedByYCoordinate 111 | * the points from the target sublist, ordered by y coordinate. 112 | * @return a PairStructure containing the closest point pair among elements of 113 | * the target sublist. 114 | */ 115 | protected PairStructure closestPair(int low, int high, 116 | List localPointsOrderedByYCoordinate) { 117 | int size = high - low; 118 | if (size == 3) { 119 | return closestPair( 120 | pointsOrderedByXCoordinate.get(low), 121 | pointsOrderedByXCoordinate.get(low + 1), 122 | pointsOrderedByXCoordinate.get(low + 2)); 123 | } else if (size == 2) { 124 | Point2D p1 = pointsOrderedByXCoordinate.get(low); 125 | Point2D p2 = pointsOrderedByXCoordinate.get(low + 1); 126 | return new PairStructure(p1, p2, p1.distanceSq(p2)); 127 | } 128 | 129 | int mid = (low + high) >> 1; // (low + high) / 2 130 | Set leftSubtreeMemberSet = new HashSet(mid - low); 131 | for (int j = low; j < mid; j++) { 132 | leftSubtreeMemberSet.add(pointsOrderedByXCoordinate.get(j)); 133 | } 134 | 135 | /* 136 | * Construct the lists of points ordered by y coordinate for the left and 137 | * right subtrees in linear time by drawing upon the master list of points 138 | * ordered by y coordinate. 139 | */ 140 | List leftPointsOrderedByYCoordinate = new ArrayList(mid 141 | - low); 142 | List rightPointsOrderedByYCoordinate = new ArrayList(high 143 | - mid); 144 | for (Point2D next : localPointsOrderedByYCoordinate) { 145 | if (leftSubtreeMemberSet.contains(next)) { 146 | leftPointsOrderedByYCoordinate.add(next); 147 | } else { 148 | rightPointsOrderedByYCoordinate.add(next); 149 | } 150 | } 151 | 152 | PairStructure leftSubtreeResult = closestPair( 153 | low, 154 | mid, 155 | leftPointsOrderedByYCoordinate); 156 | PairStructure rightSubtreeResult = closestPair( 157 | mid, 158 | high, 159 | rightPointsOrderedByYCoordinate); 160 | PairStructure result = leftSubtreeResult.distanceSq < rightSubtreeResult.distanceSq 161 | ? leftSubtreeResult 162 | : rightSubtreeResult; 163 | 164 | List boundaryPointsOrderedByYCoordinate = new ArrayList(); 165 | double midXCoordinate = pointsOrderedByXCoordinate.get(mid).getX(); 166 | for (Point2D next : localPointsOrderedByYCoordinate) { 167 | double v = next.getX() - midXCoordinate; 168 | if (v * v < result.distanceSq) { 169 | boundaryPointsOrderedByYCoordinate.add(next); 170 | } 171 | } 172 | for (int i = 0; i < boundaryPointsOrderedByYCoordinate.size(); ++i) { 173 | Point2D currentPoint = boundaryPointsOrderedByYCoordinate.get(i); 174 | int index; 175 | for (int j = 1; (index = i + j) < boundaryPointsOrderedByYCoordinate 176 | .size(); ++j) { 177 | Point2D testPoint = boundaryPointsOrderedByYCoordinate.get(index); 178 | /* 179 | * The number of points that can be situated within the boundary so that 180 | * their y coordinate is within the minimum of the result distances for 181 | * the left and right subtrees from currentPoint.getY() is bounded by a 182 | * constant, since that distance value spatially limits the number of 183 | * points that can be packed near one another on each side of the 184 | * boundary. 185 | */ 186 | double v = testPoint.getY() - currentPoint.getY(); 187 | if (v * v >= result.distanceSq) { 188 | break; 189 | } 190 | double testDistance = currentPoint.distanceSq(testPoint); 191 | if (testDistance < result.distanceSq) { 192 | result = new PairStructure(currentPoint, testPoint, testDistance); 193 | } 194 | } 195 | } 196 | 197 | return result; 198 | } 199 | 200 | /** 201 | * Execute the algorithm. 202 | * 203 | * @return a Point2D[] containing exactly two elements which are the closest 204 | * pair of points among those in the collection used to construct this 205 | * instance. 206 | */ 207 | public Point2D[] execute() { 208 | PairStructure result = closestPair( 209 | 0, 210 | pointsOrderedByXCoordinate.size(), 211 | pointsOrderedByYCoordinate); 212 | return new Point2D[] { result.p1, result.p2 }; 213 | } 214 | 215 | /** 216 | * Convenience data structure to hold a pair of points along with their 217 | * distance from one another. 218 | */ 219 | protected static class PairStructure { 220 | private Point2D p1, p2; 221 | private double distanceSq; 222 | 223 | /** 224 | * Constructor. 225 | * 226 | * @param p1 227 | * the first point. 228 | * @param p2 229 | * the second point. 230 | * @param distanceSq 231 | * the distance between p1 and p2, squared. 232 | */ 233 | public PairStructure(Point2D p1, Point2D p2, double distanceSq) { 234 | this.p1 = p1; 235 | this.p2 = p2; 236 | this.distanceSq = distanceSq; 237 | } 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/test/java/blogspot/software_and_algorithms/stern_library/data_structure/DynamicIntervalTreeTest.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.data_structure; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | import org.junit.AfterClass; 10 | import org.junit.Assert; 11 | import org.junit.BeforeClass; 12 | import org.junit.Test; 13 | 14 | /* Copyright (c) 2012 Kevin L. Stern 15 | * 16 | * Permission is hereby granted, free of charge, to any person obtaining a copy 17 | * of this software and associated documentation files (the "Software"), to deal 18 | * in the Software without restriction, including without limitation the rights 19 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | * copies of the Software, and to permit persons to whom the Software is 21 | * furnished to do so, subject to the following conditions: 22 | * 23 | * The above copyright notice and this permission notice shall be included in 24 | * all copies or substantial portions of the Software. 25 | * 26 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 | * SOFTWARE. 33 | */ 34 | 35 | /** 36 | * Test class for DynamicIntervalTree. 37 | * 38 | * @author Kevin L. Stern 39 | */ 40 | public class DynamicIntervalTreeTest { 41 | private static List> masterClosed; 42 | private static List> masterHalfOpenLow; 43 | private static List> masterHalfOpenHigh; 44 | private static List> masterOpen; 45 | private static DynamicIntervalTree> masterClosedTree; 46 | private static DynamicIntervalTree> masterHalfOpenLowTree; 47 | private static DynamicIntervalTree> masterHalfOpenHighTree; 48 | private static DynamicIntervalTree> masterOpenTree; 49 | 50 | private static void executeContainingIntervalsTest(List queryPoints, 51 | List> master, 52 | DynamicIntervalTree> tree) { 53 | Set> result = new HashSet>(); 54 | for (Double queryPoint : queryPoints) { 55 | result.addAll(tree.fetchContainingIntervals(queryPoint)); 56 | Assert.assertEquals(fetchContainingIntervals(master, queryPoint), result); 57 | result.clear(); 58 | } 59 | } 60 | 61 | private static void executeOverlappingIntervalsTest(List> queryIntervals, 62 | List> master, 63 | DynamicIntervalTree> tree) { 64 | Set> result = new HashSet>(); 65 | for (Interval next : queryIntervals) { 66 | result.addAll(tree.fetchOverlappingIntervals(next)); 67 | Assert.assertEquals(fetchOverlappingIntervals(master, next), result); 68 | result.clear(); 69 | } 70 | } 71 | 72 | /** 73 | * Helper method to fetch the set of intervals from the master list that 74 | * contain the query point using the brute force method. This gives a correct 75 | * answer against which to compare the result from the interval tree. 76 | */ 77 | private static Set> fetchContainingIntervals(List> master, 78 | double queryPoint) { 79 | Set> result = new HashSet>(); 80 | for (Interval next : master) { 81 | if (next.contains(queryPoint)) { 82 | result.add(next); 83 | } 84 | } 85 | return result; 86 | } 87 | 88 | /** 89 | * Helper method to fetch the set of intervals from the master list that 90 | * overlap the query interval using the brute force method. This gives a 91 | * correct answer against which to compare the result from the interval tree. 92 | */ 93 | private static Set> fetchOverlappingIntervals(List> master, 94 | Interval queryInterval) { 95 | Set> result = new HashSet>(); 96 | for (Interval next : master) { 97 | if (next.overlaps(queryInterval)) { 98 | result.add(next); 99 | } 100 | } 101 | return result; 102 | } 103 | 104 | @BeforeClass 105 | public static void globalSetup() { 106 | masterClosed = new ArrayList>(); 107 | masterHalfOpenLow = new ArrayList>(); 108 | masterHalfOpenHigh = new ArrayList>(); 109 | masterOpen = new ArrayList>(); 110 | 111 | final double intervalWidth = 0.1; 112 | final double halfIntervalWidth = intervalWidth / 2; 113 | 114 | for (int j = 0; j < 10; j++) { 115 | Interval next; 116 | next = new Interval(intervalWidth * j, true, intervalWidth 117 | * (j + 1), true); 118 | masterClosed.add(next); 119 | masterClosed.add(new Interval(next.getLow() + halfIntervalWidth, 120 | true, next.getHigh() + halfIntervalWidth, true)); 121 | 122 | next = new Interval(intervalWidth * j, false, intervalWidth 123 | * (j + 1), true); 124 | masterHalfOpenLow.add(next); 125 | masterHalfOpenLow 126 | .add(new Interval(next.getLow() + halfIntervalWidth, false, 127 | next.getHigh() + halfIntervalWidth, true)); 128 | 129 | next = new Interval(intervalWidth * j, true, intervalWidth 130 | * (j + 1), false); 131 | masterHalfOpenHigh.add(next); 132 | masterHalfOpenHigh 133 | .add(new Interval(next.getLow() + halfIntervalWidth, true, 134 | next.getHigh() + halfIntervalWidth, false)); 135 | 136 | next = new Interval(intervalWidth * j, false, intervalWidth 137 | * (j + 1), false); 138 | masterOpen.add(next); 139 | masterOpen.add(new Interval(next.getLow() + halfIntervalWidth, 140 | false, next.getHigh() + halfIntervalWidth, false)); 141 | } 142 | masterClosedTree = makeTree(masterClosed); 143 | masterHalfOpenLowTree = makeTree(masterHalfOpenLow); 144 | masterHalfOpenHighTree = makeTree(masterHalfOpenHigh); 145 | masterOpenTree = makeTree(masterOpen); 146 | } 147 | 148 | @AfterClass 149 | public static void globalTeardown() { 150 | masterClosed = null; 151 | masterHalfOpenLow = null; 152 | masterHalfOpenHigh = null; 153 | masterOpen = null; 154 | masterClosedTree = null; 155 | masterHalfOpenLowTree = null; 156 | masterHalfOpenHighTree = null; 157 | masterOpenTree = null; 158 | } 159 | 160 | /** 161 | * Helper method to make and populate an IntervalTree based upon the specified 162 | * list of intervals. 163 | */ 164 | private static DynamicIntervalTree> makeTree(List> list) { 165 | DynamicIntervalTree> tree = new DynamicIntervalTree>(); 166 | for (Interval next : list) 167 | tree.insert(next); 168 | return tree; 169 | } 170 | 171 | @Test 172 | public void testClear() { 173 | DynamicIntervalTree> tree = makeTree(masterClosed); 174 | Assert.assertEquals(masterClosed.size(), tree.getSize()); 175 | tree.clear(); 176 | Assert.assertTrue(tree.fetchContainingIntervals(masterClosed.get(0) 177 | .getLow()).isEmpty()); 178 | Assert.assertEquals(0, tree.getSize()); 179 | } 180 | 181 | @Test 182 | public void testConstructionPointerEdgeCases() { 183 | List> master = new ArrayList>(); 184 | master.add(new Interval(0.0, true, 0.1, true)); 185 | master.add(new Interval(0.2, true, 0.3, true)); 186 | master.add(new Interval(0.4, true, 0.5, true)); 187 | master.add(new Interval(0.6, true, 0.7, true)); 188 | DynamicIntervalTree> tree = makeTree(master); 189 | Collection> result = tree.fetchContainingIntervals(0.25); 190 | Assert.assertEquals(1, result.size()); 191 | Assert.assertEquals(master.get(1), result.iterator().next()); 192 | } 193 | 194 | @Test 195 | public void testContainingIntervals() { 196 | List queryPointList = new ArrayList(); 197 | for (Interval next : masterClosed) { 198 | queryPointList.add(next.getLow()); 199 | } 200 | executeContainingIntervalsTest(queryPointList, masterClosed, 201 | masterClosedTree); 202 | executeContainingIntervalsTest(queryPointList, masterHalfOpenLow, 203 | masterHalfOpenLowTree); 204 | executeContainingIntervalsTest(queryPointList, masterHalfOpenHigh, 205 | masterHalfOpenHighTree); 206 | executeContainingIntervalsTest(queryPointList, masterOpen, masterOpenTree); 207 | } 208 | 209 | @Test 210 | public void testNull() { 211 | DynamicIntervalTree> tree = new DynamicIntervalTree>(); 212 | try { 213 | tree.fetchContainingIntervals((Double) null); 214 | Assert.fail(); 215 | } catch (NullPointerException e) { 216 | 217 | } 218 | try { 219 | tree.fetchOverlappingIntervals((Interval) null); 220 | Assert.fail(); 221 | } catch (NullPointerException e) { 222 | 223 | } 224 | Assert.assertFalse(tree.delete(null)); 225 | try { 226 | tree.insert(null); 227 | Assert.fail(); 228 | } catch (NullPointerException e) { 229 | 230 | } 231 | } 232 | 233 | @Test 234 | public void testOverlappingIntervals() { 235 | executeOverlappingIntervalsTest(masterClosed, masterClosed, 236 | masterClosedTree); 237 | executeOverlappingIntervalsTest(masterHalfOpenLow, masterClosed, 238 | masterClosedTree); 239 | executeOverlappingIntervalsTest(masterHalfOpenHigh, masterClosed, 240 | masterClosedTree); 241 | executeOverlappingIntervalsTest(masterOpen, masterClosed, masterClosedTree); 242 | 243 | executeOverlappingIntervalsTest(masterClosed, masterHalfOpenLow, 244 | masterHalfOpenLowTree); 245 | executeOverlappingIntervalsTest(masterHalfOpenLow, masterHalfOpenLow, 246 | masterHalfOpenLowTree); 247 | executeOverlappingIntervalsTest(masterHalfOpenHigh, masterHalfOpenLow, 248 | masterHalfOpenLowTree); 249 | executeOverlappingIntervalsTest(masterOpen, masterHalfOpenLow, 250 | masterHalfOpenLowTree); 251 | 252 | executeOverlappingIntervalsTest(masterClosed, masterHalfOpenHigh, 253 | masterHalfOpenHighTree); 254 | executeOverlappingIntervalsTest(masterHalfOpenLow, masterHalfOpenHigh, 255 | masterHalfOpenHighTree); 256 | executeOverlappingIntervalsTest(masterHalfOpenHigh, masterHalfOpenHigh, 257 | masterHalfOpenHighTree); 258 | executeOverlappingIntervalsTest(masterOpen, masterHalfOpenHigh, 259 | masterHalfOpenHighTree); 260 | 261 | executeOverlappingIntervalsTest(masterClosed, masterOpen, masterOpenTree); 262 | executeOverlappingIntervalsTest(masterHalfOpenLow, masterOpen, 263 | masterOpenTree); 264 | executeOverlappingIntervalsTest(masterHalfOpenHigh, masterOpen, 265 | masterOpenTree); 266 | executeOverlappingIntervalsTest(masterOpen, masterOpen, masterOpenTree); 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /src/main/java/blogspot/software_and_algorithms/stern_library/optimization/HungarianAlgorithm.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.optimization; 2 | 3 | import java.util.Arrays; 4 | 5 | /* Copyright (c) 2012 Kevin L. Stern 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /** 27 | * An implementation of the Hungarian algorithm for solving the assignment 28 | * problem. An instance of the assignment problem consists of a number of 29 | * workers along with a number of jobs and a cost matrix which gives the cost of 30 | * assigning the i'th worker to the j'th job at position (i, j). The goal is to 31 | * find an assignment of workers to jobs so that no job is assigned more than 32 | * one worker and so that no worker is assigned to more than one job in such a 33 | * manner so as to minimize the total cost of completing the jobs. 34 | *

35 | * 36 | * An assignment for a cost matrix that has more workers than jobs will 37 | * necessarily include unassigned workers, indicated by an assignment value of 38 | * -1; in no other circumstance will there be unassigned workers. Similarly, an 39 | * assignment for a cost matrix that has more jobs than workers will necessarily 40 | * include unassigned jobs; in no other circumstance will there be unassigned 41 | * jobs. For completeness, an assignment for a square cost matrix will give 42 | * exactly one unique worker to each job. 43 | *

44 | * 45 | * This version of the Hungarian algorithm runs in time O(n^3), where n is the 46 | * maximum among the number of workers and the number of jobs. 47 | * 48 | * @author Kevin L. Stern 49 | */ 50 | public class HungarianAlgorithm { 51 | private final double[][] costMatrix; 52 | private final int rows, cols, dim; 53 | private final double[] labelByWorker, labelByJob; 54 | private final int[] minSlackWorkerByJob; 55 | private final double[] minSlackValueByJob; 56 | private final int[] matchJobByWorker, matchWorkerByJob; 57 | private final int[] parentWorkerByCommittedJob; 58 | private final boolean[] committedWorkers; 59 | 60 | /** 61 | * Construct an instance of the algorithm. 62 | * 63 | * @param costMatrix 64 | * the cost matrix, where matrix[i][j] holds the cost of assigning 65 | * worker i to job j, for all i, j. The cost matrix must not be 66 | * irregular in the sense that all rows must be the same length; in 67 | * addition, all entries must be non-infinite numbers. 68 | */ 69 | public HungarianAlgorithm(double[][] costMatrix) { 70 | this.dim = Math.max(costMatrix.length, costMatrix[0].length); 71 | this.rows = costMatrix.length; 72 | this.cols = costMatrix[0].length; 73 | this.costMatrix = new double[this.dim][this.dim]; 74 | for (int w = 0; w < this.dim; w++) { 75 | if (w < costMatrix.length) { 76 | if (costMatrix[w].length != this.cols) { 77 | throw new IllegalArgumentException("Irregular cost matrix"); 78 | } 79 | for (int j = 0; j < this.cols; j++) { 80 | if (Double.isInfinite(costMatrix[w][j])) { 81 | throw new IllegalArgumentException("Infinite cost"); 82 | } 83 | if (Double.isNaN(costMatrix[w][j])) { 84 | throw new IllegalArgumentException("NaN cost"); 85 | } 86 | } 87 | this.costMatrix[w] = Arrays.copyOf(costMatrix[w], this.dim); 88 | } else { 89 | this.costMatrix[w] = new double[this.dim]; 90 | } 91 | } 92 | labelByWorker = new double[this.dim]; 93 | labelByJob = new double[this.dim]; 94 | minSlackWorkerByJob = new int[this.dim]; 95 | minSlackValueByJob = new double[this.dim]; 96 | committedWorkers = new boolean[this.dim]; 97 | parentWorkerByCommittedJob = new int[this.dim]; 98 | matchJobByWorker = new int[this.dim]; 99 | Arrays.fill(matchJobByWorker, -1); 100 | matchWorkerByJob = new int[this.dim]; 101 | Arrays.fill(matchWorkerByJob, -1); 102 | } 103 | 104 | /** 105 | * Compute an initial feasible solution by assigning zero labels to the 106 | * workers and by assigning to each job a label equal to the minimum cost 107 | * among its incident edges. 108 | */ 109 | protected void computeInitialFeasibleSolution() { 110 | for (int j = 0; j < dim; j++) { 111 | labelByJob[j] = Double.POSITIVE_INFINITY; 112 | } 113 | for (int w = 0; w < dim; w++) { 114 | for (int j = 0; j < dim; j++) { 115 | if (costMatrix[w][j] < labelByJob[j]) { 116 | labelByJob[j] = costMatrix[w][j]; 117 | } 118 | } 119 | } 120 | } 121 | 122 | /** 123 | * Execute the algorithm. 124 | * 125 | * @return the minimum cost matching of workers to jobs based upon the 126 | * provided cost matrix. A matching value of -1 indicates that the 127 | * corresponding worker is unassigned. 128 | */ 129 | public int[] execute() { 130 | /* 131 | * Heuristics to improve performance: Reduce rows and columns by their 132 | * smallest element, compute an initial non-zero dual feasible solution and 133 | * create a greedy matching from workers to jobs of the cost matrix. 134 | */ 135 | reduce(); 136 | computeInitialFeasibleSolution(); 137 | greedyMatch(); 138 | 139 | int w = fetchUnmatchedWorker(); 140 | while (w < dim) { 141 | initializePhase(w); 142 | executePhase(); 143 | w = fetchUnmatchedWorker(); 144 | } 145 | int[] result = Arrays.copyOf(matchJobByWorker, rows); 146 | for (w = 0; w < result.length; w++) { 147 | if (result[w] >= cols) { 148 | result[w] = -1; 149 | } 150 | } 151 | return result; 152 | } 153 | 154 | /** 155 | * Execute a single phase of the algorithm. A phase of the Hungarian algorithm 156 | * consists of building a set of committed workers and a set of committed jobs 157 | * from a root unmatched worker by following alternating unmatched/matched 158 | * zero-slack edges. If an unmatched job is encountered, then an augmenting 159 | * path has been found and the matching is grown. If the connected zero-slack 160 | * edges have been exhausted, the labels of committed workers are increased by 161 | * the minimum slack among committed workers and non-committed jobs to create 162 | * more zero-slack edges (the labels of committed jobs are simultaneously 163 | * decreased by the same amount in order to maintain a feasible labeling). 164 | *

165 | * 166 | * The runtime of a single phase of the algorithm is O(n^2), where n is the 167 | * dimension of the internal square cost matrix, since each edge is visited at 168 | * most once and since increasing the labeling is accomplished in time O(n) by 169 | * maintaining the minimum slack values among non-committed jobs. When a phase 170 | * completes, the matching will have increased in size. 171 | */ 172 | protected void executePhase() { 173 | while (true) { 174 | int minSlackWorker = -1, minSlackJob = -1; 175 | double minSlackValue = Double.POSITIVE_INFINITY; 176 | for (int j = 0; j < dim; j++) { 177 | if (parentWorkerByCommittedJob[j] == -1) { 178 | if (minSlackValueByJob[j] < minSlackValue) { 179 | minSlackValue = minSlackValueByJob[j]; 180 | minSlackWorker = minSlackWorkerByJob[j]; 181 | minSlackJob = j; 182 | } 183 | } 184 | } 185 | if (minSlackValue > 0) { 186 | updateLabeling(minSlackValue); 187 | } 188 | parentWorkerByCommittedJob[minSlackJob] = minSlackWorker; 189 | if (matchWorkerByJob[minSlackJob] == -1) { 190 | /* 191 | * An augmenting path has been found. 192 | */ 193 | int committedJob = minSlackJob; 194 | int parentWorker = parentWorkerByCommittedJob[committedJob]; 195 | while (true) { 196 | int temp = matchJobByWorker[parentWorker]; 197 | match(parentWorker, committedJob); 198 | committedJob = temp; 199 | if (committedJob == -1) { 200 | break; 201 | } 202 | parentWorker = parentWorkerByCommittedJob[committedJob]; 203 | } 204 | return; 205 | } else { 206 | /* 207 | * Update slack values since we increased the size of the committed 208 | * workers set. 209 | */ 210 | int worker = matchWorkerByJob[minSlackJob]; 211 | committedWorkers[worker] = true; 212 | for (int j = 0; j < dim; j++) { 213 | if (parentWorkerByCommittedJob[j] == -1) { 214 | double slack = costMatrix[worker][j] - labelByWorker[worker] 215 | - labelByJob[j]; 216 | if (minSlackValueByJob[j] > slack) { 217 | minSlackValueByJob[j] = slack; 218 | minSlackWorkerByJob[j] = worker; 219 | } 220 | } 221 | } 222 | } 223 | } 224 | } 225 | 226 | /** 227 | * 228 | * @return the first unmatched worker or {@link #dim} if none. 229 | */ 230 | protected int fetchUnmatchedWorker() { 231 | int w; 232 | for (w = 0; w < dim; w++) { 233 | if (matchJobByWorker[w] == -1) { 234 | break; 235 | } 236 | } 237 | return w; 238 | } 239 | 240 | /** 241 | * Find a valid matching by greedily selecting among zero-cost matchings. This 242 | * is a heuristic to jump-start the augmentation algorithm. 243 | */ 244 | protected void greedyMatch() { 245 | for (int w = 0; w < dim; w++) { 246 | for (int j = 0; j < dim; j++) { 247 | if (matchJobByWorker[w] == -1 && matchWorkerByJob[j] == -1 248 | && costMatrix[w][j] - labelByWorker[w] - labelByJob[j] == 0) { 249 | match(w, j); 250 | } 251 | } 252 | } 253 | } 254 | 255 | /** 256 | * Initialize the next phase of the algorithm by clearing the committed 257 | * workers and jobs sets and by initializing the slack arrays to the values 258 | * corresponding to the specified root worker. 259 | * 260 | * @param w 261 | * the worker at which to root the next phase. 262 | */ 263 | protected void initializePhase(int w) { 264 | Arrays.fill(committedWorkers, false); 265 | Arrays.fill(parentWorkerByCommittedJob, -1); 266 | committedWorkers[w] = true; 267 | for (int j = 0; j < dim; j++) { 268 | minSlackValueByJob[j] = costMatrix[w][j] - labelByWorker[w] 269 | - labelByJob[j]; 270 | minSlackWorkerByJob[j] = w; 271 | } 272 | } 273 | 274 | /** 275 | * Helper method to record a matching between worker w and job j. 276 | */ 277 | protected void match(int w, int j) { 278 | matchJobByWorker[w] = j; 279 | matchWorkerByJob[j] = w; 280 | } 281 | 282 | /** 283 | * Reduce the cost matrix by subtracting the smallest element of each row from 284 | * all elements of the row as well as the smallest element of each column from 285 | * all elements of the column. Note that an optimal assignment for a reduced 286 | * cost matrix is optimal for the original cost matrix. 287 | */ 288 | protected void reduce() { 289 | for (int w = 0; w < dim; w++) { 290 | double min = Double.POSITIVE_INFINITY; 291 | for (int j = 0; j < dim; j++) { 292 | if (costMatrix[w][j] < min) { 293 | min = costMatrix[w][j]; 294 | } 295 | } 296 | for (int j = 0; j < dim; j++) { 297 | costMatrix[w][j] -= min; 298 | } 299 | } 300 | double[] min = new double[dim]; 301 | for (int j = 0; j < dim; j++) { 302 | min[j] = Double.POSITIVE_INFINITY; 303 | } 304 | for (int w = 0; w < dim; w++) { 305 | for (int j = 0; j < dim; j++) { 306 | if (costMatrix[w][j] < min[j]) { 307 | min[j] = costMatrix[w][j]; 308 | } 309 | } 310 | } 311 | for (int w = 0; w < dim; w++) { 312 | for (int j = 0; j < dim; j++) { 313 | costMatrix[w][j] -= min[j]; 314 | } 315 | } 316 | } 317 | 318 | /** 319 | * Update labels with the specified slack by adding the slack value for 320 | * committed workers and by subtracting the slack value for committed jobs. In 321 | * addition, update the minimum slack values appropriately. 322 | */ 323 | protected void updateLabeling(double slack) { 324 | for (int w = 0; w < dim; w++) { 325 | if (committedWorkers[w]) { 326 | labelByWorker[w] += slack; 327 | } 328 | } 329 | for (int j = 0; j < dim; j++) { 330 | if (parentWorkerByCommittedJob[j] != -1) { 331 | labelByJob[j] -= slack; 332 | } else { 333 | minSlackValueByJob[j] -= slack; 334 | } 335 | } 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /src/test/java/blogspot/software_and_algorithms/stern_library/data_structure/StaticIntervalTreeTest.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.data_structure; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | import org.junit.AfterClass; 10 | import org.junit.Assert; 11 | import org.junit.BeforeClass; 12 | import org.junit.Test; 13 | 14 | /* Copyright (c) 2012 Kevin L. Stern 15 | * 16 | * Permission is hereby granted, free of charge, to any person obtaining a copy 17 | * of this software and associated documentation files (the "Software"), to deal 18 | * in the Software without restriction, including without limitation the rights 19 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | * copies of the Software, and to permit persons to whom the Software is 21 | * furnished to do so, subject to the following conditions: 22 | * 23 | * The above copyright notice and this permission notice shall be included in 24 | * all copies or substantial portions of the Software. 25 | * 26 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 | * SOFTWARE. 33 | */ 34 | 35 | /** 36 | * Test class for StaticIntervalTree. 37 | * 38 | * @author Kevin L. Stern 39 | */ 40 | public class StaticIntervalTreeTest { 41 | private static List> masterClosed; 42 | private static List> masterHalfOpenLow; 43 | private static List> masterHalfOpenHigh; 44 | private static List> masterOpen; 45 | private static StaticIntervalTree> masterClosedTree; 46 | private static StaticIntervalTree> masterHalfOpenLowTree; 47 | private static StaticIntervalTree> masterHalfOpenHighTree; 48 | private static StaticIntervalTree> masterOpenTree; 49 | 50 | private static void executeContainingIntervalsTest(List queryPoints, 51 | List> master, 52 | StaticIntervalTree> tree) { 53 | Set> result = new HashSet>(); 54 | for (Double queryPoint : queryPoints) { 55 | Assert.assertEquals(fetchContainingIntervals(master, queryPoint), 56 | tree.fetchContainingIntervals(result, queryPoint)); 57 | result.clear(); 58 | } 59 | } 60 | 61 | private static void executeOverlappingIntervalsTest(List> queryIntervals, 62 | List> master, 63 | StaticIntervalTree> tree) { 64 | Set> result = new HashSet>(); 65 | for (Interval next : queryIntervals) { 66 | Assert.assertEquals(fetchOverlappingIntervals(master, next), 67 | tree.fetchOverlappingIntervals(result, next)); 68 | result.clear(); 69 | } 70 | } 71 | 72 | /** 73 | * Helper method to fetch the set of intervals from the master list that 74 | * contain the query point using the brute force method. This gives a correct 75 | * answer against which to compare the result from the interval tree. 76 | */ 77 | private static Set> fetchContainingIntervals(List> master, 78 | double queryPoint) { 79 | Set> result = new HashSet>(); 80 | for (Interval next : master) { 81 | if (next.contains(queryPoint)) { 82 | result.add(next); 83 | } 84 | } 85 | return result; 86 | } 87 | 88 | /** 89 | * Helper method to fetch the set of intervals from the master list that 90 | * overlap the query interval using the brute force method. This gives a 91 | * correct answer against which to compare the result from the interval tree. 92 | */ 93 | private static Set> fetchOverlappingIntervals(List> master, 94 | Interval queryInterval) { 95 | Set> result = new HashSet>(); 96 | for (Interval next : master) { 97 | if (next.overlaps(queryInterval)) { 98 | result.add(next); 99 | } 100 | } 101 | return result; 102 | } 103 | 104 | @BeforeClass 105 | public static void globalSetup() { 106 | masterClosed = new ArrayList>(); 107 | masterHalfOpenLow = new ArrayList>(); 108 | masterHalfOpenHigh = new ArrayList>(); 109 | masterOpen = new ArrayList>(); 110 | 111 | final double intervalWidth = 0.1; 112 | final double halfIntervalWidth = intervalWidth / 2; 113 | 114 | for (int j = 0; j < 10; j++) { 115 | Interval next; 116 | next = new Interval(intervalWidth * j, true, intervalWidth 117 | * (j + 1), true); 118 | masterClosed.add(next); 119 | masterClosed.add(new Interval(next.getLow() + halfIntervalWidth, 120 | true, next.getHigh() + halfIntervalWidth, true)); 121 | 122 | next = new Interval(intervalWidth * j, false, intervalWidth 123 | * (j + 1), true); 124 | masterHalfOpenLow.add(next); 125 | masterHalfOpenLow 126 | .add(new Interval(next.getLow() + halfIntervalWidth, false, 127 | next.getHigh() + halfIntervalWidth, true)); 128 | 129 | next = new Interval(intervalWidth * j, true, intervalWidth 130 | * (j + 1), false); 131 | masterHalfOpenHigh.add(next); 132 | masterHalfOpenHigh 133 | .add(new Interval(next.getLow() + halfIntervalWidth, true, 134 | next.getHigh() + halfIntervalWidth, false)); 135 | 136 | next = new Interval(intervalWidth * j, false, intervalWidth 137 | * (j + 1), false); 138 | masterOpen.add(next); 139 | masterOpen.add(new Interval(next.getLow() + halfIntervalWidth, 140 | false, next.getHigh() + halfIntervalWidth, false)); 141 | } 142 | masterClosedTree = makeTree(masterClosed); 143 | masterHalfOpenLowTree = makeTree(masterHalfOpenLow); 144 | masterHalfOpenHighTree = makeTree(masterHalfOpenHigh); 145 | masterOpenTree = makeTree(masterOpen); 146 | } 147 | 148 | @AfterClass 149 | public static void globalTeardown() { 150 | masterClosed = null; 151 | masterHalfOpenLow = null; 152 | masterHalfOpenHigh = null; 153 | masterOpen = null; 154 | masterClosedTree = null; 155 | masterHalfOpenLowTree = null; 156 | masterHalfOpenHighTree = null; 157 | masterOpenTree = null; 158 | } 159 | 160 | /** 161 | * Helper method to make and populate an IntervalTree based upon the specified 162 | * list of intervals. 163 | */ 164 | private static StaticIntervalTree> makeTree(List> list) { 165 | StaticIntervalTree> tree = new StaticIntervalTree>(); 166 | tree.buildTree(new HashSet>(list)); 167 | for (Interval next : list) 168 | tree.insert(next); 169 | return tree; 170 | } 171 | 172 | @Test 173 | public void testClear() { 174 | StaticIntervalTree> tree = makeTree(masterClosed); 175 | Assert.assertEquals(masterClosed.size(), tree.getSize()); 176 | tree.clear(); 177 | Assert.assertTrue(tree 178 | .fetchContainingIntervals(new ArrayList>(), 179 | masterClosed.get(0).getLow()).isEmpty()); 180 | Assert.assertEquals(0, tree.getSize()); 181 | /* 182 | * Test that the tree structure remains intact. 183 | */ 184 | for (Interval next : masterClosed) { 185 | tree.insert(next); 186 | } 187 | Assert.assertFalse(tree 188 | .fetchContainingIntervals(new ArrayList>(), 189 | masterClosed.get(0).getLow()).isEmpty()); 190 | } 191 | 192 | @Test 193 | public void testConstructionPointerEdgeCases() { 194 | List> master = new ArrayList>(); 195 | master.add(new Interval(0.0, true, 0.1, true)); 196 | master.add(new Interval(0.2, true, 0.3, true)); 197 | master.add(new Interval(0.4, true, 0.5, true)); 198 | master.add(new Interval(0.6, true, 0.7, true)); 199 | StaticIntervalTree> tree = makeTree(master); 200 | List> result = tree 201 | .fetchContainingIntervals(new ArrayList>(), 0.25); 202 | Assert.assertEquals(1, result.size()); 203 | Assert.assertEquals(master.get(1), result.get(0)); 204 | } 205 | 206 | @Test 207 | public void testContainingIntervals() { 208 | List queryPointList = new ArrayList(); 209 | for (Interval next : masterClosed) { 210 | queryPointList.add(next.getLow()); 211 | } 212 | executeContainingIntervalsTest(queryPointList, masterClosed, 213 | masterClosedTree); 214 | executeContainingIntervalsTest(queryPointList, masterHalfOpenLow, 215 | masterHalfOpenLowTree); 216 | executeContainingIntervalsTest(queryPointList, masterHalfOpenHigh, 217 | masterHalfOpenHighTree); 218 | executeContainingIntervalsTest(queryPointList, masterOpen, masterOpenTree); 219 | } 220 | 221 | @Test 222 | public void testNull() { 223 | StaticIntervalTree> tree = new StaticIntervalTree>(); 224 | try { 225 | tree.buildTree(null); 226 | Assert.fail(); 227 | } catch (NullPointerException e) { 228 | 229 | } 230 | try { 231 | tree.fetchContainingIntervals(new ArrayList>(), 232 | (Double) null); 233 | Assert.fail(); 234 | } catch (NullPointerException e) { 235 | 236 | } 237 | try { 238 | tree.fetchContainingIntervals((Collection>) null, 0.0); 239 | Assert.fail(); 240 | } catch (NullPointerException e) { 241 | 242 | } 243 | try { 244 | tree.fetchOverlappingIntervals(new ArrayList>(), 245 | (Interval) null); 246 | Assert.fail(); 247 | } catch (NullPointerException e) { 248 | 249 | } 250 | try { 251 | tree.fetchOverlappingIntervals((Collection>) null, 252 | new Interval(0.0, true, 1.0, true)); 253 | Assert.fail(); 254 | } catch (NullPointerException e) { 255 | 256 | } 257 | Assert.assertFalse(tree.delete(null)); 258 | Set> list = new HashSet>(masterClosed); 259 | list.add(null); 260 | try { 261 | tree.buildTree(list); 262 | Assert.fail(); 263 | } catch (NullPointerException e) { 264 | 265 | } 266 | tree.buildTree(new HashSet>(masterClosed)); 267 | try { 268 | tree.insert(null); 269 | Assert.fail(); 270 | } catch (NullPointerException e) { 271 | 272 | } 273 | } 274 | 275 | @Test 276 | public void testOverlappingIntervals() { 277 | executeOverlappingIntervalsTest(masterClosed, masterClosed, 278 | masterClosedTree); 279 | executeOverlappingIntervalsTest(masterHalfOpenLow, masterClosed, 280 | masterClosedTree); 281 | executeOverlappingIntervalsTest(masterHalfOpenHigh, masterClosed, 282 | masterClosedTree); 283 | executeOverlappingIntervalsTest(masterOpen, masterClosed, masterClosedTree); 284 | 285 | executeOverlappingIntervalsTest(masterClosed, masterHalfOpenLow, 286 | masterHalfOpenLowTree); 287 | executeOverlappingIntervalsTest(masterHalfOpenLow, masterHalfOpenLow, 288 | masterHalfOpenLowTree); 289 | executeOverlappingIntervalsTest(masterHalfOpenHigh, masterHalfOpenLow, 290 | masterHalfOpenLowTree); 291 | executeOverlappingIntervalsTest(masterOpen, masterHalfOpenLow, 292 | masterHalfOpenLowTree); 293 | 294 | executeOverlappingIntervalsTest(masterClosed, masterHalfOpenHigh, 295 | masterHalfOpenHighTree); 296 | executeOverlappingIntervalsTest(masterHalfOpenLow, masterHalfOpenHigh, 297 | masterHalfOpenHighTree); 298 | executeOverlappingIntervalsTest(masterHalfOpenHigh, masterHalfOpenHigh, 299 | masterHalfOpenHighTree); 300 | executeOverlappingIntervalsTest(masterOpen, masterHalfOpenHigh, 301 | masterHalfOpenHighTree); 302 | 303 | executeOverlappingIntervalsTest(masterClosed, masterOpen, masterOpenTree); 304 | executeOverlappingIntervalsTest(masterHalfOpenLow, masterOpen, 305 | masterOpenTree); 306 | executeOverlappingIntervalsTest(masterHalfOpenHigh, masterOpen, 307 | masterOpenTree); 308 | executeOverlappingIntervalsTest(masterOpen, masterOpen, masterOpenTree); 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /src/main/java/blogspot/software_and_algorithms/stern_library/data_structure/DynamicIntervalTree.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.data_structure; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.Comparator; 6 | import java.util.List; 7 | 8 | import blogspot.software_and_algorithms.stern_library.data_structure.RedBlackTree.Node.NodeColor; 9 | 10 | /* Copyright (c) 2012 Kevin L. Stern 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in 20 | * all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | */ 30 | 31 | /** 32 | * A dynamic interval tree is a balanced binary search tree which 33 | * stores intervals so that both point queries (queries that return intervals 34 | * from the set which contain a query point) and overlapping interval queries 35 | * (queries that return intervals from the set which overlap a query interval) 36 | * can be completed in time O(k*log(n)), where n is the number of intervals 37 | * stored in the tree and k is the size of the result set from the query. 38 | *

39 | * Insertion and deletion of intervals to and from this tree completes in time 40 | * O(log(n)) where n is the number of intervals stored in the tree. 41 | *

42 | * This tree consumes linear space in the number of intervals stored in the 43 | * tree. 44 | *

45 | * Note that this implementation supports all three closed, open and half-open 46 | * intervals. 47 | * 48 | * @author Kevin L. Stern 49 | */ 50 | public class DynamicIntervalTree, T extends Interval> { 51 | public RedBlackTree binarySearchTree = new RedBlackTree( 52 | new Comparator() { 53 | @Override 54 | public int compare(T o1, T o2) { 55 | int result = o1.getLow().compareTo(o2.getLow()); 56 | if (result == 0) { 57 | if (o1.isClosedOnLow() != o2.isClosedOnLow()) { 58 | result = o1.isClosedOnLow() ? -1 : 1; 59 | } else { 60 | result = o1.compareTo(o2); 61 | } 62 | } 63 | return result; 64 | } 65 | }) { 66 | @Override 67 | protected RedBlackTree.Node createNewNode(T value) { 68 | return new DynamicIntervalTree.Node(value); 69 | } 70 | 71 | @Override 72 | public RedBlackTree.Node delete(T value) { 73 | RedBlackTree.Node node = super.delete(value); 74 | if (node != null && node.getColor() != NodeColor.BLACK) { 75 | DynamicIntervalTree.Node temp = (DynamicIntervalTree.Node) node 76 | .getParent(); 77 | while (temp != null) { 78 | temp.computeSubtreeSpan(); 79 | temp = temp.getParent(); 80 | } 81 | } 82 | return node; 83 | } 84 | 85 | @Override 86 | protected void fixAfterDeletion(RedBlackTree.Node node) { 87 | DynamicIntervalTree.Node temp = (DynamicIntervalTree.Node) node 88 | .getParent(); 89 | while (temp != null) { 90 | temp.computeSubtreeSpan(); 91 | temp = temp.getParent(); 92 | } 93 | super.fixAfterDeletion(node); 94 | } 95 | 96 | @Override 97 | protected void fixAfterInsertion(RedBlackTree.Node node) { 98 | DynamicIntervalTree.Node temp = (DynamicIntervalTree.Node) node 99 | .getParent(); 100 | while (temp != null) { 101 | temp.computeSubtreeSpan(); 102 | temp = temp.getParent(); 103 | } 104 | super.fixAfterInsertion(node); 105 | } 106 | 107 | @Override 108 | protected void leftRotate(RedBlackTree.Node node) { 109 | super.leftRotate(node); 110 | DynamicIntervalTree.Node temp = (DynamicIntervalTree.Node) node; 111 | temp.computeSubtreeSpan(); 112 | temp.getParent().computeSubtreeSpan(); 113 | } 114 | 115 | @Override 116 | protected void rightRotate(RedBlackTree.Node node) { 117 | super.rightRotate(node); 118 | DynamicIntervalTree.Node temp = (DynamicIntervalTree.Node) node; 119 | temp.computeSubtreeSpan(); 120 | temp.getParent().computeSubtreeSpan(); 121 | } 122 | }; 123 | 124 | /** 125 | * Clear the contents of the tree. 126 | */ 127 | public void clear() { 128 | binarySearchTree.clear(); 129 | } 130 | 131 | /** 132 | * Delete the specified interval from this tree. 133 | * 134 | * @param interval 135 | * the interval to delete. 136 | * @return true if an element was deleted as a result of this call, false 137 | * otherwise. 138 | */ 139 | public boolean delete(T interval) { 140 | return binarySearchTree.delete(interval) != null; 141 | } 142 | 143 | /** 144 | * Fetch an interval containing the specified point. 145 | * 146 | * @param queryPoint 147 | * the query point. 148 | * @return an interval containing the specified point, null if none. 149 | */ 150 | protected T fetchContainingInterval(U queryPoint) { 151 | Node node = (Node) binarySearchTree.getRoot(); 152 | while (node != null) { 153 | if (node.getValue().contains(queryPoint)) { 154 | return node.getValue(); 155 | } 156 | Node leftChild = node.getLeft(); 157 | node = node.getRight(); 158 | if (leftChild != null) { 159 | int cmp = leftChild.getSubtreeSpanHigh().compareTo(queryPoint); 160 | if (cmp > 0 || cmp == 0 && leftChild.isClosedOnSubtreeSpanHigh()) { 161 | node = leftChild; 162 | } 163 | } 164 | } 165 | return null; 166 | } 167 | 168 | /** 169 | * Fetch intervals containing the specified point. 170 | * 171 | * @param queryPoint 172 | * the query point. 173 | * @return a Collection of all intervals containing the specified point. 174 | */ 175 | public Collection fetchContainingIntervals(U queryPoint) { 176 | if (queryPoint == null) { 177 | throw new NullPointerException("queryPoint is null"); 178 | } 179 | List result = new ArrayList(); 180 | Node node = (Node) binarySearchTree.getRoot(); 181 | List> queue = new ArrayList>(); 182 | if (node != null) { 183 | queue.add(node); 184 | } 185 | while (!queue.isEmpty()) { 186 | node = queue.remove(queue.size() - 1); 187 | if (node.getValue().contains(queryPoint)) { 188 | result.add(node.getValue()); 189 | } 190 | Node child = node.getLeft(); 191 | if (child != null) { 192 | int cmp = child.getSubtreeSpanHigh().compareTo(queryPoint); 193 | if (cmp > 0 || cmp == 0 && child.isClosedOnSubtreeSpanHigh()) { 194 | queue.add(child); 195 | } 196 | } 197 | child = node.getRight(); 198 | if (child != null) { 199 | int cmp = child.getSubtreeSpanLow().compareTo(queryPoint); 200 | if (cmp < 0 || cmp == 0 && child.isClosedOnSubtreeSpanLow()) { 201 | queue.add(child); 202 | } 203 | } 204 | } 205 | return result; 206 | } 207 | 208 | /** 209 | * Fetch an interval overlapping the specified interval. 210 | * 211 | * @param queryInterval 212 | * the query interval. 213 | * @return an interval overlapping the specified interval, null if none. 214 | */ 215 | protected T fetchOverlappingInterval(T queryInterval) { 216 | Node node = (Node) binarySearchTree.getRoot(); 217 | while (node != null) { 218 | if (node.getValue().overlaps(queryInterval)) { 219 | return node.getValue(); 220 | } 221 | Node leftChild = node.getLeft(); 222 | node = node.getRight(); 223 | if (leftChild != null) { 224 | int cmp = leftChild.getSubtreeSpanHigh().compareTo(queryInterval 225 | .getLow()); 226 | if (cmp > 0 || cmp == 0 && leftChild.isClosedOnSubtreeSpanHigh() 227 | && queryInterval.isClosedOnLow()) { 228 | node = leftChild; 229 | } 230 | } 231 | } 232 | return null; 233 | } 234 | 235 | /** 236 | * Fetch intervals overlapping the specified interval. 237 | * 238 | * @param queryInterval 239 | * the query interval. 240 | * @return a Collection of all intervals overlapping the specified point. 241 | */ 242 | public Collection fetchOverlappingIntervals(T queryInterval) { 243 | if (queryInterval == null) { 244 | throw new NullPointerException("queryInterval is null"); 245 | } 246 | List result = new ArrayList(); 247 | Node node = (Node) binarySearchTree.getRoot(); 248 | List> queue = new ArrayList>(); 249 | if (node != null) { 250 | queue.add(node); 251 | } 252 | while (!queue.isEmpty()) { 253 | node = queue.remove(queue.size() - 1); 254 | if (node.getValue().overlaps(queryInterval)) { 255 | result.add(node.getValue()); 256 | } 257 | Node child = node.getLeft(); 258 | if (child != null) { 259 | int cmp = child.getSubtreeSpanHigh().compareTo(queryInterval.getLow()); 260 | if (cmp > 0 || cmp == 0 && child.isClosedOnSubtreeSpanHigh() 261 | && queryInterval.isClosedOnLow()) { 262 | queue.add(child); 263 | } 264 | } 265 | child = node.getRight(); 266 | if (child != null) { 267 | int cmp = child.getSubtreeSpanLow().compareTo(queryInterval.getHigh()); 268 | if (cmp < 0 || cmp == 0 && child.isClosedOnSubtreeSpanLow() 269 | && queryInterval.isClosedOnHigh()) { 270 | queue.add(child); 271 | } 272 | } 273 | } 274 | return result; 275 | } 276 | 277 | /** 278 | * Get the number of intervals being stored in the tree. 279 | * 280 | * @return the number of intervals being stored in the tree. 281 | */ 282 | public int getSize() { 283 | return binarySearchTree.getSize(); 284 | } 285 | 286 | /** 287 | * Insert the specified interval into this tree. 288 | * 289 | * @param interval 290 | * the interval to insert. 291 | * @return true if an element was inserted as a result of this call, false 292 | * otherwise. 293 | */ 294 | public boolean insert(T interval) { 295 | return binarySearchTree.insert(interval) != null; 296 | } 297 | 298 | /** 299 | * A node for a dynamic interval tree is a red-black tree node 300 | * augmented to store the maximum high and minimum low endpoints among 301 | * intervals stored within the subtree rooted at the node. 302 | */ 303 | protected static class Node, T extends Interval> 304 | extends RedBlackTree.Node { 305 | private U subtreeSpanLow, subtreeSpanHigh; 306 | private boolean isClosedOnSubtreeSpanLow, isClosedOnSubtreeSpanHigh; 307 | 308 | /** 309 | * Construct a new node associated with the specified interval. 310 | * 311 | * @param interval 312 | * the interval with which this node is associated. 313 | */ 314 | public Node(T interval) { 315 | super(interval); 316 | subtreeSpanLow = interval.getLow(); 317 | subtreeSpanHigh = interval.getHigh(); 318 | isClosedOnSubtreeSpanLow = interval.isClosedOnLow(); 319 | isClosedOnSubtreeSpanHigh = interval.isClosedOnHigh(); 320 | } 321 | 322 | /** 323 | * Compute the maximum high and minimum low endpoints among intervals stored 324 | * within the subtree rooted at this node and correct values up the tree. 325 | */ 326 | protected void computeSubtreeSpan() { 327 | U subtreeSpanLow = getValue().getLow(); 328 | U subtreeSpanHigh = getValue().getHigh(); 329 | boolean isClosedOnSubtreeSpanLow = getValue().isClosedOnLow(); 330 | boolean isClosedOnSubtreeSpanHigh = getValue().isClosedOnHigh(); 331 | Node child; 332 | child = getLeft(); 333 | if (child != null) { 334 | int cmp = child.subtreeSpanLow.compareTo(subtreeSpanLow); 335 | if (cmp < 0 || cmp == 0 && child.isClosedOnSubtreeSpanLow) { 336 | subtreeSpanLow = child.subtreeSpanLow; 337 | isClosedOnSubtreeSpanLow = child.isClosedOnSubtreeSpanLow; 338 | } 339 | cmp = child.subtreeSpanHigh.compareTo(subtreeSpanHigh); 340 | if (cmp > 0 || cmp == 0 && child.isClosedOnSubtreeSpanHigh) { 341 | subtreeSpanHigh = child.subtreeSpanHigh; 342 | isClosedOnSubtreeSpanHigh = child.isClosedOnSubtreeSpanHigh; 343 | } 344 | } 345 | child = getRight(); 346 | if (child != null) { 347 | int cmp = child.subtreeSpanLow.compareTo(subtreeSpanLow); 348 | if (cmp < 0 || cmp == 0 && child.isClosedOnSubtreeSpanLow) { 349 | subtreeSpanLow = child.subtreeSpanLow; 350 | isClosedOnSubtreeSpanLow = child.isClosedOnSubtreeSpanLow; 351 | } 352 | cmp = child.subtreeSpanHigh.compareTo(subtreeSpanHigh); 353 | if (cmp > 0 || cmp == 0 && child.isClosedOnSubtreeSpanHigh) { 354 | subtreeSpanHigh = child.subtreeSpanHigh; 355 | isClosedOnSubtreeSpanHigh = child.isClosedOnSubtreeSpanHigh; 356 | } 357 | } 358 | this.subtreeSpanLow = subtreeSpanLow; 359 | this.isClosedOnSubtreeSpanLow = isClosedOnSubtreeSpanLow; 360 | this.subtreeSpanHigh = subtreeSpanHigh; 361 | this.isClosedOnSubtreeSpanHigh = isClosedOnSubtreeSpanHigh; 362 | } 363 | 364 | /** 365 | * {@inheritDoc} 366 | */ 367 | @Override 368 | public Node getLeft() { 369 | return (Node) super.getLeft(); 370 | } 371 | 372 | /** 373 | * {@inheritDoc} 374 | */ 375 | @Override 376 | public Node getParent() { 377 | return (Node) super.getParent(); 378 | } 379 | 380 | /** 381 | * {@inheritDoc} 382 | */ 383 | @Override 384 | public Node getRight() { 385 | return (Node) super.getRight(); 386 | } 387 | 388 | public U getSubtreeSpanHigh() { 389 | return subtreeSpanHigh; 390 | } 391 | 392 | public U getSubtreeSpanLow() { 393 | return subtreeSpanLow; 394 | } 395 | 396 | public boolean isClosedOnSubtreeSpanHigh() { 397 | return isClosedOnSubtreeSpanHigh; 398 | } 399 | 400 | public boolean isClosedOnSubtreeSpanLow() { 401 | return isClosedOnSubtreeSpanLow; 402 | } 403 | } 404 | } 405 | -------------------------------------------------------------------------------- /src/test/java/blogspot/software_and_algorithms/stern_library/data_structure/ThriftyListTest.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.data_structure; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.ObjectInputStream; 6 | import java.io.ObjectOutputStream; 7 | import java.io.PrintWriter; 8 | import java.io.StringWriter; 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.Iterator; 12 | import java.util.ListIterator; 13 | import java.util.NoSuchElementException; 14 | 15 | import junit.framework.Assert; 16 | 17 | import org.junit.Test; 18 | 19 | /* Copyright (C) 2012 Kevin L. Stern. 20 | * 21 | * Permission is hereby granted, free of charge, to any person obtaining a copy 22 | * of this software and associated documentation files (the "Software"), to deal 23 | * in the Software without restriction, including without limitation the rights 24 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | * copies of the Software, and to permit persons to whom the Software is 26 | * furnished to do so, subject to the following conditions: 27 | * 28 | * The above copyright notice and this permission notice shall be included in 29 | * all copies or substantial portions of the Software. 30 | * 31 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | * SOFTWARE. 38 | */ 39 | 40 | /** 41 | * Test case for ThriftyList. 42 | * 43 | * @author Kevin L. Stern 44 | */ 45 | public class ThriftyListTest { 46 | @Test 47 | public void testAddAll() { 48 | ThriftyList list = new ThriftyList(); 49 | list.addAll(0, Collections. emptyList()); 50 | list.addAll(Arrays.asList(10, 11, 12, 13, 14)); 51 | list.addAll(0, Arrays.asList(0, 1, 2, 3, 4)); 52 | list.addAll(5, Arrays.asList(5, 6, 7, 8, 9)); 53 | list.addAll(0, Collections. emptyList()); 54 | list.addAll(5, Collections. emptyList()); 55 | list.addAll(Collections. emptyList()); 56 | 57 | Assert.assertEquals(15, list.size()); 58 | for (int j = 0; j < 15; j++) { 59 | Assert.assertEquals(Integer.valueOf(j), list.get(j)); 60 | } 61 | } 62 | 63 | @Test 64 | public void testAddFirstThenRemoveFirst() { 65 | ThriftyList list = new ThriftyList(); 66 | 67 | int testCount = 100; 68 | for (int j = 0; j < testCount; j++) { 69 | list.addFirst(j); 70 | } 71 | 72 | for (int j = testCount - 1; j >= 0; j--) { 73 | Assert.assertEquals(Integer.valueOf(j), list.removeFirst()); 74 | } 75 | 76 | Assert.assertEquals(0, list.size()); 77 | } 78 | 79 | @Test 80 | public void testAddLastThenRemoveLast() { 81 | ThriftyList list = new ThriftyList(); 82 | int testCount = 100; 83 | for (int j = 0; j < testCount; j++) { 84 | list.addLast(j); 85 | } 86 | 87 | for (int j = testCount - 1; j >= 0; j--) { 88 | Assert.assertEquals(Integer.valueOf(j), list.removeLast()); 89 | } 90 | 91 | Assert.assertEquals(0, list.size()); 92 | } 93 | 94 | @Test 95 | public void testClone() { 96 | ThriftyList list = new ThriftyList(); 97 | int testCount = 10; 98 | for (int j = 0; j < testCount; j++) { 99 | list.add(j); 100 | } 101 | ThriftyList cloneList = (ThriftyList) list.clone(); 102 | 103 | Assert.assertEquals(testCount, cloneList.size()); 104 | for (int j = 0; j < testCount; j++) { 105 | Assert.assertEquals(Integer.valueOf(j), cloneList.get(j)); 106 | } 107 | } 108 | 109 | @Test 110 | public void testContains() { 111 | ThriftyList list = new ThriftyList(); 112 | int testCount = 10; 113 | for (int j = 0; j < testCount; j++) { 114 | list.add(j); 115 | } 116 | for (int j = 0; j < testCount; j++) { 117 | Assert.assertTrue(list.contains(j)); 118 | } 119 | Assert.assertFalse(list 120 | .contains("This String is certainly not in the list")); 121 | 122 | Integer[] allValues = new Integer[testCount]; 123 | for (int j = 0; j < testCount; j++) { 124 | allValues[j] = j; 125 | } 126 | Assert.assertTrue(list.containsAll(Arrays.asList(allValues))); 127 | allValues[0] = -1; 128 | Assert.assertFalse(list.containsAll(Arrays.asList(allValues))); 129 | } 130 | 131 | @Test 132 | public void testDescendingIterator() { 133 | ThriftyList list = new ThriftyList(); 134 | Iterator ri = list.descendingIterator(); 135 | Assert.assertFalse(ri.hasNext()); 136 | try { 137 | ri.next(); 138 | Assert.fail(); 139 | } catch (NoSuchElementException e) { 140 | 141 | } 142 | try { 143 | ri.remove(); 144 | Assert.fail(); 145 | } catch (IllegalStateException e) { 146 | 147 | } 148 | 149 | int testCount = 100; 150 | for (int j = 0; j < testCount; j++) { 151 | list.add(j); 152 | } 153 | 154 | ri = list.descendingIterator(); 155 | for (int j = testCount - 1; j >= 0; j--) { 156 | Assert.assertTrue(ri.hasNext()); 157 | Assert.assertEquals(Integer.valueOf(j), ri.next()); 158 | } 159 | 160 | ri = list.descendingIterator(); 161 | try { 162 | ri.remove(); 163 | Assert.fail(); 164 | } catch (IllegalStateException e) { 165 | 166 | } 167 | for (int j = testCount - 1; j >= 0; j--) { 168 | Assert.assertTrue(ri.hasNext()); 169 | Assert.assertEquals(Integer.valueOf(j), ri.next()); 170 | ri.remove(); 171 | } 172 | Assert.assertFalse(ri.hasNext()); 173 | Assert.assertEquals(0, list.size()); 174 | try { 175 | ri.next(); 176 | Assert.fail(); 177 | } catch (NoSuchElementException e) { 178 | 179 | } 180 | } 181 | 182 | @Test 183 | public void testGet() { 184 | ThriftyList list = new ThriftyList(); 185 | int testCount = 10; 186 | for (int j = 0; j < testCount; j++) { 187 | list.add(j); 188 | } 189 | for (int j = 0; j < testCount; j++) { 190 | Assert.assertEquals(Integer.valueOf(j), list.get(j)); 191 | } 192 | Assert.assertEquals(Integer.valueOf(0), list.getFirst()); 193 | Assert.assertEquals(Integer.valueOf(list.get(list.size() - 1)), 194 | list.getLast()); 195 | } 196 | 197 | @Test 198 | public void testIndexOfAndLastIndexOf() { 199 | ThriftyList list = new ThriftyList(); 200 | int testCount = 10; 201 | for (int j = 0; j < testCount; j++) { 202 | list.add(j); 203 | } 204 | for (int j = 0; j < testCount; j++) { 205 | Assert.assertEquals(j, list.indexOf(list.get(j))); 206 | Assert.assertEquals(j, list.lastIndexOf(list.get(j))); 207 | } 208 | 209 | Assert.assertEquals(-1, list 210 | .indexOf("This String is certainly not in the list")); 211 | Assert.assertEquals(-1, list 212 | .lastIndexOf("This String is certainly not in the list")); 213 | 214 | list.add(0); 215 | Assert.assertEquals(0, list.indexOf(0)); 216 | Assert.assertEquals(list.size() - 1, list.lastIndexOf(0)); 217 | } 218 | 219 | @Test 220 | public void testInsertToBack() { 221 | ThriftyList list = new ThriftyList(); 222 | int testCount = 100; 223 | for (int j = 0; j < testCount; j++) { 224 | list.add(j, j); 225 | } 226 | for (int j = 0; j < testCount; j++) { 227 | Assert.assertEquals(Integer.valueOf(j), list.get(j)); 228 | } 229 | } 230 | 231 | @Test 232 | public void testInsertToFront() { 233 | ThriftyList list = new ThriftyList(); 234 | int testCount = 100; 235 | for (int j = testCount; j >= 0; j--) { 236 | list.add(0, j); 237 | } 238 | for (int j = 0; j < testCount; j++) { 239 | Assert.assertEquals(Integer.valueOf(j), list.get(j)); 240 | } 241 | } 242 | 243 | @Test 244 | public void testInsertToMiddle() { 245 | ThriftyList list = new ThriftyList(); 246 | int testCount = 100; 247 | list.add(0); 248 | list.add(testCount - 2); 249 | list.add(testCount - 1); 250 | for (int j = 1; j < testCount - 2; j++) { 251 | list.add(j, j); 252 | } 253 | for (int j = 0; j < testCount; j++) { 254 | Assert.assertEquals(Integer.valueOf(j), list.get(j)); 255 | } 256 | } 257 | 258 | @Test 259 | public void testIterator() { 260 | ThriftyList list = new ThriftyList(); 261 | 262 | ListIterator i = list.listIterator(); 263 | try { 264 | i.next(); 265 | Assert.fail(); 266 | } catch (NoSuchElementException e) { 267 | 268 | } 269 | try { 270 | i.remove(); 271 | Assert.fail(); 272 | } catch (IllegalStateException e) { 273 | 274 | } 275 | 276 | int testCount = 100; 277 | i = list.listIterator(); 278 | for (int j = 0; j < testCount; j++) { 279 | i.add(j); 280 | } 281 | 282 | for (int j = testCount - 1; j >= 0; j--) { 283 | Assert.assertEquals(j, i.previousIndex()); 284 | Assert.assertEquals(Integer.valueOf(j), i.previous()); 285 | } 286 | 287 | for (int j = 0; j < testCount; j++) { 288 | Assert.assertTrue(i.hasNext()); 289 | Integer o = list.get(j); 290 | Assert.assertEquals(j, i.nextIndex()); 291 | Assert.assertEquals(o, i.next()); 292 | i.remove(); 293 | Assert.assertEquals(testCount - 1, list.size()); 294 | i.add(o); 295 | Assert.assertEquals(testCount, list.size()); 296 | Assert.assertEquals(o, i.previous()); 297 | i.next(); 298 | } 299 | 300 | int arbitraryIndex = testCount / 2; 301 | Integer arbitraryValue = -1; 302 | i = list.listIterator(arbitraryIndex); 303 | i.next(); 304 | i.set(arbitraryValue); 305 | for (int j = 0; j < testCount; j++) { 306 | if (j == arbitraryIndex) { 307 | Assert.assertEquals(arbitraryValue, list.get(j)); 308 | continue; 309 | } 310 | Assert.assertEquals(Integer.valueOf(j), list.get(j)); 311 | } 312 | 313 | i = list.listIterator(list.size()); 314 | for (int j = list.size() - 1; i.hasPrevious(); j--) { 315 | Integer o = list.get(j); 316 | Assert.assertEquals(j, i.previousIndex()); 317 | Assert.assertEquals(o, i.previous()); 318 | i.remove(); 319 | Assert.assertEquals(testCount - 1, list.size()); 320 | i.add(o); 321 | Assert.assertEquals(testCount, list.size()); 322 | Assert.assertEquals(o, i.previous()); 323 | } 324 | } 325 | 326 | @Test 327 | public void testPeek() { 328 | ThriftyList list = new ThriftyList(); 329 | Assert.assertNull(list.peek()); 330 | Assert.assertNull(list.peekFirst()); 331 | Assert.assertNull(list.peekLast()); 332 | int testCount = 10; 333 | for (int j = 0; j < testCount; j++) { 334 | list.add(j); 335 | } 336 | Assert.assertEquals(list.getFirst(), list.peek()); 337 | Assert.assertEquals(list.getFirst(), list.peekFirst()); 338 | Assert.assertEquals(list.getLast(), list.peekLast()); 339 | Assert.assertEquals(testCount, list.size()); 340 | } 341 | 342 | @Test 343 | public void testPoll() { 344 | ThriftyList list = new ThriftyList(); 345 | Assert.assertNull(list.poll()); 346 | Assert.assertNull(list.pollFirst()); 347 | Assert.assertNull(list.pollLast()); 348 | int testCount = 10; 349 | for (int j = 0; j < testCount; j++) { 350 | list.add(j); 351 | } 352 | Assert.assertEquals(list.getFirst(), list.poll()); 353 | Assert.assertEquals(list.getFirst(), list.pollFirst()); 354 | Assert.assertEquals(list.getLast(), list.pollLast()); 355 | Assert.assertEquals(testCount - 3, list.size()); 356 | } 357 | 358 | @Test 359 | public void testRemoveBack() { 360 | ThriftyList list = new ThriftyList(); 361 | int testCount = 100; 362 | for (int j = 0; j < testCount; j++) { 363 | list.add(j); 364 | } 365 | for (int j = testCount - 1; j >= 0; j--) { 366 | Assert.assertEquals(Integer.valueOf(j), list.remove(j)); 367 | } 368 | } 369 | 370 | @Test 371 | public void testRemoveFirstAndLastOccurrence() { 372 | ThriftyList list = new ThriftyList(); 373 | int testCount = 10; 374 | for (int j = 0; j < testCount; j++) { 375 | list.add(j); 376 | } 377 | list.add(0); 378 | list.removeFirstOccurrence(0); 379 | Assert.assertNotSame(Integer.valueOf(0), list.get(0)); 380 | Assert.assertEquals(Integer.valueOf(0), list.peekLast()); 381 | list.add(0, 0); 382 | list.removeLastOccurrence(0); 383 | Assert.assertNotSame(Integer.valueOf(0), list.peekLast()); 384 | Assert.assertEquals(Integer.valueOf(0), list.peekFirst()); 385 | } 386 | 387 | @Test 388 | public void testRemoveFront() { 389 | ThriftyList list = new ThriftyList(); 390 | int testCount = 100; 391 | for (int j = 0; j < testCount; j++) { 392 | list.add(j); 393 | } 394 | for (int j = 0; j < testCount; j++) { 395 | Assert.assertEquals(Integer.valueOf(j), list.remove(0)); 396 | } 397 | } 398 | 399 | @Test 400 | public void testSerialize() { 401 | ThriftyList list = new ThriftyList(); 402 | int testCount = 10; 403 | for (int j = 0; j < testCount; j++) { 404 | list.add(j); 405 | } 406 | try { 407 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 408 | ObjectOutputStream os = new ObjectOutputStream(out); 409 | try { 410 | os.writeObject(list); 411 | } finally { 412 | os.close(); 413 | } 414 | ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream( 415 | out.toByteArray())); 416 | try { 417 | list = (ThriftyList) is.readObject(); 418 | } finally { 419 | is.close(); 420 | } 421 | } catch (Exception e) { 422 | StringWriter writer = new StringWriter(); 423 | e.printStackTrace(new PrintWriter(writer)); 424 | Assert.fail(writer.toString()); 425 | } 426 | for (int j = 0; j < testCount; j++) { 427 | Assert.assertEquals(Integer.valueOf(j), list.get(j)); 428 | } 429 | 430 | list = new ThriftyList(); 431 | try { 432 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 433 | ObjectOutputStream os = new ObjectOutputStream(out); 434 | try { 435 | os.writeObject(list); 436 | } finally { 437 | os.close(); 438 | } 439 | ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream( 440 | out.toByteArray())); 441 | try { 442 | list = (ThriftyList) is.readObject(); 443 | } finally { 444 | is.close(); 445 | } 446 | } catch (Exception e) { 447 | StringWriter writer = new StringWriter(); 448 | e.printStackTrace(new PrintWriter(writer)); 449 | Assert.fail(writer.toString()); 450 | } 451 | Assert.assertTrue(list.isEmpty()); 452 | } 453 | 454 | @Test 455 | public void testSet() { 456 | ThriftyList list = new ThriftyList(); 457 | int testCount = 10; 458 | Integer dummyValue = -1; 459 | for (int j = 0; j < testCount; j++) { 460 | list.add(dummyValue); 461 | } 462 | for (int j = 0; j < testCount; j++) { 463 | list.set(j, Integer.valueOf(j)); 464 | } 465 | for (int j = 0; j < testCount; j++) { 466 | Assert.assertEquals(Integer.valueOf(j), list.get(j)); 467 | } 468 | } 469 | 470 | @Test 471 | public void testSizeAndClear() { 472 | ThriftyList list = new ThriftyList(); 473 | int testCount = 10; 474 | for (int j = 0; j < testCount; j++) { 475 | list.add(j); 476 | } 477 | Assert.assertEquals(testCount, list.size()); 478 | list.clear(); 479 | Assert.assertTrue(list.isEmpty()); 480 | } 481 | } 482 | -------------------------------------------------------------------------------- /src/main/java/blogspot/software_and_algorithms/stern_library/data_structure/RedBlackTree.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.data_structure; 2 | 3 | import java.util.Comparator; 4 | import java.util.Iterator; 5 | import java.util.NoSuchElementException; 6 | 7 | import blogspot.software_and_algorithms.stern_library.data_structure.RedBlackTree.Node.NodeColor; 8 | 9 | /* Copyright (c) 2012 Kevin L. Stern 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a copy 12 | * of this software and associated documentation files (the "Software"), to deal 13 | * in the Software without restriction, including without limitation the rights 14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | * copies of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be included in 19 | * all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | */ 29 | 30 | /** 31 | * A red-black tree is a binary search tree guaranteeing that no path 32 | * from root to leaf is more than twice as long as any other such path. This 33 | * property provides an assurance that the height of a red-black tree is 34 | * logarithmic in the number of nodes in the tree. 35 | *

36 | * This implementation is based upon Cormen, Leiserson, Rivest, Stein's 37 | * Introduction to Algorithms book. 38 | * 39 | * See Introduction to Algorithms Cormen, Leiserson, Rivest, and Stein. 40 | * Introduction to Algorithms. 2nd ed. Cambridge, MA: MIT Press, 2001. 41 | * ISBN: 0262032937. 42 | */ 43 | public class RedBlackTree implements Iterable { 44 | private Comparator comparator; 45 | private Node root; 46 | private int size; 47 | 48 | /** 49 | * Default constructor. Tree uses the natural ordering of elements defined by 50 | * {@link Comparable#compareTo(Object)}; for custom ordering see 51 | * {@link RedBlackTree#RedBlackTree(Comparator)}. 52 | */ 53 | public RedBlackTree() { 54 | this(null); 55 | } 56 | 57 | /** 58 | * Construct a new tree which uses the specified custom comparator. 59 | * 60 | * @param comparator 61 | * the comparator to use when ordering elements. 62 | */ 63 | public RedBlackTree(Comparator comparator) { 64 | this.comparator = comparator; 65 | this.root = null; 66 | this.size = 0; 67 | } 68 | 69 | /** 70 | * Clear all entries from this tree. 71 | */ 72 | public void clear() { 73 | root = null; 74 | size = 0; 75 | } 76 | 77 | /** 78 | * Convenience method to compare two values either by use of the Comparator, 79 | * if not null, or by casting to Comparable otherwise. This method may result 80 | * in a ClassCastException if the tree holds non-Comparable values and no 81 | * Comparator was specified upon construction. 82 | * 83 | * @param val1 84 | * the lhs of the compare operation. 85 | * @param val2 86 | * the rhs of the compare operation. 87 | * @return a negative integer, zero, or a positive integer depending upon 88 | * whether val1 is less than, equal to, or greater than val2, 89 | * respectively. 90 | */ 91 | private int compare(T val1, T val2) { 92 | return comparator == null ? ((Comparable) val1).compareTo(val2) 93 | : comparator.compare(val1, val2); 94 | } 95 | 96 | /** 97 | * Test whether or not the specified value is an element of this tree. 98 | * 99 | * @param value 100 | * the query value. 101 | * @return true if the specified value is an element of this tree, false 102 | * otherwise. 103 | */ 104 | public boolean contains(T value) { 105 | return getNode(value) != null; 106 | } 107 | 108 | /** 109 | * Create a new node with the specified value. This method allows a subclass 110 | * to assert control over the type of the nodes held by this tree. 111 | * 112 | * @param value 113 | * the value to apply to the new node. 114 | * @return a new node holding the specified value. 115 | */ 116 | protected Node createNewNode(T value) { 117 | return new Node(value); 118 | } 119 | 120 | /** 121 | * Delete the specified value from this tree. 122 | * 123 | * @param value 124 | * the value to delete. 125 | * @return the node which contained the value before it was removed from this 126 | * tree, null if the value is not an element of this tree. 127 | */ 128 | public Node delete(T value) { 129 | if (value == null) { 130 | return null; 131 | } 132 | Node node = getNode(value); 133 | if (node == null) 134 | return null; 135 | Node swap; 136 | if (!(node.getLeft() == null || node.getRight() == null)) { 137 | Node successor = getSuccessor(node); 138 | exchangeValues(node, successor); 139 | node = successor; 140 | } 141 | if (node.getLeft() != null) { 142 | swap = node.getLeft(); 143 | } else { 144 | swap = node.getRight(); 145 | } 146 | if (swap != null) 147 | swap.setParent(node.getParent()); 148 | if (node.getParent() == null) 149 | root = swap; 150 | else if (node == node.getParent().getLeft()) 151 | node.getParent().setLeft(swap); 152 | else 153 | node.getParent().setRight(swap); 154 | if (node.getColor() == NodeColor.BLACK) { 155 | if (root != null) 156 | fixAfterDeletion(swap == null ? node : swap); 157 | } 158 | 159 | size--; 160 | 161 | return node; 162 | } 163 | 164 | /** 165 | * Called by {@link #delete(Object)} when the node to be removed is a leaf. In 166 | * this case, the node's value is exchanged with its successor as per the 167 | * typical binary tree node removal operation. This method allows a subclass 168 | * to influence value exchange behavior (e.g. if additional node information 169 | * needs to be exchanged). 170 | * 171 | * @param node 172 | * the node whose value is to be removed. 173 | * @param successor 174 | * the node to actually be removed. 175 | */ 176 | protected void exchangeValues(Node node, Node successor) { 177 | T tempValue = successor.getValue(); 178 | successor.setValue(node.getValue()); 179 | node.setValue(tempValue); 180 | } 181 | 182 | /** 183 | * Re-balance the tree after a delete operation. 184 | * 185 | * @param node 186 | * the deleted node or the swap node. 187 | */ 188 | protected void fixAfterDeletion(Node node) { 189 | while (node != root && getColor(node) == NodeColor.BLACK) { 190 | if (node == node.getParent().getLeft() 191 | || (node.getParent().getRight() != null && node != node.getParent() 192 | .getRight())) { 193 | Node temp = node.getParent().getRight(); 194 | if (getColor(temp) == NodeColor.RED) { 195 | setColor(temp, NodeColor.BLACK); 196 | setColor(node.getParent(), NodeColor.RED); 197 | leftRotate(node.getParent()); 198 | temp = node.getParent().getRight(); 199 | } 200 | if (getColor(temp.getLeft()) == NodeColor.BLACK 201 | && getColor(temp.getRight()) == NodeColor.BLACK) { 202 | setColor(temp, NodeColor.RED); 203 | node = node.getParent(); 204 | } else { 205 | if (getColor(temp.getRight()) == NodeColor.BLACK) { 206 | setColor(temp.getLeft(), NodeColor.BLACK); 207 | setColor(temp, NodeColor.RED); 208 | rightRotate(temp); 209 | temp = node.getParent().getRight(); 210 | } 211 | setColor(temp, getColor(node.getParent())); 212 | setColor(node.getParent(), NodeColor.BLACK); 213 | setColor(temp.getRight(), NodeColor.BLACK); 214 | leftRotate(node.getParent()); 215 | node = root; 216 | } 217 | } else { 218 | Node temp = node.getParent().getLeft(); 219 | if (getColor(temp) == NodeColor.RED) { 220 | setColor(temp, NodeColor.BLACK); 221 | setColor(node.getParent(), NodeColor.RED); 222 | rightRotate(node.getParent()); 223 | temp = node.getParent().getLeft(); 224 | } 225 | if (getColor(temp.getRight()) == NodeColor.BLACK 226 | && getColor(temp.getLeft()) == NodeColor.BLACK) { 227 | setColor(temp, NodeColor.RED); 228 | node = node.getParent(); 229 | } else { 230 | if (getColor(temp.getLeft()) == NodeColor.BLACK) { 231 | setColor(temp.getRight(), NodeColor.BLACK); 232 | setColor(temp, NodeColor.RED); 233 | leftRotate(temp); 234 | temp = node.getParent().getLeft(); 235 | } 236 | setColor(temp, getColor(node.getParent())); 237 | setColor(node.getParent(), NodeColor.BLACK); 238 | setColor(temp.getLeft(), NodeColor.BLACK); 239 | rightRotate(node.getParent()); 240 | node = root; 241 | } 242 | } 243 | } 244 | setColor(node, NodeColor.BLACK); 245 | } 246 | 247 | /** 248 | * Re-balance the tree after an insert operation. 249 | * 250 | * @param node 251 | * the inserted node. 252 | */ 253 | protected void fixAfterInsertion(Node node) { 254 | while (getColor(node.getParent()) == NodeColor.RED) { 255 | if (node.getParent() == node.getParent().getParent().getLeft()) { 256 | Node temp = node.getParent().getParent().getRight(); 257 | if (getColor(temp) == NodeColor.RED) { 258 | setColor(node.getParent(), (NodeColor.BLACK)); 259 | setColor(temp, NodeColor.BLACK); 260 | setColor(node.getParent().getParent(), NodeColor.RED); 261 | node = node.getParent().getParent(); 262 | } else { 263 | if (node == node.getParent().getRight()) { 264 | node = node.getParent(); 265 | leftRotate(node); 266 | } 267 | setColor(node.getParent(), NodeColor.BLACK); 268 | setColor(node.getParent().getParent(), NodeColor.RED); 269 | rightRotate(node.getParent().getParent()); 270 | } 271 | } else { 272 | Node temp = node.getParent().getParent().getLeft(); 273 | if (getColor(temp) == NodeColor.RED) { 274 | setColor(node.getParent(), NodeColor.BLACK); 275 | setColor(temp, NodeColor.BLACK); 276 | setColor(node.getParent().getParent(), NodeColor.RED); 277 | node = node.getParent().getParent(); 278 | } else { 279 | if (node == node.getParent().getLeft()) { 280 | node = node.getParent(); 281 | rightRotate(node); 282 | } 283 | setColor(node.getParent(), NodeColor.BLACK); 284 | setColor(node.getParent().getParent(), NodeColor.RED); 285 | leftRotate(node.getParent().getParent()); 286 | } 287 | } 288 | } 289 | setColor(root, NodeColor.BLACK); 290 | } 291 | 292 | /** 293 | * Convenience method implementing the concept of a null-node leaf being 294 | * black. 295 | * 296 | * @param node 297 | * the node whose color is to be determined, null is interpreted as a 298 | * null leaf and is assigned the color black. 299 | * @return the color of the specified node. 300 | */ 301 | private NodeColor getColor(Node node) { 302 | return (node == null) ? NodeColor.BLACK : node.getColor(); 303 | } 304 | 305 | /** 306 | * Get the node containing the smallest value held by this tree. 307 | * 308 | * @return the node containing the smallest value held by this tree. 309 | */ 310 | public Node getFirstNode() { 311 | Node result = root; 312 | 313 | if (result != null) { 314 | while (result.getLeft() != null) { 315 | result = result.getLeft(); 316 | } 317 | } 318 | 319 | return result; 320 | } 321 | 322 | /** 323 | * Get the node that holds the specified value. 324 | * 325 | * @param value 326 | * the query value. 327 | * @return the node that holds the specified value, null if none. 328 | */ 329 | public Node getNode(T value) { 330 | if (value == null) { 331 | return null; 332 | } 333 | Node node = root; 334 | while (node != null) { 335 | int delta = compare(node.getValue(), value); 336 | if (delta < 0) { 337 | node = node.getRight(); 338 | } else if (delta > 0) { 339 | node = node.getLeft(); 340 | } else { 341 | break; 342 | } 343 | } 344 | return node; 345 | } 346 | 347 | /** 348 | * Get the predecessor of the specified node. The predecessor of a node n is 349 | * the node with the largest value in the tree smaller than the value held by 350 | * n. 351 | */ 352 | public Node getPredecessor(Node node) { 353 | if (node.getLeft() != null) { 354 | node = node.getLeft(); 355 | while (node.getRight() != null) 356 | node = node.getRight(); 357 | return node; 358 | } 359 | Node temp = node.getParent(); 360 | while (temp != null && node == temp.getLeft()) { 361 | node = temp; 362 | temp = temp.getParent(); 363 | } 364 | return temp; 365 | } 366 | 367 | /** 368 | * Get the root of this tree. 369 | * 370 | * @return the root of this tree. 371 | */ 372 | public Node getRoot() { 373 | return root; 374 | } 375 | 376 | /** 377 | * Get the number of elements contained within this tree. 378 | * 379 | * @return the number of elements contained within this tree. 380 | */ 381 | public int getSize() { 382 | return size; 383 | } 384 | 385 | /** 386 | * Get the successor of the specified node. The successor of a node n is the 387 | * node with the smallest value in the tree larger than the value held by n. 388 | */ 389 | public Node getSuccessor(Node node) { 390 | if (node.getRight() != null) { 391 | node = node.getRight(); 392 | while (node.getLeft() != null) 393 | node = node.getLeft(); 394 | return node; 395 | } 396 | Node temp = node.getParent(); 397 | while (temp != null && node == temp.getRight()) { 398 | node = temp; 399 | temp = temp.getParent(); 400 | } 401 | return temp; 402 | } 403 | 404 | /** 405 | * Insert the specified value into this tree. 406 | * 407 | * @param value 408 | * the value to insert. 409 | * @return the new node containing the specified value if the value was not 410 | * already present in the tree, null otherwise. 411 | */ 412 | public Node insert(T value) { 413 | Node node = null; 414 | Node parent = root; 415 | while (parent != null) { 416 | int delta = compare(parent.getValue(), value); 417 | if (delta < 0) { 418 | if (parent.getRight() == null) { 419 | node = createNewNode(value); 420 | parent.setRight(node); 421 | node.setParent(parent); 422 | parent = null; 423 | } else { 424 | parent = parent.getRight(); 425 | } 426 | } else if (delta > 0) { 427 | if (parent.getLeft() == null) { 428 | node = createNewNode(value); 429 | parent.setLeft(node); 430 | node.setParent(parent); 431 | parent = null; 432 | } else { 433 | parent = parent.getLeft(); 434 | } 435 | } else { 436 | return null; 437 | } 438 | } 439 | if (node == null) { 440 | node = createNewNode(value); 441 | root = node; 442 | } 443 | 444 | setColor(node, NodeColor.RED); 445 | fixAfterInsertion(node); 446 | size++; 447 | 448 | return node; 449 | } 450 | 451 | /** 452 | * @return true if there are no items in this tree, false otherwise. 453 | */ 454 | public boolean isEmpty() { 455 | return size == 0; 456 | } 457 | 458 | /** 459 | * Returns an Iterator over the elements of this tree. 460 | * 461 | * @return an Iterator over the elements of this tree. 462 | */ 463 | @Override 464 | public Iterator iterator() { 465 | return new Iterator() { 466 | private Node cursor = getFirstNode(); 467 | private T lastReturn; 468 | 469 | @Override 470 | public boolean hasNext() { 471 | return cursor != null; 472 | } 473 | 474 | @Override 475 | public T next() { 476 | if (cursor == null) { 477 | throw new NoSuchElementException(); 478 | } 479 | lastReturn = cursor.getValue(); 480 | cursor = getSuccessor(cursor); 481 | return lastReturn; 482 | } 483 | 484 | @Override 485 | public void remove() { 486 | if (lastReturn == null) { 487 | throw new NoSuchElementException(); 488 | } 489 | T currentValue = cursor == null ? null : cursor.getValue(); 490 | delete(lastReturn); 491 | cursor = currentValue == null ? null : getNode(currentValue); 492 | lastReturn = null; 493 | } 494 | }; 495 | } 496 | 497 | /** 498 | * Perform a left rotate operation on the specified node. 499 | * 500 | * @param node 501 | * the node on which the left rotate operation will be performed. 502 | */ 503 | protected void leftRotate(Node node) { 504 | Node temp = node.getRight(); 505 | node.setRight(temp.getLeft()); 506 | if (temp.getLeft() != null) 507 | temp.getLeft().setParent(node); 508 | temp.setParent(node.getParent()); 509 | if (node.getParent() == null) 510 | root = temp; 511 | else if (node == node.getParent().getLeft()) 512 | node.getParent().setLeft(temp); 513 | else 514 | node.getParent().setRight(temp); 515 | temp.setLeft(node); 516 | node.setParent(temp); 517 | } 518 | 519 | /** 520 | * Perform a right rotate operation on the specified node. 521 | * 522 | * @param node 523 | * the node on which a right rotate operation is to be performed. 524 | */ 525 | protected void rightRotate(Node node) { 526 | Node temp = node.getLeft(); 527 | node.setLeft(temp.getRight()); 528 | if (temp.getRight() != null) 529 | temp.getRight().setParent(node); 530 | temp.setParent(node.getParent()); 531 | if (node.getParent() == null) 532 | root = temp; 533 | else if (node == node.getParent().getRight()) 534 | node.getParent().setRight(temp); 535 | else 536 | node.getParent().setLeft(temp); 537 | temp.setRight(node); 538 | node.setParent(temp); 539 | } 540 | 541 | /** 542 | * Convenience method to set the color of the specified node to the specified 543 | * color if the node is non-null. 544 | * 545 | * @param node 546 | * the target node, possibly null. 547 | * @param color 548 | * the target color, non-null. 549 | */ 550 | private void setColor(Node node, NodeColor color) { 551 | if (node != null) 552 | node.setColor(color); 553 | } 554 | 555 | /** 556 | * {@inheritDoc} 557 | */ 558 | @Override 559 | public String toString() { 560 | StringBuilder builder = new StringBuilder("{"); 561 | if (!isEmpty()) { 562 | for (Iterator i = iterator();;) { 563 | builder.append(i.next()); 564 | if (i.hasNext()) { 565 | builder.append(", "); 566 | } else { 567 | break; 568 | } 569 | } 570 | } 571 | builder.append("}"); 572 | return builder.toString(); 573 | } 574 | 575 | /** 576 | * A red-black tree node is a binary tree node augmented to hold an 577 | * additional piece of information called the node's color. The 578 | * domain of values from which a red-black tree node's color is assigned 579 | * comprises red and black. A red-black tree requires that 580 | * its nodes provide the ability to set the parent and children and to set the 581 | * value stored by the node. This class encapsulates the concept of a 582 | * red-black tree node. 583 | */ 584 | public static class Node { 585 | private NodeColor color; 586 | private Node left, right, parent; 587 | private T value; 588 | 589 | /** 590 | * Construct a new node with the specified value. 591 | * 592 | * @param value 593 | * the value to store in this node. 594 | */ 595 | public Node(T value) { 596 | if (value == null) { 597 | throw new NullPointerException("value is null"); 598 | } 599 | 600 | this.value = value; 601 | } 602 | 603 | /** 604 | * Get the color. 605 | * 606 | * @return the color, never null. 607 | */ 608 | public NodeColor getColor() { 609 | return color; 610 | } 611 | 612 | /** 613 | * Get the left child. 614 | * 615 | * @return the left child, possibly null. 616 | */ 617 | public Node getLeft() { 618 | return left; 619 | } 620 | 621 | /** 622 | * Get the parent. 623 | * 624 | * @return the parent, possibly null. 625 | */ 626 | public Node getParent() { 627 | return parent; 628 | } 629 | 630 | /** 631 | * Get the right child. 632 | * 633 | * @return the right child, possibly null. 634 | */ 635 | public Node getRight() { 636 | return right; 637 | } 638 | 639 | /** 640 | * Get the value. 641 | * 642 | * @return the value. 643 | */ 644 | public T getValue() { 645 | return value; 646 | } 647 | 648 | /** 649 | * Test whether or not this node is a leaf node. 650 | * 651 | * @return true if this node is a leaf node, false otherwise. 652 | */ 653 | public boolean isLeaf() { 654 | return left == null && right == null; 655 | } 656 | 657 | /** 658 | * Set the color. 659 | * 660 | * @param color 661 | * the color to set to this node. 662 | */ 663 | protected void setColor(NodeColor color) { 664 | this.color = color; 665 | } 666 | 667 | /** 668 | * Set the left child. 669 | * 670 | * @param node 671 | * the node to set as the left child of this node. 672 | */ 673 | protected void setLeft(Node node) { 674 | this.left = node; 675 | } 676 | 677 | /** 678 | * Set the parent. 679 | * 680 | * @param node 681 | * the node to set as the parent of this node. 682 | */ 683 | protected void setParent(Node node) { 684 | this.parent = node; 685 | } 686 | 687 | /** 688 | * Set the right child. 689 | * 690 | * @param node 691 | * the node to set as the right child of this node. 692 | */ 693 | protected void setRight(Node node) { 694 | this.right = node; 695 | } 696 | 697 | /** 698 | * Set the value. 699 | * 700 | * @param value 701 | * the value to store in this node, must be non-null. 702 | */ 703 | protected void setValue(T value) { 704 | if (value == null) { 705 | throw new IllegalArgumentException("value is null"); 706 | } 707 | this.value = value; 708 | } 709 | 710 | /** 711 | * The domain of values from which a node's color is assigned. 712 | */ 713 | public static enum NodeColor { 714 | BLACK, RED 715 | } 716 | } 717 | } 718 | -------------------------------------------------------------------------------- /src/main/java/blogspot/software_and_algorithms/stern_library/data_structure/StaticIntervalTree.java: -------------------------------------------------------------------------------- 1 | package blogspot.software_and_algorithms.stern_library.data_structure; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.Collections; 6 | import java.util.Comparator; 7 | import java.util.Iterator; 8 | import java.util.List; 9 | import java.util.Set; 10 | 11 | /* Copyright (c) 2012 Kevin L. Stern 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining a copy 14 | * of this software and associated documentation files (the "Software"), to deal 15 | * in the Software without restriction, including without limitation the rights 16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | * copies of the Software, and to permit persons to whom the Software is 18 | * furnished to do so, subject to the following conditions: 19 | * 20 | * The above copyright notice and this permission notice shall be included in 21 | * all copies or substantial portions of the Software. 22 | * 23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | * SOFTWARE. 30 | */ 31 | 32 | /** 33 | * A static interval tree is a balanced binary search tree which is 34 | * built to store a pre-specified set of intervals so that both point queries 35 | * (queries that return intervals from the set which contain a query point) and 36 | * overlapping interval queries (queries that return intervals from the set 37 | * which overlap a query interval) can be completed in time O(log(n)+k), where n 38 | * is the size of the pre-specified set of intervals and k is the size of the 39 | * result set from the query. 40 | *

41 | * While this implementation of a static interval tree is built to support a 42 | * pre-specified set of intervals, intervals from this set may be added to and 43 | * removed from the tree at will, giving a semi-static nature to the tree. The 44 | * construction process completes in time O(n*log(n)) where n is the size of the 45 | * set of intervals with which the tree is built. 46 | *

47 | * Insertion and deletion of intervals to and from a constructed tree completes 48 | * in time O(log(n)) where n is the size of the set of intervals with which the 49 | * tree was built. 50 | *

51 | * A constructed tree consumes linear space in the size of the set of intervals 52 | * with which the tree was built. 53 | *

54 | * Note that this implementation supports all three closed, open and half-open 55 | * intervals. 56 | * 57 | * @author Kevin L. Stern 58 | */ 59 | public class StaticIntervalTree, T extends Interval> { 60 | private Node root; 61 | private int size; 62 | 63 | /** 64 | * Default constructor. 65 | */ 66 | public StaticIntervalTree() { 67 | size = 0; 68 | } 69 | 70 | /** 71 | * Internal helper method to construct a subtree structure capable of holding 72 | * the elements of the specified portion of the specified list of intervals. 73 | * 74 | * @param intervalList 75 | * the list of intervals with which to build the subtree; must be 76 | * ordered by low endpoint. 77 | * @param low 78 | * the low index of the portion of intervalList to consider, 79 | * inclusive. 80 | * @param high 81 | * the high index of the portion of intervalList to consider, 82 | * exclusive. 83 | */ 84 | private Node buildSubtree(List intervalList, int low, int high) { 85 | U point = intervalList.get((low + high) >>> 1).getLow(); 86 | Node result = new Node(point); 87 | 88 | int lowPointer = low; 89 | int highPointer = high; 90 | for (int j = low; j < highPointer; j++) { 91 | T next = intervalList.get(j); 92 | if (next.getHigh().compareTo(point) < 0) { 93 | Collections.swap(intervalList, lowPointer++, j); 94 | } else if (next.getLow().compareTo(point) > 0) { 95 | highPointer = j; 96 | } 97 | } 98 | if (low < lowPointer) { 99 | result.setLeft(buildSubtree(intervalList, low, lowPointer)); 100 | } 101 | if (highPointer < high) { 102 | result.setRight(buildSubtree(intervalList, highPointer, high)); 103 | } 104 | 105 | return result; 106 | } 107 | 108 | /** 109 | * Build the interval tree to support the elements of the specified set of 110 | * intervals. Note that this method does not insert intervals into the tree 111 | * (this must be done via {@link #insert(Interval)} after the tree is built). 112 | * 113 | * @param intervals 114 | * the set of intervals for which the tree is to be built. 115 | */ 116 | public void buildTree(Set intervals) { 117 | List intervalList = new ArrayList(intervals); 118 | Collections.sort(intervalList, new Comparator() { 119 | @Override 120 | public int compare(T o1, T o2) { 121 | return o1.getLow().compareTo(o2.getLow()); 122 | } 123 | }); 124 | root = buildSubtree(intervalList, 0, intervals.size()); 125 | size = 0; 126 | } 127 | 128 | /** 129 | * Clear the contents of the tree, leaving the tree structure intact. 130 | */ 131 | public void clear() { 132 | if (root != null) { 133 | List> stack = new ArrayList>(); 134 | stack.add(root); 135 | while (!stack.isEmpty()) { 136 | Node next = stack.remove(stack.size() - 1); 137 | next.clear(); 138 | Node temp; 139 | if ((temp = next.getLeft()) != null) { 140 | stack.add(temp); 141 | } 142 | if ((temp = next.getRight()) != null) { 143 | stack.add(temp); 144 | } 145 | } 146 | } 147 | size = 0; 148 | } 149 | 150 | /** 151 | * Delete the specified interval from this tree. 152 | * 153 | * @param interval 154 | * the interval to delete. 155 | * @return true if an element was deleted as a result of this call, false 156 | * otherwise. 157 | */ 158 | public boolean delete(T interval) { 159 | if (interval == null) { 160 | return false; 161 | } 162 | Node node = root; 163 | while (node != null) { 164 | U temp = node.getPoint(); 165 | if (interval.getLow().compareTo(temp) <= 0 166 | && temp.compareTo(interval.getHigh()) <= 0) { 167 | if (node.delete(interval)) { 168 | size -= 1; 169 | return true; 170 | } 171 | } else if (interval.getHigh().compareTo(temp) < 0) { 172 | node = node.getLeft(); 173 | } else { 174 | node = node.getRight(); 175 | } 176 | } 177 | return false; 178 | } 179 | 180 | /** 181 | * Fetch intervals containing the specified point. 182 | * 183 | * @param target 184 | * the target Collection into which to place the desired intervals. 185 | * @param queryPoint 186 | * the query point. 187 | * @return the target Collection. 188 | */ 189 | public > V fetchContainingIntervals(V target, 190 | U queryPoint) { 191 | if (target == null) { 192 | throw new NullPointerException("target is null"); 193 | } 194 | if (queryPoint == null) { 195 | throw new NullPointerException("queryPoint is null"); 196 | } 197 | Node node = root; 198 | while (node != null) { 199 | U temp = node.getPoint(); 200 | if (queryPoint.equals(temp)) { 201 | node.fetchIntervalsContainingNodePoint(target); 202 | node = null; 203 | } else if (queryPoint.compareTo(temp) < 0) { 204 | node.fetchIntervalsContainingPointLow(target, queryPoint, true); 205 | node = node.getLeft(); 206 | } else { 207 | node.fetchIntervalsContainingPointHigh(target, queryPoint, true); 208 | node = node.getRight(); 209 | } 210 | } 211 | return target; 212 | } 213 | 214 | /** 215 | * Fetch intervals overlapping the specified interval. 216 | * 217 | * @param target 218 | * the target Collection into which to place the desired intervals. 219 | * @param queryInterval 220 | * the query interval. 221 | * @return the target Collection. 222 | */ 223 | public > V fetchOverlappingIntervals(V target, 224 | T queryInterval) { 225 | if (target == null) { 226 | throw new NullPointerException("target is null"); 227 | } 228 | if (queryInterval == null) { 229 | throw new NullPointerException("queryInterval is null"); 230 | } 231 | List> stack = new ArrayList>(); 232 | if (root != null) { 233 | stack.add(root); 234 | } 235 | while (!stack.isEmpty()) { 236 | Node node = stack.remove(stack.size() - 1); 237 | U temp = node.getPoint(); 238 | if (queryInterval.getLow().compareTo(temp) <= 0 239 | && temp.compareTo(queryInterval.getHigh()) <= 0) { 240 | node.fetchOverlappingIntervals(target, queryInterval); 241 | if (node.getLeft() != null) { 242 | stack.add(node.getLeft()); 243 | } 244 | if (node.getRight() != null) { 245 | stack.add(node.getRight()); 246 | } 247 | } else if (queryInterval.getHigh().compareTo(temp) < 0) { 248 | node.fetchIntervalsContainingPointLow(target, queryInterval.getHigh(), 249 | queryInterval.isClosedOnHigh()); 250 | if (node.getLeft() != null) { 251 | stack.add(node.getLeft()); 252 | } 253 | } else { 254 | node.fetchIntervalsContainingPointHigh(target, queryInterval.getLow(), 255 | queryInterval.isClosedOnLow()); 256 | if (node.getRight() != null) { 257 | stack.add(node.getRight()); 258 | } 259 | } 260 | } 261 | return target; 262 | } 263 | 264 | /** 265 | * Get the number of intervals being stored in the tree. 266 | * 267 | * @return the number of intervals being stored in the tree. 268 | */ 269 | public int getSize() { 270 | return size; 271 | } 272 | 273 | /** 274 | * Insert the specified interval into this tree. Behavior is undefined when 275 | * the interval was not included in the set of intervals presented at the most 276 | * recent call to {@link #buildTree(Set)}. 277 | * 278 | * @param interval 279 | * the interval to insert. 280 | * @return true if an element was inserted as a result of this call, false 281 | * otherwise. 282 | */ 283 | public boolean insert(T interval) { 284 | Node node = root; 285 | while (node != null) { 286 | U temp = node.getPoint(); 287 | if (interval.getLow().compareTo(temp) <= 0 288 | && temp.compareTo(interval.getHigh()) <= 0) { 289 | if (node.insert(interval)) { 290 | size++; 291 | return true; 292 | } 293 | } else if (interval.getHigh().compareTo(temp) < 0) { 294 | node = node.getLeft(); 295 | } else { 296 | node = node.getRight(); 297 | } 298 | } 299 | return false; 300 | } 301 | 302 | /** 303 | * A node for a static interval tree is a binary tree node augmented 304 | * with an associated point value and the ability to store intervals. 305 | *

306 | * Intervals that are stored within this node either contain the node's point 307 | * value or are open at an endpoint that equals the node's point value. 308 | * Intervals are stored so that these two cases are easily distinguished from 309 | * one another: Each such class of interval is stored in two structures, one 310 | * is a tree sorted by low endpoint and the other is a tree sorted by high 311 | * endpoint. This enables efficient point queries as well as insertions and 312 | * deletions from the node. 313 | */ 314 | protected static class Node, T extends Interval> { 315 | private RedBlackTree highOrderedContainingIntervals = new OrderLinkedRedBlackTree( 316 | new Comparator() { 317 | @Override 318 | public int compare(T o1, T o2) { 319 | int result = o1.getHigh().compareTo(o2.getHigh()); 320 | if (result == 0) { 321 | if (o1.isClosedOnHigh() != o2.isClosedOnHigh()) { 322 | result = o1.isClosedOnHigh() ? 1 : -1; 323 | } else { 324 | result = o1.compareTo(o2); 325 | } 326 | } 327 | return result > 0 ? -1 : result < 0 ? 1 : 0; 328 | } 329 | }); 330 | private RedBlackTree lowOrderedContainingIntervals = new OrderLinkedRedBlackTree( 331 | new Comparator() { 332 | @Override 333 | public int compare(T o1, T o2) { 334 | int result = o1.getLow().compareTo(o2.getLow()); 335 | if (result == 0) { 336 | result = o1.compareTo(o2); 337 | } 338 | return result > 0 ? 1 : result < 0 ? -1 : 0; 339 | } 340 | }); 341 | private RedBlackTree highOrderedExcludingIntervals = new OrderLinkedRedBlackTree( 342 | new Comparator() { 343 | @Override 344 | public int compare(T o1, T o2) { 345 | int result = o1.getHigh().compareTo(o2.getHigh()); 346 | if (result == 0) { 347 | if (o1.isClosedOnHigh() != o2.isClosedOnHigh()) { 348 | result = o1.isClosedOnHigh() ? 1 : -1; 349 | } else { 350 | result = o1.compareTo(o2); 351 | } 352 | } 353 | return result > 0 ? -1 : result < 0 ? 1 : 0; 354 | } 355 | }); 356 | private RedBlackTree lowOrderedExcludingIntervals = new OrderLinkedRedBlackTree( 357 | new Comparator() { 358 | @Override 359 | public int compare(T o1, T o2) { 360 | int result = o1.getLow().compareTo(o2.getLow()); 361 | if (result == 0) { 362 | result = o1.compareTo(o2); 363 | } 364 | return result > 0 ? 1 : result < 0 ? -1 : 0; 365 | } 366 | }); 367 | private Node left, right; 368 | private U point; 369 | 370 | /** 371 | * Construct a new node associated with the specified point. 372 | * 373 | * @param point 374 | * the point with which this node is associated. 375 | */ 376 | public Node(U point) { 377 | if (point == null) { 378 | throw new NullPointerException("point is null"); 379 | } 380 | this.point = point; 381 | } 382 | 383 | /** 384 | * Clear the elements of this node. 385 | */ 386 | public void clear() { 387 | lowOrderedContainingIntervals.clear(); 388 | highOrderedContainingIntervals.clear(); 389 | lowOrderedExcludingIntervals.clear(); 390 | highOrderedExcludingIntervals.clear(); 391 | } 392 | 393 | /** 394 | * Delete the specified interval from this node. 395 | * 396 | * @param interval 397 | * the interval to delete. 398 | * @return true if an element was deleted as a result of this call, false 399 | * otherwise. 400 | */ 401 | public boolean delete(T interval) { 402 | if (interval.contains(point)) { 403 | if (lowOrderedContainingIntervals.delete(interval) == null) { 404 | return false; 405 | } 406 | highOrderedContainingIntervals.delete(interval); 407 | } else { 408 | if (lowOrderedExcludingIntervals.delete(interval) == null) { 409 | return false; 410 | } 411 | highOrderedExcludingIntervals.delete(interval); 412 | } 413 | return true; 414 | } 415 | 416 | /** 417 | * Fetch all intervals from this node that contain the node's point. 418 | * 419 | * @param target 420 | * the target Collection into which to place the desired intervals. 421 | */ 422 | public void fetchIntervalsContainingNodePoint(Collection target) { 423 | if (highOrderedContainingIntervals.getSize() > 0) { 424 | RedBlackTree.Node temp = highOrderedContainingIntervals 425 | .getFirstNode(); 426 | while (temp != null) { 427 | target.add(temp.getValue()); 428 | temp = highOrderedContainingIntervals.getSuccessor(temp); 429 | } 430 | } 431 | } 432 | 433 | /** 434 | * Fetch intervals containing the specified value. This method is called 435 | * when the specified value is greater than this node's point. 436 | * 437 | * @param target 438 | * the target Collection into which to place the desired intervals. 439 | * @param queryPoint 440 | * the query value. 441 | * @param isClosedOnValue 442 | * true if the search is inclusive of the specified value, false 443 | * otherwise. 444 | */ 445 | public void fetchIntervalsContainingPointHigh(Collection target, 446 | U queryPoint, 447 | boolean isClosedOnValue) { 448 | for (Iterator i = highOrderedContainingIntervals.iterator(); i 449 | .hasNext();) { 450 | T next = i.next(); 451 | int cmp = next.getHigh().compareTo(queryPoint); 452 | if (cmp < 0 || cmp == 0 && (!isClosedOnValue || !next.isClosedOnHigh())) { 453 | break; 454 | } 455 | target.add(next); 456 | } 457 | for (Iterator i = highOrderedExcludingIntervals.iterator(); i 458 | .hasNext();) { 459 | T next = i.next(); 460 | int cmp = next.getHigh().compareTo(queryPoint); 461 | if (cmp < 0 || cmp == 0 && (!isClosedOnValue || !next.isClosedOnHigh())) { 462 | break; 463 | } 464 | target.add(next); 465 | } 466 | } 467 | 468 | /** 469 | * Fetch intervals containing the specified value. This method is called 470 | * when the specified value is less than this node's point. 471 | * 472 | * @param target 473 | * the target Collection into which to place the desired intervals. 474 | * @param queryPoint 475 | * the query value. 476 | * @param isClosedOnValue 477 | * true if the search is inclusive of the specified value, false 478 | * otherwise. 479 | */ 480 | public void fetchIntervalsContainingPointLow(Collection target, 481 | U queryPoint, 482 | boolean isClosedOnValue) { 483 | for (Iterator i = lowOrderedContainingIntervals.iterator(); i 484 | .hasNext();) { 485 | T next = i.next(); 486 | int cmp = next.getLow().compareTo(queryPoint); 487 | if (cmp > 0 || cmp == 0 && (!isClosedOnValue || !next.isClosedOnLow())) 488 | break; 489 | target.add(next); 490 | } 491 | for (Iterator i = lowOrderedExcludingIntervals.iterator(); i.hasNext();) { 492 | T next = i.next(); 493 | int cmp = next.getLow().compareTo(queryPoint); 494 | if (cmp > 0 || cmp == 0 && (!isClosedOnValue || !next.isClosedOnLow())) 495 | break; 496 | target.add(next); 497 | } 498 | } 499 | 500 | /** 501 | * Fetch all intervals from this node which overlap the specified interval. 502 | * By contract, the interval must be such that {@link Interval#getLow()} 503 | * {@link Node#getPoint()} 504 | * {@link Interval#getHigh()}. 505 | * 506 | * @param target 507 | * the target Collection into which to place the desired intervals. 508 | * @param queryInterval 509 | * the query interval. 510 | */ 511 | public void fetchOverlappingIntervals(Collection target, 512 | Interval queryInterval) { 513 | if (queryInterval.getLow().compareTo(point) == 0) { 514 | fetchIntervalsContainingPointHigh(target, queryInterval.getLow(), 515 | queryInterval.isClosedOnLow()); 516 | } else if (queryInterval.getHigh().compareTo(point) == 0) { 517 | fetchIntervalsContainingPointLow(target, queryInterval.getHigh(), 518 | queryInterval.isClosedOnHigh()); 519 | } else { 520 | fetchIntervalsContainingNodePoint(target); 521 | if (highOrderedExcludingIntervals.getSize() > 0) { 522 | RedBlackTree.Node temp = highOrderedExcludingIntervals 523 | .getFirstNode(); 524 | while (temp != null) { 525 | target.add(temp.getValue()); 526 | temp = highOrderedExcludingIntervals.getSuccessor(temp); 527 | } 528 | } 529 | } 530 | } 531 | 532 | /** 533 | * Get the left child. 534 | * 535 | * @return the left child, null if none exists. 536 | */ 537 | public Node getLeft() { 538 | return left; 539 | } 540 | 541 | /** 542 | * Get the point associated with this node. 543 | * 544 | * @return the point associated with this node. 545 | */ 546 | public U getPoint() { 547 | return point; 548 | } 549 | 550 | /** 551 | * Get the right child. 552 | * 553 | * @return the right child, null if none exists. 554 | */ 555 | public Node getRight() { 556 | return right; 557 | } 558 | 559 | /** 560 | * Insert the specified interval into this node. By contract, the interval 561 | * must be such that {@link Interval#getLow()} <= 562 | * {@link Node#getPoint()} <= {@link Interval#getHigh()}. 563 | * 564 | * @param interval 565 | * the interval to insert. 566 | * @return true if an element was inserted as a result of this call, false 567 | * otherwise. 568 | */ 569 | private boolean insert(T interval) { 570 | if (interval.contains(point)) { 571 | if (lowOrderedContainingIntervals.insert(interval) == null) { 572 | return false; 573 | } 574 | highOrderedContainingIntervals.insert(interval); 575 | } else { 576 | if (lowOrderedExcludingIntervals.insert(interval) == null) { 577 | return false; 578 | } 579 | highOrderedExcludingIntervals.insert(interval); 580 | } 581 | return true; 582 | } 583 | 584 | /** 585 | * Set the left child to the specified node. 586 | * 587 | * @param node 588 | * the left child. 589 | */ 590 | private void setLeft(Node node) { 591 | left = node; 592 | } 593 | 594 | /** 595 | * Set the right child to the specified node. 596 | * 597 | * @param node 598 | * the right child. 599 | */ 600 | private void setRight(Node node) { 601 | right = node; 602 | } 603 | 604 | /** 605 | * {@inheritDoc} 606 | */ 607 | @Override 608 | public String toString() { 609 | StringBuilder builder = new StringBuilder("{"); 610 | for (Iterator i = lowOrderedContainingIntervals.iterator();;) { 611 | builder.append(i.next().toString()); 612 | if (i.hasNext()) { 613 | builder.append(", "); 614 | } else { 615 | break; 616 | } 617 | } 618 | for (Iterator i = lowOrderedExcludingIntervals.iterator(); i.hasNext();) { 619 | builder.append(", ").append(i.next().toString()); 620 | } 621 | builder.append("}"); 622 | return builder.toString(); 623 | } 624 | } 625 | } 626 | --------------------------------------------------------------------------------