├── .gitattributes ├── .github └── workflows │ └── gradle.yml ├── .gitignore ├── README.md ├── build.gradle ├── config └── checkstyle │ ├── checkstyle.xml │ └── suppressions.xml ├── docs ├── assets │ └── images │ │ ├── (2,4)tree.jpg │ │ ├── (a,b)childpolicy.jpg │ │ ├── 1DORS.jpg │ │ ├── 1DORSDynamicUpdates.jpg │ │ ├── 1DORSQuery.jpeg │ │ ├── 2DORS.jpg │ │ ├── 2DORSQuery.jpg │ │ ├── 2DORSTrees.jpg │ │ ├── AvlTree.png │ │ ├── BalancedProof.png │ │ ├── BinarySearch.png │ │ ├── BinarySearchTemplated1.jpeg │ │ ├── BinarySearchTemplated2.jpeg │ │ ├── BinarySearchTemplated3.jpeg │ │ ├── BubbleSort.jpeg │ │ ├── CountingSort.png │ │ ├── InsertionSort.png │ │ ├── LectureHoares.jpeg │ │ ├── Lomutos.jpeg │ │ ├── MSTProperty3.png │ │ ├── MSTProperty4.png │ │ ├── MergeSortIterative.jpg │ │ ├── MergeSortRecursive.png │ │ ├── ParanoidQuickSort.jpeg │ │ ├── PathCompression.png │ │ ├── QuickFind.png │ │ ├── QuickSortFirstPivot.png │ │ ├── QuickSortOverview.jpeg │ │ ├── QuickUnion1.png │ │ ├── QuickUnion2.png │ │ ├── RadixSort.png │ │ ├── SegmentTree.png │ │ ├── SelectionSort.png │ │ ├── ThreeWayPartitioning.jpeg │ │ ├── TreeRotation.png │ │ ├── Trie.png │ │ ├── UsualHoares.jpeg │ │ ├── WeightedUnion.png │ │ ├── WeightedUnionLeetCode.png │ │ ├── btreesplitchild.jpeg │ │ ├── firstBadVersion.jpeg │ │ ├── kmp.png │ │ └── max_heap.png └── team │ └── profiles.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── scripts ├── README.md └── algorithms │ ├── patternFinding │ └── RunKMP.java │ └── sorting │ ├── countingSort │ └── RunCountingSort.java │ └── insertionSort │ └── RunInsertionSort.java ├── settings.gradle └── src ├── main └── java │ ├── algorithms │ ├── binarySearch │ │ ├── README.md │ │ ├── binarySearch │ │ │ ├── BinarySearch.java │ │ │ └── README.md │ │ └── binarySearchTemplated │ │ │ ├── BinarySearchTemplated.java │ │ │ ├── README.md │ │ │ └── binarySearchTemplatedExamples │ │ │ └── README.md │ ├── minimumSpanningTree │ │ ├── README.md │ │ ├── kruskal │ │ │ ├── Kruskal.java │ │ │ └── README.md │ │ └── prim │ │ │ ├── Prim.java │ │ │ └── README.md │ ├── orthogonalRangeSearching │ │ ├── RangeTreeNode.java │ │ ├── oneDim │ │ │ ├── OrthogonalRangeSearching.java │ │ │ └── README.md │ │ └── twoDim │ │ │ ├── OrthogonalRangeSearching.java │ │ │ └── README.md │ ├── patternFinding │ │ ├── KMP.java │ │ └── README.md │ └── sorting │ │ ├── bubbleSort │ │ ├── BubbleSort.java │ │ └── README.md │ │ ├── countingSort │ │ ├── CountingSort.java │ │ └── README.md │ │ ├── cyclicSort │ │ ├── README.md │ │ ├── generalised │ │ │ ├── CyclicSort.java │ │ │ └── README.md │ │ └── simple │ │ │ ├── CyclicSort.java │ │ │ ├── FindFirstMissingNonNegative.java │ │ │ └── README.md │ │ ├── insertionSort │ │ ├── InsertionSort.java │ │ └── README.md │ │ ├── mergeSort │ │ ├── iterative │ │ │ ├── MergeSort.java │ │ │ └── README.md │ │ └── recursive │ │ │ ├── MergeSort.java │ │ │ └── README.md │ │ ├── quickSort │ │ ├── README.md │ │ ├── hoares │ │ │ ├── QuickSort.java │ │ │ └── README.md │ │ ├── lomuto │ │ │ ├── QuickSort.java │ │ │ └── README.md │ │ ├── paranoid │ │ │ ├── QuickSort.java │ │ │ └── README.md │ │ └── threeWayPartitioning │ │ │ ├── QuickSort.java │ │ │ └── README.md │ │ ├── radixSort │ │ ├── README.md │ │ └── RadixSort.java │ │ └── selectionSort │ │ ├── README.md │ │ └── SelectionSort.java │ └── dataStructures │ ├── avlTree │ ├── AVLTree.java │ ├── Node.java │ └── README.md │ ├── bTree │ ├── BTree.java │ └── README.md │ ├── binarySearchTree │ ├── BinarySearchTree.java │ ├── Node.java │ └── README.md │ ├── disjointSet │ ├── README.md │ ├── quickFind │ │ ├── DisjointSet.java │ │ └── README.md │ └── weightedUnion │ │ ├── DisjointSet.java │ │ └── README.md │ ├── hashSet │ ├── chaining │ │ └── HashSet.java │ └── openAddressing │ │ ├── HashSet.java │ │ └── README.md │ ├── heap │ ├── MaxHeap.java │ └── README.md │ ├── linkedList │ ├── LinkedList.java │ └── README.md │ ├── lruCache │ ├── LRU.java │ └── README.md │ ├── queue │ ├── Deque │ │ ├── Deque.java │ │ └── README.md │ ├── Queue.java │ ├── README.md │ └── monotonicQueue │ │ ├── MonotonicQueue.java │ │ └── README.md │ ├── segmentTree │ ├── README.md │ ├── SegmentTree.java │ └── arrayRepresentation │ │ └── SegmentTree.java │ ├── stack │ ├── README.md │ └── Stack.java │ └── trie │ ├── README.md │ └── Trie.java └── test └── java ├── algorithms ├── binarySearch │ └── BinarySearchTest.java ├── minimumSpanningTree │ ├── kruskal │ │ └── KruskalTest.java │ └── prim │ │ └── PrimTest.java ├── orthogonalRangeSearching │ ├── oneDim │ │ └── OrthogonalRangeSearchingTest.java │ └── twoDim │ │ └── OrthogonalRangeSearchingTest.java ├── patternFinding │ └── KmpTest.java └── sorting │ ├── bubbleSort │ └── BubbleSortTest.java │ ├── countingSort │ └── CountingSortTest.java │ ├── cyclicSort │ ├── generalised │ │ └── CyclicSortTest.java │ └── simple │ │ ├── FindFirstMissingNonNegativeTest.java │ │ └── SimpleCyclicSortTest.java │ ├── insertionSort │ └── InsertionSortTest.java │ ├── mergeSort │ ├── iterative │ │ └── MergeSortTest.java │ └── recursive │ │ └── MergeSortTest.java │ ├── quickSort │ ├── hoares │ │ └── QuickSortTest.java │ ├── lomuto │ │ └── QuickSortTest.java │ ├── paranoid │ │ └── QuickSortTest.java │ └── threeWayPartitioning │ │ └── QuickSortTest.java │ ├── radixSort │ └── RadixSortTest.java │ └── selectionSort │ └── SelectionSortTest.java └── dataStructures ├── bTree └── BTreeTest.java ├── binarySearchTree └── BinarySearchTreeTest.java ├── disjointSet ├── quickFind │ └── DisjointSetTest.java └── weightedUnion │ └── DisjointSetTest.java ├── hashSet ├── chaining │ └── HashSetTest.java └── openAddressing │ └── HashSetTest.java ├── heap └── MaxHeapTest.java ├── linkedList └── LinkedListTest.java ├── queue └── MonotonicQueueTest.java ├── segmentTree ├── SegmentTreeTest.java └── arrayRepresentation │ └── SegmentTreeTest.java ├── stack └── StackTest.java └── trie └── TrieTest.java /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | 10 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Gradle 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Set up JDK 11 20 | uses: actions/setup-java@v3 21 | with: 22 | java-version: '11' 23 | distribution: 'temurin' 24 | - name: Build with Gradle 25 | uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0 26 | with: 27 | arguments: build --no-daemon 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.class 3 | *.swp 4 | 5 | # User-custom config files/folders 6 | .idea/ 7 | 8 | # Ignore Gradle project-specific cache directory 9 | .gradle 10 | 11 | # Ignore Gradle build output directory 12 | build/ 13 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id "com.adarshr.test-logger" version "3.2.0" 4 | id "checkstyle" 5 | } 6 | 7 | sourceSets.main.java.srcDirs += ['scripts'] 8 | 9 | checkstyle { 10 | toolVersion = '10.2' 11 | } 12 | 13 | repositories { 14 | mavenCentral() 15 | } 16 | 17 | dependencies { 18 | // Use JUnit test framework. 19 | testImplementation 'junit:junit:4.13.2' 20 | } 21 | 22 | java { 23 | toolchain { 24 | languageVersion = JavaLanguageVersion.of(11) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /config/checkstyle/suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/assets/images/(2,4)tree.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/(2,4)tree.jpg -------------------------------------------------------------------------------- /docs/assets/images/(a,b)childpolicy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/(a,b)childpolicy.jpg -------------------------------------------------------------------------------- /docs/assets/images/1DORS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/1DORS.jpg -------------------------------------------------------------------------------- /docs/assets/images/1DORSDynamicUpdates.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/1DORSDynamicUpdates.jpg -------------------------------------------------------------------------------- /docs/assets/images/1DORSQuery.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/1DORSQuery.jpeg -------------------------------------------------------------------------------- /docs/assets/images/2DORS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/2DORS.jpg -------------------------------------------------------------------------------- /docs/assets/images/2DORSQuery.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/2DORSQuery.jpg -------------------------------------------------------------------------------- /docs/assets/images/2DORSTrees.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/2DORSTrees.jpg -------------------------------------------------------------------------------- /docs/assets/images/AvlTree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/AvlTree.png -------------------------------------------------------------------------------- /docs/assets/images/BalancedProof.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/BalancedProof.png -------------------------------------------------------------------------------- /docs/assets/images/BinarySearch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/BinarySearch.png -------------------------------------------------------------------------------- /docs/assets/images/BinarySearchTemplated1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/BinarySearchTemplated1.jpeg -------------------------------------------------------------------------------- /docs/assets/images/BinarySearchTemplated2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/BinarySearchTemplated2.jpeg -------------------------------------------------------------------------------- /docs/assets/images/BinarySearchTemplated3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/BinarySearchTemplated3.jpeg -------------------------------------------------------------------------------- /docs/assets/images/BubbleSort.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/BubbleSort.jpeg -------------------------------------------------------------------------------- /docs/assets/images/CountingSort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/CountingSort.png -------------------------------------------------------------------------------- /docs/assets/images/InsertionSort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/InsertionSort.png -------------------------------------------------------------------------------- /docs/assets/images/LectureHoares.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/LectureHoares.jpeg -------------------------------------------------------------------------------- /docs/assets/images/Lomutos.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/Lomutos.jpeg -------------------------------------------------------------------------------- /docs/assets/images/MSTProperty3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/MSTProperty3.png -------------------------------------------------------------------------------- /docs/assets/images/MSTProperty4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/MSTProperty4.png -------------------------------------------------------------------------------- /docs/assets/images/MergeSortIterative.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/MergeSortIterative.jpg -------------------------------------------------------------------------------- /docs/assets/images/MergeSortRecursive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/MergeSortRecursive.png -------------------------------------------------------------------------------- /docs/assets/images/ParanoidQuickSort.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/ParanoidQuickSort.jpeg -------------------------------------------------------------------------------- /docs/assets/images/PathCompression.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/PathCompression.png -------------------------------------------------------------------------------- /docs/assets/images/QuickFind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/QuickFind.png -------------------------------------------------------------------------------- /docs/assets/images/QuickSortFirstPivot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/QuickSortFirstPivot.png -------------------------------------------------------------------------------- /docs/assets/images/QuickSortOverview.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/QuickSortOverview.jpeg -------------------------------------------------------------------------------- /docs/assets/images/QuickUnion1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/QuickUnion1.png -------------------------------------------------------------------------------- /docs/assets/images/QuickUnion2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/QuickUnion2.png -------------------------------------------------------------------------------- /docs/assets/images/RadixSort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/RadixSort.png -------------------------------------------------------------------------------- /docs/assets/images/SegmentTree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/SegmentTree.png -------------------------------------------------------------------------------- /docs/assets/images/SelectionSort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/SelectionSort.png -------------------------------------------------------------------------------- /docs/assets/images/ThreeWayPartitioning.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/ThreeWayPartitioning.jpeg -------------------------------------------------------------------------------- /docs/assets/images/TreeRotation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/TreeRotation.png -------------------------------------------------------------------------------- /docs/assets/images/Trie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/Trie.png -------------------------------------------------------------------------------- /docs/assets/images/UsualHoares.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/UsualHoares.jpeg -------------------------------------------------------------------------------- /docs/assets/images/WeightedUnion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/WeightedUnion.png -------------------------------------------------------------------------------- /docs/assets/images/WeightedUnionLeetCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/WeightedUnionLeetCode.png -------------------------------------------------------------------------------- /docs/assets/images/btreesplitchild.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/btreesplitchild.jpeg -------------------------------------------------------------------------------- /docs/assets/images/firstBadVersion.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/firstBadVersion.jpeg -------------------------------------------------------------------------------- /docs/assets/images/kmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/kmp.png -------------------------------------------------------------------------------- /docs/assets/images/max_heap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/docs/assets/images/max_heap.png -------------------------------------------------------------------------------- /docs/team/profiles.md: -------------------------------------------------------------------------------- 1 | # Team Profile 2 | 3 | | Name | Description/About | Website (LinkedIn/GitHub/Personal) | Contributions | 4 | |-----------|-------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|-------------------------------------------------------------| 5 | | Andre | Aspiring ML engineer. Developing this with wonderful ex-students. | You can find me [here](https://4ndrelim.github.io) | Team lead | 6 | | Kai Ting | Likes algorithms and a committed TA! | [Linkedin](https://www.linkedin.com/in/kai-ting-ho-425181268/) | Cool sorting and obscure trees! B-Trees, ORS.. | 7 | | Changxian | DevOps is right up his alley! | ... | Hashing variants! BTS DevOps - configure Gradle & workflows | 8 | | Shu Heng | Interested in ML, aspiring researcher. | No website but here's my [Linkedin](https://www.linkedin.com/in/yeoshuheng), please give me a job :< | CS Fundamentals! Stacks and queues! RB-tree. | 9 | | Junneng | Aspiring tech entrepreneur. | [LinkedIn](https://www.linkedin.com/in/soo-jun-neng/) | Binary Search variants, Minimum Spanning Trees! | 10 | | Amadeus | ... | ... | Graphs! | 11 | | Owen | ... | ... | Graphs and confusing mazes | 12 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4ndrelim/data-structures-and-algorithms/58a4622b726d14b39cff7ec890495997a1016e4a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # Running On Custom Inputs 2 | 3 | This folder allows you to run the algorithm/structures on any custom input you pass in. 4 | 5 | Simply create a file of the similar structure as in the src/ folder or import the respective package to call the 6 | algorithm/structure in the `main` method of the file you create. Then, run the file via IntelliJ as per normal. 7 | 8 | Some samples are shown. 9 | -------------------------------------------------------------------------------- /scripts/algorithms/patternFinding/RunKMP.java: -------------------------------------------------------------------------------- 1 | package algorithms.patternFinding; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Runs the KMP algorithm. 7 | */ 8 | public class RunKMP { 9 | //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\ 10 | ////////////////////////////////////////// This section is for user input \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 11 | private static String seq = "abclaabcabcabc"; 12 | private static String pattern = "abc"; 13 | 14 | //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\ 15 | 16 | public static void main(String[] args) { 17 | List indices = KMP.findOccurrences(seq, pattern); 18 | display(indices); 19 | prettyPrint(seq, pattern, indices); 20 | } 21 | 22 | /** 23 | * Prints the string representation of the List. 24 | * @param arr the given List. 25 | */ 26 | public static void display(List arr) { 27 | StringBuilder toDisplay = new StringBuilder("["); 28 | for (int num : arr) { 29 | toDisplay.append(String.format("%d ", num)); 30 | } 31 | toDisplay = toDisplay.replace(toDisplay.length() - 1, toDisplay.length(), "]"); 32 | System.out.println(toDisplay); 33 | } 34 | 35 | /** 36 | * Pretty prints the sequence and pattern. 37 | * @param seq the given string sequence. 38 | * @param pattern the given string pattern. 39 | * @param indices TODO. 40 | */ 41 | public static void prettyPrint(String seq, String pattern, List indices) { 42 | StringBuilder prettySeq = new StringBuilder(" Sequence: "); 43 | StringBuilder prettyPtr = new StringBuilder("Start Pts: "); 44 | int currPos = 0; 45 | for (int i = 0; i < seq.length(); i++) { 46 | char curr = seq.charAt(i); 47 | prettySeq.append(String.format("%c ", curr)); 48 | 49 | if (currPos < indices.size()) { 50 | int currIdx = indices.get(currPos); 51 | if (currIdx == i) { 52 | prettyPtr.append("^ "); 53 | currPos++; 54 | } else { 55 | prettyPtr.append(" "); 56 | } 57 | } 58 | } 59 | System.out.println(" Pattern: " + pattern); 60 | System.out.println(prettySeq); 61 | System.out.println(prettyPtr); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /scripts/algorithms/sorting/countingSort/RunCountingSort.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.countingSort; 2 | 3 | /** 4 | * Script to run Counting Sort. 5 | */ 6 | public class RunCountingSort { 7 | 8 | //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// 9 | ////////////////////////////////////////// This section is for user input \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 10 | private static int[] toSort = 11 | new int[] {3, 4, 2, 65, 76, 93, 22, 1, 5, 7, 88, 54, 44, 7, 5, 6, 2, 64, 43, 22, 27, 33, 59, 64, 76, 99, 37, 7}; 12 | 13 | //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// 14 | 15 | public static void main(String[] args) { 16 | int[] sorted = CountingSort.sort(toSort); 17 | display(sorted); 18 | } 19 | 20 | /** 21 | * Prints the string representation of the array. 22 | * 23 | * @param arr the given array. 24 | */ 25 | public static void display(int[] arr) { 26 | StringBuilder toDisplay = new StringBuilder("["); 27 | for (int num : arr) { 28 | toDisplay.append(String.format("%d ", num)); 29 | } 30 | toDisplay = toDisplay.replace(toDisplay.length() - 1, toDisplay.length(), "]"); 31 | System.out.println(toDisplay); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /scripts/algorithms/sorting/insertionSort/RunInsertionSort.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.insertionSort; 2 | 3 | /** 4 | * Script to run Insertion Sort. 5 | */ 6 | public class RunInsertionSort { 7 | 8 | //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// 9 | ////////////////////////////////////////// This section is for user input \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 10 | private static int[] toSort = 11 | new int[] {3, 4, 2, 65, 76, 93, 22, 1, 5, 7, 88, 54, 44, 7, 5, 6, 2, 64, 43, 22, 27, 33, 59, 64, 76, 99, 37, 7}; 12 | 13 | //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// 14 | 15 | public static void main(String[] args) { 16 | int[] sorted = InsertionSort.sort(toSort); 17 | display(sorted); 18 | } 19 | 20 | /** 21 | * Prints the string representation of the array. 22 | * 23 | * @param arr the given array. 24 | */ 25 | public static void display(int[] arr) { 26 | StringBuilder toDisplay = new StringBuilder("["); 27 | for (int num : arr) { 28 | toDisplay.append(String.format("%d ", num)); 29 | } 30 | toDisplay = toDisplay.replace(toDisplay.length() - 1, toDisplay.length(), "]"); 31 | System.out.println(toDisplay); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.3/userguide/building_swift_projects.html in the Gradle documentation. 6 | */ 7 | 8 | rootProject.name = 'data-structures-and-algorithms' 9 | -------------------------------------------------------------------------------- /src/main/java/algorithms/binarySearch/README.md: -------------------------------------------------------------------------------- 1 | # Binary Search 2 | 3 | ## Background 4 | 5 | Binary search is a search algorithm that finds the position of a target value within a sorted array or list. It compares 6 | the target value to the middle element of the search range, then, based on the comparison, narrows the search to the 7 | upper or lower half of the current search range. 8 | 9 | ## Implementation Invariant 10 | 11 | At the end of each iteration, the target value is either within the search range or does not exist in the search space. 12 | 13 | ## BinarySearch and BinarySearchTemplate 14 | 15 | We will discuss more implementation-specific details and complexity analysis in the respective folders. In short, 16 | 1. The [binarySearch](binarySearch) method is a more straightforward and intuitive version of the binary search 17 | algorithm. 18 | 2. The [binarySearchTemplate](binarySearchTemplated) method provides a more generalised template that can be used for 19 | most binary search problems by introducing a condition method that can be modified based on the requirements of the 20 | implementation. 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/java/algorithms/binarySearch/binarySearch/BinarySearch.java: -------------------------------------------------------------------------------- 1 | package algorithms.binarySearch.binarySearch; 2 | 3 | /** 4 | * Here, we are implementing BinarySearch where we search an array for a target value at O(log n) time complexity. 5 | *

6 | * Assumptions: 7 | * The array is sorted in ascending order. 8 | * All elements in the array are unique. (to allow for easy testing) 9 | *

10 | * Brief Description and Implementation Invariant: 11 | * 12 | * Brief Description: 13 | * With the assumption that the array is sorted in ascending order, BinarySearch reduces the search range by half or 14 | * half + 1 (due to floor division) after every loop. This is done by reassigning the max (high) or min (low) of the 15 | * search range to the middle of the search range when the target value is smaller than or larger than the current 16 | * middle value respectively, and continuing the search thereafter. 17 | *

18 | * In both scenarios where the target is not equal to arr[mid], the high and low pointers are reassigned mid decremented 19 | * /incremented by 1. This ensures that there will never be an infinite loop as the search range will no longer include 20 | * the mid-value, causing the search range to constantly "shrink". We know we can decrement/increment mid by 1 during 21 | * the reassignment as the mid-value is definitely not the target and should no longer be in the search range. 22 | *

23 | * At the end of every loop, we know that the target value is either within the search range or does not exist in the 24 | * array, thereby ensuring the correctness of the algorithm. 25 | *

26 | * Since after each iteration, the search range is halved, it will only take a maximum of O(log n) times before the 27 | * target is either found or determined to not exist in the array. 28 | */ 29 | public class BinarySearch { 30 | /** 31 | * Searches for a target value within a sorted array using the binary search algorithm. 32 | * 33 | * @param arr the sorted array in which the search is to be performed. 34 | * @param target the value to be searched for. 35 | * @return the index of the target if found, otherwise -1. 36 | */ 37 | public static int search(int[] arr, int target) { 38 | int high = arr.length - 1; // max index is 3 if array length is 4 39 | int low = 0; 40 | while (low <= high) { // When low == high, arr[low] can still == target, therefore should still check 41 | int mid = low + (high - low) / 2; // equivalent to high + low / 2 but reduces cases of integer overflow 42 | if (arr[mid] > target) { 43 | high = mid - 1; // -1 since current mid is not == target and should not be in the search range anymore 44 | } else if (arr[mid] < target) { 45 | low = mid + 1; // +1 since current mid is not == target and should not be in the search range anymore 46 | } else { 47 | return mid; 48 | } 49 | } 50 | 51 | return -1; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/algorithms/binarySearch/binarySearch/README.md: -------------------------------------------------------------------------------- 1 | # BinarySearch 2 | 3 | ## Background 4 | 5 | BinarySearch is a more straightforward and intuitive version of the binary search algorithm. In this approach, after the 6 | mid-value is calculated, the high or low pointer is adjusted by just one unit. 7 | 8 | ### Illustration 9 | 10 | ![binary search img](../../../../../../docs/assets/images/BinarySearch.png) 11 | 12 | Image Source: GeeksforGeeks 13 | 14 | From the above example, after mid points to index 4 in the first search, the low pointer moves to index 5 (+1 from 4) 15 | when narrowing the search. Similarly, when mid points to index 7 in the second search, the high pointer shifts to index 16 | 6 (-1 from 7) when narrowing the search. This prevents any possibility of infinite loops. During the search, the moment 17 | mid-value is equal to the target value, the search ends prematurely. 18 | 19 | ## Complexity Analysis 20 | **Time**: 21 | - Worst case: O(log n) 22 | - Average case: O(log n) 23 | - Best case: O(1) 24 | 25 | In the worst case, the target is either in the first or last index or does not exist in the array at all. 26 | In the best case, the target is the middle (odd number of element) or the first middle element (even number of elements) 27 | if floor division is used to determine the middle. 28 | 29 | **Space**: O(1) 30 | 31 | Since no new data structures are used and searching is only done within the array given. -------------------------------------------------------------------------------- /src/main/java/algorithms/binarySearch/binarySearchTemplated/BinarySearchTemplated.java: -------------------------------------------------------------------------------- 1 | package algorithms.binarySearch.binarySearchTemplated; 2 | 3 | /** 4 | * Here, we are implementing BinarySearchTemplated where we search an array for a target value at O(log n) time 5 | * complexity. 6 | *

7 | * Assumptions: 8 | * The array is sorted in ascending order. 9 | * All elements in the array are unique. (to allow for easy testing) 10 | *

11 | * Brief Description and Implementation Invariant: 12 | * With the assumption that the array is sorted in ascending order, BinarySearchTemplated reduces the search range by 13 | * half or half + 1 (due to floor division) after every loop. This is done by reassigning the max (high) or min (low) of 14 | * the search range to the middle of the search range when the target value is smaller than or larger than the current 15 | * middle value respectively, and continuing the search thereafter. 16 | *

17 | * In this version, there is no condition to check if the current mid is equal to the target to prematurely end the 18 | * search. Hence, the only way for the loop to complete is when low exceeds high. 19 | *

20 | * At the end of every loop, we know that the target value is either within the search range or does not exist in the 21 | * array, thereby ensuring the correctness of the algorithm. 22 | *

23 | * Since after each iteration, the search range is halved, it will only take a maximum of O(log n) times before the 24 | * target is either found or determined to not exist in the array. 25 | */ 26 | public class BinarySearchTemplated { 27 | /** 28 | * A utility method to compare two integers. 29 | * 30 | * @param value The current value from the array. 31 | * @param target The value being searched for. 32 | * @return true if the current value is less than the target, otherwise false. 33 | */ 34 | // The condition should be changed accordingly 35 | public static boolean condition(int value, int target) { 36 | return value >= target; 37 | } 38 | 39 | /** 40 | * Conducts a binary search to find the target within the sorted array. 41 | * 42 | * @param arr The sorted input array. 43 | * @param target The value to be searched within the array. 44 | * @return The index of the target value if found, otherwise -1. 45 | */ 46 | public static int search(int[] arr, int target) { 47 | // The search space i.e. high and low should be changed accordingly. 48 | int high = arr.length - 1; // max index is 3 if array length is 4 49 | int low = 0; 50 | while (low < high) { 51 | int mid = low + (high - low) / 2; // equivalent to high + low / 2 but reduces cases of integer overflow 52 | if (condition(arr[mid], target)) { // if value >= target 53 | high = mid; 54 | } else { // if value < target 55 | low = mid + 1; 56 | } 57 | } 58 | 59 | // Checks if low value is indeed the target 60 | // Note that the following checks may not be required in other use cases of this template 61 | if (low < arr.length && arr[low] == target) { 62 | // returned value should be changed accordingly (low or low - 1) 63 | return low; 64 | } 65 | // Returns -1 if loop was exited without finding the target 66 | return -1; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/algorithms/minimumSpanningTree/README.md: -------------------------------------------------------------------------------- 1 | # Minimum Spanning Tree Algorithms 2 | 3 | ## Background 4 | 5 | Minimum Spanning Tree (MST) algorithms are used to find the minimum spanning tree of a weighted, connected graph. A 6 | spanning tree of a graph is a connected, acyclic subgraph that includes all the vertices of the original graph. An MST 7 | is a spanning tree with the minimum possible total edge weight. 8 | 9 | ### 4 Properties of MST 10 | 1. An MST should not have any cycles 11 | 2. If you cut an MST at any single edge, the two pieces will also be MSTs 12 | 3. **Cycle Property:** For every cycle, the maximum weight edge is not in the MST 13 | 14 | ![MST Property 3](../../../../../docs/assets/images/MSTProperty3.png) 15 | 16 | Image Source: CS2040S 22/23 Sem 2 Lecture Slides 17 | 18 | 4. **Cut Property:** For every partition of the nodes, the minimum weight edge across the cut is in the MST 19 | 20 | ![MST Property 4](../../../../../docs/assets/images/MSTProperty4.png) 21 | 22 | Image Source: CS2040S 22/23 Sem 2 Lecture Slides 23 | 24 | Note that the other edges across the partition may or may not be in the MST. 25 | 26 | ## Prim's Algorithm and Kruskal's Algorithm 27 | 28 | We will discuss more implementation-specific details and complexity analysis in the respective folders. In short, 29 | 1. [Prim's Algorithm](prim) is a greedy algorithm that finds the minimum spanning tree of a graph by starting from an 30 | arbitrary node (vertex) and adding the edge with the minimum weight that connects the current tree to a new node, adding 31 | the node to the current tree, until all nodes are included in the tree. 32 | <<<<<<< HEAD 33 | 2. [Kruskal's Algorithm](kruskal) is a greedy algorithm that finds the minimum spanning tree of a graph by sorting the 34 | edges by weight and adding the edge with the minimum weight that does not form a cycle into the current tree. 35 | 36 | ## Notes 37 | 38 | ### Difference between Minimum Spanning Tree and Shortest Path 39 | It is important to note that a Minimum Spanning Tree of a graph does not represent the shortest path between all the 40 | nodes. See below for an example: 41 | 42 | The below graph is a weighted, connected graph with 5 nodes and 6 edges: 43 | ![original graph img](../../../../../docs/assets/images/originalGraph.jpg) 44 | 45 | The following is the Minimum Spanning Tree of the above graph: 46 | ![MST img](../../../../../docs/assets/images/MST.jpg) 47 | 48 | Taking node A and D into consideration, the shortest path between them is A -> D, with a total weight of 4. 49 | ![SPOriginal img](../../../../../docs/assets/images/SPOriginal.jpg) 50 | 51 | However, the shortest path between A and D in the Minimum Spanning Tree is A -> C -> D, with a total weight of 5, which 52 | is not the shortest path in the original graph. 53 | ![SPMST img](../../../../../docs/assets/images/SPMST.jpg) 54 | -------------------------------------------------------------------------------- /src/main/java/algorithms/minimumSpanningTree/kruskal/README.md: -------------------------------------------------------------------------------- 1 | # Kruskal's Algorithm 2 | 3 | ## Background 4 | Kruskal's Algorithm is a greedy algorithm used to find the minimum spanning tree (MST) of a connected, weighted graph. 5 | It works by sorting all the edges in the graph by their weight in non-decreasing order and then adding the smallest edge 6 | to the MST, provided it does not form a cycle with the already included edges. This is repeated until all vertices are 7 | included in the MST. 8 | 9 | ## Implementation Details 10 | Kruskal's Algorithm uses a simple `ArrayList` to sort the edges by weight. 11 | 12 | A [`DisjointSet`](/dataStructures/disjointSet/weightedUnion) data structure is also used to keep track of the 13 | connectivity of vertices and detect cycles. 14 | 15 | ## Complexity Analysis 16 | 17 | **Time Complexity:** 18 | Sorting the edges by weight: O(E log E) = O(E log V), where V and E is the number of vertices and edges respectively. 19 | Union-Find operations: O(E α(V)), where α is the inverse Ackermann function. 20 | Overall complexity: O(E log V) 21 | 22 | **Space Complexity:** 23 | O(V + E) for the storage of vertices in the disjoint set and edges in the priority queue. -------------------------------------------------------------------------------- /src/main/java/algorithms/minimumSpanningTree/prim/README.md: -------------------------------------------------------------------------------- 1 | # Prim's Algorithm 2 | 3 | ## Background 4 | 5 | Prim's Algorithm is a greedy algorithm that finds the minimum spanning tree of a graph by starting from an 6 | arbitrary node (vertex) and adding the edge, with the minimum weight that connects the current tree to an unexplored 7 | node, and the unexplored node to the current tree, until all nodes are included in the tree. 8 | 9 | ### Implementation Details 10 | 11 | A `PriorityQueue` (binary heap) is utilised to keep track of the minimum weight edge that connects the current tree to 12 | an unexplored node. In an ideal scenario, the minimum weight edge to each node in the priority queue should be updated each 13 | time a lighter edge is found to maintain a single unique node in the priority queue. This means that a decrease key 14 | operation is required. 15 | 16 | **Decrease Key Operation:** 17 | 18 | However, we know that the decrease key operation of a binary heap implementation of a priority 19 | queue will take O(V) time, which will result in a larger time complexity for the entire algorithm compared to using only 20 | O(log V) operations for each edge. Hence, in our implementation, to avoid the use of a decrease key operation, we will simply insert duplicate nodes with 21 | their new minimum weight edge, which will take O(log E) = O(log V) given an upper bound of E = V^2, into the queue, 22 | while leaving the old node in the queue. Additionally, we will track if a node has already been added into the MST to 23 | avoid adding duplicate nodes. 24 | 25 | **Priority Queue Implementation:** 26 | 27 | Note that a priority queue is an abstract data type that can be implemented using different data structures. In this 28 | implementation, the default Java `PriorityQueue` is used, which is a binary heap. By implementing the priority queue 29 | with an AVL tree, a decrease key operation that has a time complexity of O(log V) can also be achieved. 30 | 31 | ## Complexity Analysis 32 | 33 | **Time Complexity:** 34 | - O(V^2 log V) for the basic version with an adjacency matrix, where V is the number of vertices. 35 | - O(E log V) with a binary heap and adjacency list, where V and E is the number of vertices and edges 36 | respectively. 37 | 38 | **Space Complexity:** 39 | - O(V^2) for the adjacency matrix representation. 40 | - O(V + E) for the adjacency list representation. 41 | 42 | ## Notes 43 | 44 | ### Difference between Prim's Algorithm and Dijkstra's Algorithm 45 | 46 | | | Prim's Algorithm | Dijkstra's Algorithm | 47 | |-------------------------------------|---------------------------------------------------------------------------------|----------------------------------------------------------| 48 | | Purpose | Finds MST - minimum sum of edge weights that includes all vertices in the graph | Finds shortest path from a single source to all vertices | 49 | | Property Compared in Priority Queue | Minimum weight of incoming edge to a vertex | Minimum distance from source vertex to current vertex | 50 | 51 | -------------------------------------------------------------------------------- /src/main/java/algorithms/orthogonalRangeSearching/RangeTreeNode.java: -------------------------------------------------------------------------------- 1 | package algorithms.orthogonalRangeSearching; 2 | 3 | /** 4 | * This class is the node class for building a range tree. 5 | * @param Generic type of the node class 6 | */ 7 | public class RangeTreeNode { 8 | private T val; 9 | private int height; 10 | private RangeTreeNode left = null; 11 | private RangeTreeNode right = null; 12 | private RangeTreeNode parent = null; 13 | private RangeTreeNode yTree = null; 14 | 15 | public RangeTreeNode(T val) { 16 | this.val = val; 17 | } 18 | 19 | /** 20 | * Constructor for range tree node 21 | * @param val value of node 22 | * @param left left child of node 23 | * @param right right child of node 24 | */ 25 | public RangeTreeNode(T val, RangeTreeNode left, RangeTreeNode right) { 26 | this.val = val; 27 | this.left = left; 28 | this.right = right; 29 | } 30 | 31 | public T getVal() { 32 | return this.val; 33 | } 34 | 35 | public int getHeight() { 36 | return this.height; 37 | } 38 | 39 | public RangeTreeNode getLeft() { 40 | return this.left; 41 | } 42 | 43 | public RangeTreeNode getRight() { 44 | return this.right; 45 | } 46 | 47 | public RangeTreeNode getParent() { 48 | return this.parent; 49 | } 50 | 51 | public RangeTreeNode getYTree() { 52 | return this.yTree; 53 | } 54 | 55 | public void setVal(T val) { 56 | this.val = val; 57 | } 58 | 59 | public void setLeft(RangeTreeNode left) { 60 | this.left = left; 61 | } 62 | 63 | public void setRight(RangeTreeNode right) { 64 | this.right = right; 65 | } 66 | 67 | public void setParent(RangeTreeNode parent) { 68 | this.parent = parent; 69 | } 70 | 71 | public void setHeight(int height) { 72 | this.height = height; 73 | } 74 | 75 | public void setYTree(RangeTreeNode yTree) { 76 | this.yTree = yTree; 77 | } 78 | 79 | @Override 80 | public boolean equals(Object other) { 81 | if (other == this) { 82 | return true; 83 | } 84 | if (!(other instanceof RangeTreeNode)) { 85 | return false; 86 | } 87 | RangeTreeNode node = (RangeTreeNode) other; 88 | return this.val == node.val; 89 | } 90 | 91 | @Override 92 | public String toString() { 93 | return String.valueOf(this.val); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/algorithms/orthogonalRangeSearching/oneDim/README.md: -------------------------------------------------------------------------------- 1 | # 1D Orthogonal Range Searching 2 | 3 | 1D orthogonal range searching is a computational problem where you search for elements or data points within a 4 | specified 1D range (interval) in a collection of 1D data (e.g. Find me everyone between ages 22 and 27). Additionally, 5 | we also want to support efficient insertions of new data points into the maintained set. 6 | 7 | One strategy would be to sort all the data points in O(nlogn) time, then insertion would take O(n). We can binary 8 | search the low and high of the specified range to return the start and end indices of all the data points within 9 | in O(logn) time. This would be a reasonable approach if the no. of queries >> no. of insertions. 10 | 11 | In cases where the no. of insertions >> no. of queries, we might want to further optimise the time complexity of 12 | insertions to O(logn) using a 1D range tree. 13 | 14 | Strategy: 15 | 1. Use a binary search tree 16 | 2. Store all points in the leaves of the tree (internal nodes only store copies) 17 | 3. Each internal node v stores the MAX of any leaf in the left sub-tree (**range tree property**) 18 | 19 | ![1DORS](../../../../../../docs/assets/images/1DORS.jpg) 20 | 21 | Say we want to find all the nodes between 10 and 50 i.e. query(10, 50). We would want to: 22 | 1. Find split node: highest node where search includes both left & right subtrees 23 | => we want to make use of the range tree property to perform binary search to find our split node. See findSplit(root, low, high) in code. 24 | 2. Left traversal & right traversal 25 | - Left traversal covers the range within [low, splitNode] 26 | - Right traversal covers the range within (splitNode, high] 27 | 28 | ![1DORSQuery](../../../../../../docs/assets/images/1DORSQuery.jpeg) 29 | Image Source: Lecture Slides 30 | 31 | ## Complexity Analysis 32 | **Time**: 33 | 34 | Build Tree (cost incurred once only): 35 | - if given an unsorted array, O(nlogn) limited by sorting step 36 | - if given a sorted array, O(n) 37 | 38 | Querying: O(k + logn) 39 | - Find Split Node: O(logn) (binary search) 40 | - Left Traversal: at every step, we either 41 | 1. output all-right subtree (O(k) where k is no. of leaves) and recurse left 42 | 2. recurse right (at most logn times) 43 | - Right Traversal: similar to left traversal 44 | 45 | **Space**: S(n) = S(n / 2) + O(n) => O(nlogn) 46 | 47 | ## Notes 48 | ### Dynamic Updates 49 | If we need to dynamically update the tree, insertion and deletion is done in a manner similar to AVL trees 50 | (insert/delete and rotate to maintain height balance), except now we need to ensure that we are adding the new node 51 | as a leaf node, and we still need to adhere to the range tree property. 52 | 53 | Note how the ORS tree property enables efficient dynamic updating of the tree, as the value of the nodes do not need 54 | to change after rotation. 55 | 56 | ![1DORSDynamicUpdates](../../../../../../docs/assets/images/1DORSDynamicUpdates.jpg) 57 | 58 | For more implementation details, refer to the code below "// Functions from here onwards are designed to support 59 | dynamic updates." 60 | -------------------------------------------------------------------------------- /src/main/java/algorithms/orthogonalRangeSearching/twoDim/README.md: -------------------------------------------------------------------------------- 1 | # 2D Orthogonal Range Searching 2 | 3 | 2D orthogonal range searching is a computational problem that involves efficiently answering queries about points 4 | in a two-dimensional space that fall within a specified axis-aligned rectangular range (orthogonal range). In other 5 | words, given a set of points in 2D, find me all the points that lie within a specific rectangle. 6 | 7 | To do so, we can extend the idea of 1D range trees to the 2D context. 8 | 9 | Strategy 10 | - Each node in the x-tree has a set of points in its subtree 11 | - Store a y-tree at each x-node containing all the points in the x-subtree 12 | 13 | ![2DORS](../../../../../../docs/assets/images/2DORS.jpg) 14 | 15 | 1. Build an x-tree using only x-coordinates. 16 | - This should be done in the exact same way as you would for a 1D range tree. 17 | 2. For every node in the x-tree, build a y-tree out of nodes in the x-subtree using only y-coordinates. 18 | 19 | ![2DORSTrees](../../../../../../docs/assets/images/2DORSTrees.jpg) 20 | 21 | Given the 2D range tree, we now want to query the points in a given rectangle 22 | i.e. search(tree, xLow, xHigh, yLow, yHigh). 23 | 24 | We first want to find the points that will satisfy the x-condition i.e. find me all points whose x-coordinates are 25 | between xLow and xHigh. To do so, we first need to find our split node in the x-tree, by performing binary search[^1] 26 | while traversing the x-tree - similar to how we found the split node in a 1D range tree. This will give us our X-split. 27 | 28 | Now given our X-split, we want to find points that satisfy both our x-condition and y-condition. 29 | 30 | Let's first explore our x-left subtree (X-left traversal). If (xLow <= x-coordinate of curr node), we are confident 31 | that the entire right subtree of the curr node satisfies our x-condition (because of BST property). 32 | 33 | Now we want to perform a Y-search to find the points that also satisfy the y-condition. To do so, we first need to 34 | find our split node in the y-tree i.e. Y-split. Notice that the problem is now reduced to a 1D ORS problem. Once we 35 | have found Y-split, we can simply perform left traversal and right traversal from Y-split, similar to how it is done 36 | in 1D ORS. 37 | 38 | A similar symmetric logic applies in exploring our x-right subtree. 39 | 40 | ![2DORSQuery](../../../../../../docs/assets/images/2DORSQuery.jpg) 41 | Image Source: https://www.cse.wustl.edu/~taoju/cse546/lectures/Lecture21_rangequery_2d.pdf 42 | 43 | ## Complexity Analysis 44 | **Time**: 45 | 46 | Build Tree (cost incurred once only): O(nlog^2n) 47 | 48 | Querying: O(k + log^2n) 49 | - O(logn) to find X-split 50 | - O(logn) recursing steps in X-left traversal/X-right-traversal 51 | - O(logn) y-tree searches of cost O(logn) => overall: O(log^2n) 52 | - O(k) enumerating output 53 | 54 | **Space**: O(nlogn) 55 | - Each point appears in at most 1 y-tree per level. (Refer to 1st image for visualisation) 56 | - There are O(logn) levels in the x-tree. 57 | - Therefore, each node appears in at most O(logn) y-trees. => overall: O(nlogn) 58 | - The x-tree takes O(n) space. 59 | 60 | ## Notes 61 | ### Dynamic Updates 62 | Unlike 1D range trees, dynamic updates in a 2D range tree are inefficient as rotations in the x-tree might involve 63 | entirely rebuilding the y-trees for the rotated notes. Therefore, 2D ORS is mainly used for static querying. 64 | 65 | ### d-dimensional Range Trees 66 | - Query cost: O(k + log^dn) 67 | - Build tree cost: O(nlog^(d-1)n) 68 | - Space: O(nlog^(d-1)n) 69 | 70 | [^1] This reference to binary search differs from our typical binary search formulation, but essentially refers 71 | to the findSplit function where you walk down the balanced tree and remove roughly half of the nodes each time. -------------------------------------------------------------------------------- /src/main/java/algorithms/patternFinding/README.md: -------------------------------------------------------------------------------- 1 | # Knuth-Moris-Pratt Algorithm 2 | 3 | ## Background 4 | KMP match is a type of pattern-searching algorithm that improves the efficiency of naive search by avoiding unnecessary 5 | comparisons. It is most notable when the pattern has repeating sub-patterns. 6 |
7 | Pattern-searching problems is prevalent across many fields of CS, for instance, 8 | in text editors when searching for a pattern, in computational biology sequence matching problems, 9 | in NLP problems, and even for looking for file patterns for effective file management. 10 | It is hence crucial that we develop an efficient algorithm. 11 | 12 | Typically, the algorithm returns a list of indices that denote the start of each occurrence of the pattern string. 13 | 14 | ![KMP](../../../../../docs/assets/images/kmp.png) 15 | Image Source: GeeksforGeeks 16 | 17 | ### Intuition 18 | It's efficient because it utilizes the information gained from previous character comparisons. When a mismatch occurs, 19 | the algorithm uses this information to skip over as many characters as possible. 20 | 21 | Considering the string pattern: 22 | 23 | Pattern:| X | Y | X | Y | C | X | Y | X | Y | F | 24 | |-------|---|---|---|---|---|---|---|---|---|---| 25 | Position:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 26 | 27 | and string: 28 | 29 | String: | X | Y | X | Y | C | X | Y | X | Y | C | X | Y | X | Y | F | G | A | B | C | 30 | |--------|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| 31 | Position:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 32 | 33 | KMP has, during its initial processing of the pattern, identified that "XYXY" is a repeating sub-pattern. 34 | This means when the mismatch at F (10th character of the pattern) and C (10th character of the string) occurs, 35 | KMP doesn't need to start matching again from the very beginning of the pattern.
36 | Instead, it leverages the information that "XYXY" has already been matched. 37 | 38 | Therefore, the algorithm continues matching from the 5th character of the pattern string. 39 | 40 | This is the key idea behind KMP algorithm and is applied throughout the string. 41 | How it knows to 'reuse' previously identified sub-patterns is by keeping 42 | track of what is known as the Longest Prefix Suffix (LPS; Longest prefix that is also a suffix) Table. Look at the 43 | implementation for a step-by-step explanation for how LPS table is generated. 44 | 45 | Pattern: | | X | Y | X | Y | C | X | Y | X | Y | F | 46 | |--------|---|---|---|---|---|---|---|---|---|---|---| 47 | Position:| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 48 | LPS: | -1| 0 | 0 | 1 | 2 | 0 | 1 | 2 | 3 | 4 | 0 | 49 | 50 | ## Complexity Analysis 51 | Let k be the length of the pattern and n be the length of the string to match against. 52 | 53 | Naively, we can look for patterns in a given sequence in O(nk) where n is the length of the sequence and k 54 | is the length of the pattern. We do this by iterating every character of the sequence, and look at the 55 | immediate k-1 characters that come after it. This is not a big issue if k is known to be small, but there's 56 | no guarantee this is the case. 57 | 58 | **Time complexity**: O(n+k) 59 | 60 | KMP does this in O(n+k) by making use of previously identified sub-patterns. It identifies sub-patterns 61 | by first processing the pattern input in O(k) time, allowing identification of patterns in 62 | O(n) traversal of the sequence. More details found in the src code. 63 | 64 | **Space complexity**: O(k) auxiliary space to store suffix that matches with prefix of the pattern string 65 | 66 | ## Notes 67 | 1. A detailed illustration of how the algorithm works is shown in the code. 68 | But if you have trouble understanding the implementation, 69 | here is a good [video](https://www.youtube.com/watch?v=EL4ZbRF587g) as well. 70 | 2. A subroutine to find Longest Prefix Suffix (LPS) is commonly involved in the preprocessing step of KMP. 71 | It may be useful to interpret these numbers as the number of characters matched between the suffix and prefix.
72 | Knowing the number of characters of prefix would help in informing the position of the next character of the pattern to 73 | match. 74 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/bubbleSort/BubbleSort.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.bubbleSort; 2 | 3 | /** 4 | * Here, we are implementing BubbleSort where we sort the array in increasing (or more precisely, non-decreasing) 5 | * order. Below are some details specific to this implementation. 6 | *

7 | * Early Termination: 8 | * At the end of the (n-1)th iteration of the outer loop, where n is the length of the array, the largest (n-1) 9 | * elements are correctly sorted at the final (n-1) positions of the array, leaving the last 1 element placed correctly 10 | * in the first position of the array. Therefore, (n-1) iterations of the outer loop is sufficient. 11 | *

12 | * Slight optimisation: 13 | * At the kth iteration of the outer loop, we only require (n-k) adjacent comparisons to get the kth-largest element 14 | * to its correct position. 15 | */ 16 | public class BubbleSort { 17 | /** 18 | * Sorts the given array in-place in non-decreasing order. 19 | * 20 | * @param arr array to be sorted. 21 | * @return the same array arr that is sorted. 22 | */ 23 | public static int[] sort(int[] arr) { 24 | int n = arr.length; 25 | boolean swapped; // tracks of the presence of swaps within one iteration of the outer loop to 26 | // facilitate early termination 27 | for (int i = 0; i < n - 1; i++) { // outer loop which supports the invariant; n-1 suffice 28 | swapped = false; 29 | for (int j = 0; j < n - 1 - i; j++) { // inner loop that does the adjacent comparisons 30 | if (arr[j] > arr[j + 1]) { // if we changed this to <, we will sort the array in non-increasing order 31 | int temp = arr[j]; 32 | arr[j] = arr[j + 1]; 33 | arr[j + 1] = temp; 34 | swapped = true; 35 | } 36 | } 37 | if (!swapped) { 38 | break; 39 | } 40 | } 41 | return arr; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/bubbleSort/README.md: -------------------------------------------------------------------------------- 1 | # Bubble Sort 2 | 3 | ## Background 4 | 5 | Bubble sort is one of the more intuitive comparison-based sorting algorithms. 6 | It makes repeated comparisons between neighbouring elements, 'bubbling' (side-by-side swaps) 7 | largest (or smallest) element in the unsorted region to the sorted region (often the front or the back). 8 | 9 | ![bubble sort img](../../../../../../docs/assets/images/BubbleSort.jpeg) 10 | 11 | ### Implementation Invariant 12 | **After the kth iteration, the biggest k items are correctly sorted at the final k positions of the array**. 13 | 14 | The job of the kth iteration of the outer loop is to bubble the kth-largest element to the kth position of the array 15 | from the right (i.e. its correct position). 16 | This is done through repeatedly comparing adjacent elements and swapping them if they are in the wrong order. 17 | 18 | ## Complexity Analysis 19 | 20 | **Time**: 21 | 22 | - Worst case (reverse sorted array): O(n^2) 23 | - Average case: O(n^2) 24 | - Best case (sorted array): O(n) 25 | 26 | In the worst case, during each iteration of the outer loop, the number of adjacent comparisons is upper-bounded 27 | by n. Since BubbleSort requires (n-1) iterations of the outer loop to sort the entire array, the total number 28 | of comparisons performed can be upper-bounded by (n-1) * n ≈ n^2. 29 | 30 | This implementation of BubbleSort terminates the outer loop once there are no swaps within one iteration of the 31 | outer loop. This improves the best case time complexity to O(n) for an already sorted array. 32 | 33 | **Space**: O(1) since sorting is done in-place 34 | 35 | ## Notes -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/countingSort/CountingSort.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.countingSort; 2 | 3 | /** 4 | *

Stable implementation of Counting Sort. 5 | * This non-comparison-based sorting algorithm is efficient for sorting integers with a small range. 6 | * For detailed explanation and complexity analysis, see README. 7 | */ 8 | public class CountingSort { 9 | /** 10 | * Sorts the given array. 11 | * 12 | * @param arr array to be sorted. 13 | * @return new array that is sorted. 14 | */ 15 | public static int[] sort(int[] arr) { 16 | int k = 0; 17 | int n = arr.length; 18 | // Find the max. value in arr. This tells us the size of the freq map array. 19 | for (int i = 0; i < n; i++) { 20 | k = Math.max(k, arr[i]); 21 | } 22 | // Obtain frequency map 23 | int[] freq = new int[k + 1]; 24 | for (int num : arr) { 25 | freq[num]++; 26 | } 27 | // Obtain prefix sum of freq map 28 | for (int i = 1; i < k + 1; i++) { 29 | freq[i] += freq[i - 1]; 30 | } 31 | // Sort the array by placing element in the output array 32 | int[] sorted = new int[n]; 33 | for (int i = arr.length - 1; i >= 0; i--) { // we start from the back to maintain stable property 34 | int num = arr[i]; 35 | sorted[freq[num] - 1] = num; // freq[num]-1 because 0-indexed 36 | freq[num]--; 37 | } 38 | return sorted; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/countingSort/README.md: -------------------------------------------------------------------------------- 1 | # Counting Sort 2 | 3 | ## Background 4 | 5 | Counting sort is a non-comparison-based sorting algorithm and isn't bounded by the O(nlogn) lower-bound 6 | of most sorting algorithms.
7 | It first obtains the frequency map of all elements (i.e. counting the occurrence of every element), then 8 | computes the prefix sum for the map. This prefix map tells us which position an element should be inserted. 9 | It is updated after each insertion to reflect the new position to insert the next time the same element is 10 | encountered.
11 | 12 | ![counting sort img](../../../../../../docs/assets/images/CountingSort.png) 13 | 14 | Image Source: https://www.oreilly.com/library/view/mastering-algorithms-with/1565924533/ch12s13.html 15 | 16 | _To align with the naming convention of our implementation, data => arr, counts => freq, temp => sorted._ 17 | 18 | ### Implementation Invariant 19 | 20 | **At the end of the ith iteration, the ith element (of the original array) from the back will be placed in 21 | its correct position**. 22 | 23 | Note: An alternative implementation from the front is easily done with minor modification. 24 | The catch is that this implementation would not be stable. 25 | 26 | ### Common Misconception 27 | 28 | _"Counting sort does not require total ordering of elements since it is non-comparison based."_ 29 | 30 | This is incorrect. It requires total ordering of elements to determine their relative positions in the sorted output. 31 | In our implementation, the total ordering property is reflected by virtue of the structure of the frequency map. 32 | 33 | ## Complexity Analysis 34 | 35 | **Time**: O(k+n)=O(max(k,n))
36 | **Space**: O(k+n)=O(max(k,n))
37 | where k is the value of the largest element and n is the number of elements. 38 | 39 | Counting sort is most efficient if the range of input values do not exceed the number of input values.
40 | Counting sort is NOT AN IN-PLACE algorithm. For one, it requires additional space to store freq map.
41 | 42 | ## Notes 43 | 44 | 1. Our counting sort implementation works only on non-negative integers. However, to adapt it to work for arrays with 45 | negative integers, we can add an offset of +m, where m is the smallest integer in the array. 46 | 2. Counting sort (stable version) is often used as a sub-routine for radix sort. 47 | 3. Supplementary: Here is a [video](https://www.youtube.com/watch?v=OKd534EWcdk) if you are still having troubles. 48 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/cyclicSort/README.md: -------------------------------------------------------------------------------- 1 | # Cyclic Sort 2 | 3 | ## Background 4 | 5 | Cyclic sort is a comparison-based, in-place algorithm that performs sorting (generally) in O(n^2) time. 6 | Under some special conditions (discussed later), the algorithm is non-comparison based and 7 | the best case could be done in O(n) time. This is the version that tends to be used in practice. 8 | 9 | ### Implementation Invariant 10 | 11 | **At the end of the kth iteration, the smallest (largest) i items are correctly sorted 12 | in the first (last) i positions of the array**. 13 | 14 | ### Comparison to Selection Sort 15 | 16 | Its invariant is quite similar as selection sort's. But they differ in maintaining this invariant. 17 | The algorithm for cyclic sort does a bit more than selection sort's. 18 | In the process of trying to find the correct element for the ith position, any element that was 19 | encountered will be correctly placed in their positions as well. 20 | 21 | This allows cyclic sort to have a time complexity of O(n) for certain inputs. 22 | (where the relative ordering of the elements is already known). This is discussed in the [**simple**](./simple) case. 23 | 24 | ## Simple and Generalised Case 25 | 26 | We discuss more implementation-specific details and complexity analysis in the respective folders. In short, 27 | 28 | 1. The [**simple**](./simple) case discusses the **non-comparison based** implementation of cyclic sort under 29 | certain conditions. This allows the best case to be better than O(n^2). 30 | 2. The [**generalised**](./generalised) case discusses cyclic sort for general inputs. This is comparison-based and is 31 | typically implemented in O(n^2). 32 | 33 | Note that, in practice, the generalised case is hardly used. There are more efficient algorithms to use for sorting, 34 | e.g. merge and quick sort. If the concern is the number of swaps, generalized cyclic sort does indeed require fewer 35 | swaps, but likely won't lower than selection sort's. 36 | 37 | In other words, **cyclic sort is specially designed for situations where the elements to be sorted are 38 | known to fall within a specific, continuous range, such as integers from 1 to n, without any gaps or duplicates.** 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/cyclicSort/generalised/CyclicSort.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.cyclicSort.generalised; 2 | /** 3 | * Implementation of cyclic sort in the generalised case where the input can contain any integer and duplicates. 4 | */ 5 | public class CyclicSort { 6 | /** 7 | * Sorts the given array using CyclicSort. Generalised case where the input array can contain any integer, and 8 | * can contain duplicates. 9 | * See documentation in README for more details. 10 | * 11 | * @param arr the given array to be sorted. Can contain any integers and can contain duplicates. 12 | * @param n the size of the given array to be sorted. 13 | */ 14 | public static void cyclicSort(int[] arr, int n) { 15 | for (int currIdx = 0; currIdx < n - 1; currIdx++) { 16 | int currElement; 17 | int rightfulPos; 18 | 19 | do { 20 | rightfulPos = currIdx; // initialization since elements before currIdx are correctly placed 21 | currElement = arr[currIdx]; 22 | for (int i = currIdx + 1; i < n; i++) { // O(n) find rightfulPos for the currElement 23 | if (arr[i] < currElement) { 24 | rightfulPos++; 25 | } 26 | } 27 | if (rightfulPos == currIdx) { // verified curr position is correct for curr element 28 | break; 29 | } 30 | while (currElement == arr[rightfulPos]) { // duplicates found, so find next suitable position 31 | rightfulPos++; 32 | } 33 | int tmp = currElement; // swap, put item in its rightful position 34 | arr[currIdx] = arr[rightfulPos]; 35 | arr[rightfulPos] = tmp; 36 | } while (rightfulPos != currIdx); 37 | } 38 | } 39 | 40 | /** 41 | * Overloaded helper method that calls internal implementation. 42 | * @param arr the input array to be sorted. 43 | */ 44 | public static void sort(int[] arr) { 45 | cyclicSort(arr, arr.length); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/cyclicSort/generalised/README.md: -------------------------------------------------------------------------------- 1 | # Generalized Case 2 | 3 | ## Background 4 | 5 | Implementation of cyclic sort in the generalised case where the input can contain any integer and duplicates. 6 | 7 | This is a comparison-based algorithm. In short, for the element encountered at the ith position of the original array, 8 | the algorithm does a O(n) traversal to search for its rightful position and does a swap. It repeats this until a swap 9 | placing the correct element at the ith position, before moving onto the next (i+1)th. 10 | 11 | ### Illustration 12 | 13 | Result: 6 10 6 5 7 4 2 1
  14 | Read: ^ 6 should be placed at index 4, so we do a swap with 6 and 7,

15 | Result: 7 10 6 5 6 4 2 1
  16 | Read: ^ 7 should be placed at index 6, so we do a swap. Note that index 5 should be taken up by dup 6

17 | Result: 2 10 6 5 6 4 7 1
  18 | Read: ^ 2 should be placed at index 1, so swap.

19 | Result: 10 2 6 5 6 4 7 1
  20 | Read: ^ 10 is the largest, should be placed at last index, so swap.

21 | Result: 1 2 6 5 6 4 7 10
  22 | Read: ^ Correctly placed, so move on. Same for 2.
  23 | Read:       24 | ^ 6 should be placed at index 4. But index 4 already has a 6. So place at index 5 and so on.

25 | Result: 1 2 4 5 6 6 7 10
  26 | Read:          27 | ^ ^ ^ ^ ^ Continue with O(n) verification of correct position at each iteration 28 | 29 | ## Complexity Analysis 30 | 31 | **Time**: 32 | 33 | - Best: O(n^2) even if the ith element is encountered in the ith position, a O(n) traversal validation check is needed 34 | - Worst: O(n^2) since we need O(n) time to find / validate the correct position of an element and 35 | the total number of O(n) traversals is bounded by O(n). 36 | - Average: O(n^2), it's bounded by the above two 37 | 38 | **Space**: O(1) auxiliary space, this is an in-place algorithm 39 | 40 | ## Notes -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/cyclicSort/simple/CyclicSort.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.cyclicSort.simple; 2 | 3 | /** 4 | * Implementation of cyclic sort in the simple case where the n elements of the given array are contiguous, 5 | * but not in sorted order. Below illustrates the idea using integers from 0 to n-1. 6 | */ 7 | public class CyclicSort { 8 | /** 9 | * Sorts the given array. 10 | * 11 | * @param arr the array to be sorted. 12 | */ 13 | public static void sort(int[] arr) { 14 | int curr = 0; // can be easily modified to work on n numbers starting at some other number 15 | while (curr < arr.length) { // iterate until the end of the array 16 | int ele = arr[curr]; // encounter an element that may not be in its correct position 17 | assert ele >= 0 && ele < arr.length : "Input array should only have integers from 0 to n-1 (inclusive)"; 18 | if (ele != curr) { // verified that it is indeed not the correct element to be placed at curr position 19 | int tmp = arr[ele]; // go to the correct position of ele 20 | arr[ele] = ele; // do a swap 21 | arr[curr] = tmp; // note that curr isn't incremented because we haven't yet placed the correct element 22 | } else { 23 | curr += 1; // found the correct element to be placed at curr, which in this eg, is itself, so, increment 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/cyclicSort/simple/FindFirstMissingNonNegative.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.cyclicSort.simple; 2 | 3 | /** 4 | * Cyclic sort algorithm can be easily modified to find first missing non-negative integer (i.e. starting from 0) 5 | * in O(n). 6 | */ 7 | public class FindFirstMissingNonNegative { 8 | /** 9 | * Finds the first missing non-negative integer in the array. 10 | * 11 | * @param arr the given array. 12 | * @return the first missing, non-negative integer. 13 | */ 14 | public static int findMissing(int[] arr) { 15 | int curr = 0; 16 | while (curr < arr.length) { 17 | int ele = arr[curr]; 18 | if (ele >= 0 && ele < arr.length && ele != curr) { // if ele (still) needs to be placed in its correct pos 19 | if (arr[ele] == ele) { // the correct position of ele already has ele (i.e. duplicates), so just move on 20 | curr += 1; 21 | continue; 22 | } 23 | int tmp = arr[ele]; // do the swap and place ele at its right position first 24 | arr[ele] = ele; 25 | arr[curr] = tmp; 26 | } else { 27 | curr += 1; // either found the correct element, or a number out of range to ignore first. 28 | } 29 | } 30 | 31 | for (int i = 0; i < arr.length; i++) { // iterate to look for the missing number which will be out of place. 32 | if (arr[i] != i) { 33 | return i; // this is the missing non-negative element! 34 | } 35 | } 36 | return arr.length; // 0 to n-1 integers are all present. First missing non-negative element is n. 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/cyclicSort/simple/README.md: -------------------------------------------------------------------------------- 1 | # Simple Case 2 | 3 | ## Background 4 | 5 | Cyclic Sort can achieve O(n) time complexity in cases where the elements of the collection have a known, 6 | continuous range, and there exists a direct, O(1) time-complexity mapping from each element to its respective index in 7 | the sorted order. 8 | 9 | This is typically applicable when sorting a sequence of integers that are in a consecutive range 10 | or can be easily mapped to such a range. We illustrate the idea with n integers from 0 to n-1. 11 | 12 | In this implementation, the algorithm is **not comparison-based**! (unlike the general case). 13 | It makes use of the known inherent ordering of the numbers, 14 | bypassing the `nlogn` lower bound for most sorting algorithms. 15 | 16 |
17 | Duplicates 18 | Not designed to hande duplicates. When duplicates are present, the algorithm can run into issues, 19 | such as overwriting elements or getting stuck in infinite loops, 20 | because it assumes that each element has a unique position in the array. 21 | 22 | If you need to handle duplicates, modifications are required, 23 | such as checking for duplicate values before placing elements, 24 | which can impact the simplicity and efficiency (possibly degrade to `O(n^2)`) of the algorithm. 25 |
26 | 27 |
28 | Inherent Ordering..? 29 | This property allows the sorting algorithm to avoid comparing elements with each other 30 | and instead directly place each element in its correct position. 31 | 32 | For example, if sorting integers from 0 to n-1, the number 0 naturally belongs at index 0, 1 at index 1, and so on. 33 | This inherent structure allows Cyclic Sort to achieve `O(n)` time complexity, 34 | bypassing the typical `O(nlogn)` time bound of comparison-based sorting algorithms 35 | ([proof](https://tildesites.bowdoin.edu/~ltoma/teaching/cs231/fall07/Lectures/sortLB.pdf)) 36 | by using the known order of elements rather than making comparisons to determine their positions. 37 |
38 | 39 | ## Complexity Analysis 40 | 41 | **Time**: 42 | 43 | - Best: O(n) since the array has to be traversed 44 | - Worst: O(n) since each element is at most seen twice 45 | - Average: O(n), it's bounded by the above two 46 | 47 | **Space**: O(1) auxiliary space, this is an in-place algorithm 48 | 49 | ## Case Study: Find First Missing Non-negative Integer 50 | 51 | Cyclic sort algorithm can be easily modified to find first missing non-negative integer (i.e. starting from 0) in O(n). 52 | The invariant is the same, but for numbers that are out-of-range (negative or greater than n), 53 | simply ignore the number at the position and 54 | move on first. It may be subject to swap later. 55 | 56 | There are other ways of doing so, using a hash set for instance, but what makes cyclic sort stand out is that it is 57 | able to do so in O(1) space. In other words, it is in-place and require no additional space. 58 | 59 | The algorithm does a 2-pass iteration. 60 | 61 | 1. In the 1st iteration, it places elements at its rightful position where possible. 62 | 2. In the 2nd iteration, it will look for the first out of place element (element that is not supposed 63 | to be in that position). The answer will be the index of that position. 64 | 65 | Note that the answer is necessarily between 0 and n (inclusive), where n is the length of the array, 66 | otherwise there would be a contradiction. 67 | 68 | ## Notes 69 | 70 | 1. It may seem quite trivial to sort integers from 0 to n-1 when one could simply generate such a sequence. 71 | But this algorithm is useful in cases where the integers to be sorted are keys to associated values (or some mapping) 72 | and sorting needs to be done in O(1) auxiliary space. 73 | 2. The implementation here uses integers from 0 to n-1. This can be easily modified for n contiguous integers starting 74 | at some arbitrary number (simply offset by this start number). 75 | 3. This version of cyclic sort does not handle duplicates (at least, sorting might not be guaranteed to be in O(n)) 76 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/insertionSort/InsertionSort.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.insertionSort; 2 | 3 | /** 4 | * Here, we are implementing InsertionSort where we sort the array in increasing (or more precisely, non-decreasing) 5 | * order. 6 | *

7 | * Note: 8 | * 1. the loop invariant here slightly differs from the lecture slides as we are using 0-based indexing 9 | * 2. Insertion into the sorted portion is done byb 'bubbling' elements as in bubble sort 10 | */ 11 | 12 | public class InsertionSort { 13 | /** 14 | * Sorts the given array in-place in non-decreasing order. 15 | * 16 | * @param arr array to be sorted. 17 | * @return the same array arr that is sorted. 18 | */ 19 | public static int[] sort(int[] arr) { 20 | int n = arr.length; 21 | for (int i = 1; i < n; i++) { //loop which supports the invariant 22 | arr = insert(arr, i, arr[i]); 23 | } 24 | return arr; 25 | } 26 | 27 | /** 28 | * Inserts val within the sorted portion of the array. The sorted portion of the array is arr[0, idx - 1]. 29 | * 30 | * @param arr the array to be sorted (of length idx) 31 | * @param idx index of the element to be inserted into the sorted portion of the array 32 | * @param val value of the element to be inserted into the sorted portion of the array 33 | * @return returns arr with arr[0, idx] in sorted order 34 | */ 35 | private static int[] insert(int[] arr, int idx, int val) { 36 | int pointer = idx - 1; 37 | 38 | while (pointer >= 0 && arr[pointer] > val) { //if we change this to arr[pointer] < val, 39 | // we can sort the array in non-increasing order 40 | arr[pointer + 1] = arr[pointer]; 41 | pointer -= 1; 42 | } 43 | arr[pointer + 1] = val; 44 | 45 | return arr; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/insertionSort/README.md: -------------------------------------------------------------------------------- 1 | # Insertion Sort 2 | 3 | ## Background 4 | 5 | Insertion sort is a comparison-based sorting algorithm that builds the final sorted array one element at a 6 | time. It works by repeatedly taking an element from the unsorted portion of the array and 7 | inserting it correctly (portion remains sorted) into the sorted portion. Note that the position is not final 8 | since subsequent elements from unsorted portion may displace previously inserted elements. What's important is 9 | the sorted region remains sorted. 10 | 11 | More succinctly, at the kth iteration, we take the element arr[k] and insert 12 | it into arr[0, k-1] following sorted order, returning us arr[0, k] in sorted order. 13 | 14 | ![InsertionSort](../../../../../../docs/assets/images/InsertionSort.png) 15 | 16 | ### Implementation Invariant 17 | The loop invariant: **At the end of kth iteration, the first (k+1) items in the array are in sorted order**. 18 | 19 | At the end of the (n-1)th iteration, all n items in the array will be in sorted order. 20 | 21 | ## Complexity Analysis 22 | 23 | **Time**: 24 | 25 | - Worst case (reverse sorted array): O(n^2) 26 | - Average case: O(n^2) 27 | - Best case (sorted array): O(n) 28 | 29 | In the worst case, inserting an element into the sorted array of length m requires us to iterate through the 30 | entire array, requiring O(m) time. Since InsertionSort does this insertion (n - 1) times, the time complexity 31 | of InsertionSort in the worst case is 1 + 2 + ... + (n-2) + (n-1) = O(n^2). 32 | 33 | In the best case of an already sorted array, inserting an element into the sorted array of length m requires 34 | O(1) time as we insert it directly behind the first position of the pointer in the sorted array. Since InsertionSort 35 | does this insertion (n-1) times, the time complexity of InsertionSort in the best case is O(1) * (n-1) = O(n). 36 | 37 | **Space**: O(1) since sorting is done in-place 38 | 39 | ## Notes 40 | 41 | ### Common Misconception 42 | 43 | Its invariant is often confused with selection sort's. In selection sort, an element in the unsorted region will 44 | be immediately placed in its correct and final position as it would be in the sorted array. This is not the case 45 | for insertion sort. However, it is because of this 'looser' invariant that allows for a better best case time complexity 46 | for insertion sort. 47 | 48 | Image Source: https://www.hackerrank.com/challenges/correctness-invariant/problem 49 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/mergeSort/iterative/MergeSort.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.mergeSort.iterative; 2 | 3 | /** 4 | * Here, we are implementing MergeSort where we sort the array in increasing (or more precisely, non-decreasing) 5 | * order iteratively. 6 | */ 7 | 8 | public class MergeSort { 9 | 10 | /** 11 | * Sorts the given array in non-decreasing order. 12 | * 13 | * @param arr The given array to be sorted. 14 | */ 15 | public static void sort(int[] arr) { 16 | int interval = 1; 17 | int n = arr.length; 18 | int[] temp = new int[n]; 19 | 20 | while (interval < n) { 21 | for (int i = 0; i < n - interval; i += 2 * interval) { 22 | int end = Math.min(i + 2 * interval - 1, n - 1); 23 | int mid = i + interval - 1; 24 | merge(arr, i, mid, end, temp); 25 | } 26 | interval *= 2; 27 | } 28 | } 29 | 30 | /** 31 | * Merges two sorted sub-arrays within the given array. The two sub-arrays are arr[start, mid] and 32 | * arr[mid + 1, end]. Upon completion of this function, arr[start, end] will be in sorted order. 33 | * 34 | * @param arr The array containing the sub-arrays to be merged. 35 | * @param start The starting index of the first sub-array to be merged. 36 | * @param mid The ending index (inclusive) of the first sub-array to be merged. In the iterative implementation, 37 | * the mid parameter is required in the merge function to determine the splitting point between the 38 | * sub-arrays to be merged. 39 | * @param end The ending index (inclusive) of the second sub-array to be merged. 40 | * @param temp A temporary array used for merging intermediate results. 41 | */ 42 | private static void merge(int[] arr, int start, int mid, int end, int[] temp) { 43 | int i = start; 44 | int j = mid + 1; 45 | int pointer = start; 46 | 47 | // Merge the two sorted sub-arrays into the temp array 48 | while (i <= mid && j <= end) { 49 | if (arr[i] <= arr[j]) { 50 | //we use <= here to maintain stability of MergeSort. If arr[i] == arr[j], the algorithm prefers the 51 | //one from the left sub-array (arr[i]). This decision preserves the relative order of equal elements. 52 | 53 | //if we change this to arr[i] >= arr[j], we can sort the array in non-increasing order. 54 | 55 | temp[pointer] = arr[i]; 56 | i++; 57 | } else { 58 | temp[pointer] = arr[j]; 59 | j++; 60 | } 61 | pointer++; 62 | } 63 | 64 | // Copy any remaining elements from the left sub-array 65 | while (i <= mid) { 66 | temp[pointer] = arr[i]; 67 | i++; 68 | pointer++; 69 | } 70 | 71 | // Copy any remaining elements from the right sub-array 72 | while (j <= end) { 73 | temp[pointer] = arr[j]; 74 | j++; 75 | pointer++; 76 | } 77 | 78 | // Copy the merged elements back to the original array 79 | if (end + 1 - start >= 0) { 80 | System.arraycopy(temp, start, arr, start, end + 1 - start); 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/mergeSort/iterative/README.md: -------------------------------------------------------------------------------- 1 | # Merge Sort 2 | 3 | ### Background 4 | The iterative implementation of MergeSort takes a bottom-up approach, where the sorting process starts by merging 5 | intervals of size 1. Intervals of size 1 are trivially in sorted order. The algorithm then proceeds to merge 6 | adjacent sorted intervals, doubling the interval size with each merge step, until the entire array is fully sorted. 7 | 8 | ![MergeSort Iterative](../../../../../../../docs/assets/images/MergeSortIterative.jpg) 9 | 10 | Image Source: https://www.chelponline.com/iterative-merge-sort-12243 11 | 12 | ### Implementation Invariant 13 | At each iteration of the merging process, the main array is divided into sub-arrays of a certain interval 14 | size (interval). Each of these sub-arrays is sorted within itself. The last sub-array may not be of size 15 | interval, but it is still sorted since its size is necessarily less than interval. 16 | 17 | ### Complexity Analysis 18 | Time: 19 | - Worst case: O(nlogn) 20 | - Average case: O(nlogn) 21 | - Best case: O(nlogn) 22 | 23 | Given two sorted arrays of size p and q, we need O(p + q) time to merge the two arrays into one sorted array, since 24 | we have to iterate through every element in both arrays once. 25 | 26 | - At the 1st level of the merge process, our merging subroutine involves n sub-arrays of size 1, taking O(n) time. 27 | - At the 2nd level of the merge process, our merging subroutine involves (n/2) sub-arrays of size 2, taking O(n) time. 28 | - At the kth level of the merge process, our merging subroutine involves (n/(2^(k-1))) sub-arrays of size (2^(k-1)), 29 | taking O(n) time. 30 | 31 | Since interval doubles at every iteration of the merge process, there are logn such levels. Every level takes 32 | O(n) time, hence overall time complexity is n * logn = O(nlogn) 33 | 34 | Regardless of how sorted the input array is, MergeSort carries out the partitioning and merging process, so the 35 | time complexity of MergeSort is O(nlogn) for all cases. 36 | 37 | Space: 38 | - O(n) since we require a temporary array to temporarily store the merged elements in sorted order -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/mergeSort/recursive/MergeSort.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.mergeSort.recursive; 2 | 3 | /** 4 | * Here, we are implementing MergeSort where we sort the array in increasing (or more precisely, non-decreasing) 5 | * order recursively. 6 | */ 7 | 8 | public class MergeSort { 9 | 10 | /** 11 | * Sorts the sub-array arr[start, end] using the MergeSort algorithm. 12 | * 13 | * @param arr The given array to be sorted. 14 | * @param start The starting index of the sub-array to be sorted. 15 | * @param end The ending index (inclusive) of the sub-array to be sorted. 16 | * @param temp A temporary array used for merging. 17 | */ 18 | private static void mergeSort(int[] arr, int start, int end, int[] temp) { 19 | if (start >= end) { 20 | return; 21 | } 22 | int mid = (start + end) / 2; 23 | mergeSort(arr, start, mid, temp); 24 | mergeSort(arr, mid + 1, end, temp); 25 | merge(arr, start, end, temp); 26 | } 27 | 28 | /** 29 | * Merges two sorted sub-arrays within the given array. The two sub-arrays are arr[start, mid] and 30 | * arr[mid + 1, end]. Upon completion of this function, arr[start, end] will be in sorted order. 31 | * 32 | * @param arr The array containing the sub-arrays to be merged. 33 | * @param start The starting index of the first sub-array to be merged. 34 | * @param end The ending index (inclusive) of the second sub-array to be merged. 35 | * @param temp A temporary array used for merging intermediate results. 36 | */ 37 | private static void merge(int[] arr, int start, int end, int[] temp) { 38 | int mid = (start + end) / 2; 39 | int i = start; 40 | int j = mid + 1; 41 | int pointer = start; 42 | 43 | // Merge the two sorted sub-arrays into the temp array 44 | while (i <= mid && j <= end) { 45 | if (arr[i] <= arr[j]) { 46 | //we use <= here to maintain stability of MergeSort. If arr[i] == arr[j], the algorithm prefers the 47 | //one from the left sub-array (arr[i]). This decision preserves the relative order of equal elements. 48 | 49 | //if we change this to arr[i] >= arr[j], we can sort the array in non-increasing order. 50 | 51 | temp[pointer] = arr[i]; 52 | i++; 53 | } else { 54 | temp[pointer] = arr[j]; 55 | j++; 56 | } 57 | pointer++; 58 | } 59 | 60 | // Copy any remaining elements from the left sub-array 61 | while (i <= mid) { 62 | temp[pointer] = arr[i]; 63 | i++; 64 | pointer++; 65 | } 66 | 67 | // Copy any remaining elements from the right sub-array 68 | while (j <= end) { 69 | temp[pointer] = arr[j]; 70 | j++; 71 | pointer++; 72 | } 73 | 74 | // Copy the merged elements back to the original array 75 | if (end + 1 - start >= 0) { 76 | System.arraycopy(temp, start, arr, start, end + 1 - start); 77 | } 78 | } 79 | 80 | /** 81 | * Sorts the given array in non-decreasing order. 82 | * 83 | * @param arr The given array to be sorted 84 | */ 85 | public static void sort(int[] arr) { 86 | int n = arr.length; 87 | int[] temp = new int[n]; 88 | mergeSort(arr, 0, n - 1, temp); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/mergeSort/recursive/README.md: -------------------------------------------------------------------------------- 1 | # Merge Sort 2 | 3 | ### Background 4 | MergeSort is a divide-and-conquer sorting algorithm. The recursive implementation takes a top-down approach by 5 | recursively dividing the array into two halves, sorting each half separately, and then merging the sorted halves 6 | to produce the final sorted output. 7 | 8 | ![MergeSort Recursive](../../../../../../../docs/assets/images/MergeSortRecursive.png) 9 | 10 | Image Source: https://www.101computing.net/merge-sort-algorithm/ 11 | 12 | ### Implementation Invariant (for the merging subroutine) 13 | The sub-array temp[start, (k-1)] consists of the (𝑘−start) smallest elements of arr[start, mid] and 14 | arr[mid + 1, end], in sorted order. 15 | 16 | ### Complexity Analysis 17 | Time: 18 | - Worst case: O(nlogn) 19 | - Average case: O(nlogn) 20 | - Best case: O(nlogn) 21 | Merging two sorted sub-arrays of size (n/2) requires O(n) time as we need to iterate through every element in both 22 | sub-arrays in order to merge the two sorted sub-arrays into one sorted array. 23 | 24 | Recursion expression: T(n) = 2T(n/2) + O(n) => O(nlogn) 25 | 26 | Regardless of how sorted the input array is, MergeSort carries out the same divide-and-conquer strategy, so the 27 | time complexity of MergeSort is O(nlogn) for all cases. 28 | 29 | Space: 30 | - O(n) since we require a temporary array to temporarily store the merged elements in sorted order -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/quickSort/README.md: -------------------------------------------------------------------------------- 1 | # QuickSort 2 | 3 | This page will provide a high-level overview of the different types of QuickSort and the significance of each 4 | enhancement. 5 | 6 | ![QuickSort Overview](../../../../../../docs/assets/images/QuickSortOverview.jpeg) 7 | 8 | We start off with Simple QuickSort, which utilises Hoare's / Lomuto's partitioning with a fixed pivot. But the time 9 | complexity of Simple QuickSort degrades to O(n^2) when pivots chosen are continuously bad. This could be because of 10 | inputs such as sorted arrays, where picking the first element as the pivot will always lead to an extremely imbalanced 11 | partitioning. This could also be because of the presence of duplicates, as we now handle duplicates by putting them to 12 | one side of the pivot. 13 | 14 | Therefore, we introduced randomisation in pivot selection to prevent fixed pivot selection from continuously choosing 15 | a bad pivot. We also added a good pivot check (Paranoid) to Simple QuickSort to ensure that the pivots we select are 16 | "good" and will not cause extremely imbalanced partitions. However, the presence of the good pivot checks will cause 17 | infinite recursion when the array contains many duplicates, since any pivot you choose in the array will fail the good 18 | pivot check. 19 | 20 | So, we introduced 3-way partitioning to handle duplicates by partitioning the array into three sections: elements less 21 | than the pivot, elements equal to the pivot, and elements greater than the pivot. Now the good pivot check ignores the 22 | size of the segment that comprises elements = to pivot. 23 | 24 | ## Recommended Order of Reading 25 | 1. [Hoares](./hoares) 26 | 2. [Lomuto](./lomuto) 27 | 3. [Paranoid](./paranoid) 28 | 4. [3-way partitioning](./threeWayPartitioning) 29 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/quickSort/hoares/QuickSort.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.quickSort.hoares; 2 | 3 | /** 4 | * Here, we are implementing Hoares's QuickSort where we sort the array in increasing (or more precisely, 5 | * non-decreasing) order. We will follow lecture implementation here, which differs slightly from the usual 6 | * implementation of Hoare's. See more under notes in README. 7 | *

8 | * Implementation Invariant: 9 | * The pivot is in the correct position, with elements to its left being <= it, and elements to its right being > it. 10 | * (We edited the pseudocode a bit to keep the duplicates to the left of the pivot.) 11 | *

12 | * This implementation picks the first element as the pivot. 13 | */ 14 | 15 | public class QuickSort { 16 | /** 17 | * Sorts the given array in-place in non-decreasing order. 18 | * 19 | * @param arr array to be sorted. 20 | */ 21 | public static void sort(int[] arr) { 22 | int n = arr.length; 23 | quickSort(arr, 0, n - 1); 24 | } 25 | 26 | /** 27 | * Recursively sorts the sub-array from index 'start' to index 'end' in non-decreasing order 28 | * using the QuickSort algorithm. 29 | * 30 | * @param arr the array containing the sub-array to be sorted. 31 | * @param start the starting index (inclusive) of the sub-array to be sorted. 32 | * @param end the ending index (inclusive) of the sub-array to be sorted. 33 | */ 34 | private static void quickSort(int[] arr, int start, int end) { 35 | if (start < end) { 36 | int pIdx = partition(arr, start, end); 37 | quickSort(arr, start, pIdx - 1); 38 | quickSort(arr, pIdx + 1, end); 39 | } 40 | } 41 | 42 | /** 43 | * Partitions the sub-array from index 'start' to index 'end' around a randomly selected pivot element. 44 | * The elements less than or equal to the pivot are placed on the left side, and the elements greater than 45 | * the pivot are placed on the right side. 46 | *

47 | * Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we need to iterate 48 | * through every element in the sub-array once. 49 | * 50 | * @param arr the array containing the sub-array to be partitioned. 51 | * @param start the starting index (inclusive) of the sub-array to be partitioned. 52 | * @param end the ending index (inclusive) of the sub-array to be partitioned. 53 | * @return the index of the pivot element in its correct position after partitioning. 54 | */ 55 | private static int partition(int[] arr, int start, int end) { 56 | int pivot = arr[start]; 57 | int low = start; 58 | int high = end + 1; 59 | 60 | while (low < high) { 61 | do { 62 | low++; 63 | } while (low < high && arr[low] <= pivot); // we use <= as opposed to < to pack duplicates to the left side 64 | // of the pivot 65 | do { 66 | high--; 67 | } while (low < high && arr[high] > pivot); 68 | 69 | if (low < high) { 70 | swap(arr, low, high); 71 | } 72 | } 73 | swap(arr, start, low - 1); 74 | 75 | return low - 1; 76 | } 77 | 78 | /** 79 | * Swaps the elements at indices 'i' and 'j' in the given array. 80 | * 81 | * @param arr the array in which the elements should be swapped. 82 | * @param i the index of the first element to be swapped. 83 | * @param j the index of the second element to be swapped. 84 | */ 85 | private static void swap(int[] arr, int i, int j) { 86 | int temp = arr[i]; 87 | arr[i] = arr[j]; 88 | arr[j] = temp; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/quickSort/lomuto/QuickSort.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.quickSort.lomuto; 2 | 3 | /** 4 | * Here, we are implementing Lomuto's QuickSort where we sort the array in increasing (or more precisely, 5 | * non-decreasing) order. 6 | */ 7 | 8 | public class QuickSort { 9 | /** 10 | * Sorts the given array in-place in non-decreasing order. 11 | * 12 | * @param arr array to be sorted. 13 | */ 14 | public static void sort(int[] arr) { 15 | int n = arr.length; 16 | quickSort(arr, 0, n - 1); 17 | } 18 | 19 | /** 20 | * Recursively sorts the sub-array from index 'start' to index 'end' in non-decreasing order 21 | * using the QuickSort algorithm. 22 | * 23 | * @param arr the array containing the sub-array to be sorted. 24 | * @param start the starting index (inclusive) of the sub-array to be sorted. 25 | * @param end the ending index (inclusive) of the sub-array to be sorted. 26 | */ 27 | private static void quickSort(int[] arr, int start, int end) { 28 | if (start < end) { 29 | int pIdx = partition(arr, start, end); 30 | quickSort(arr, start, pIdx - 1); 31 | quickSort(arr, pIdx + 1, end); 32 | } 33 | } 34 | 35 | /** 36 | * Partitions the sub-array from index 'start' to index 'end' around a randomly selected pivot element. 37 | * The elements less than or equal to the pivot are placed on the left side, and the elements greater than 38 | * the pivot are placed on the right side. 39 | *

40 | * Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we need to iterate 41 | * through every element in the sub-array once. 42 | * 43 | * @param arr the array containing the sub-array to be partitioned. 44 | * @param start the starting index (inclusive) of the sub-array to be partitioned. 45 | * @param end the ending index (inclusive) of the sub-array to be partitioned. 46 | * @return the index of the pivot element in its correct position after partitioning. 47 | */ 48 | private static int partition(int[] arr, int start, int end) { 49 | int pIdx = random(start, end); 50 | int pivot = arr[pIdx]; 51 | 52 | swap(arr, start, pIdx); // swap the pivot to the start of the array 53 | 54 | int idx = start + 1; // interpret: at the end, all elements at indices less than this var is <= pivot 55 | 56 | for (int i = start + 1; i <= end; i++) { 57 | if (arr[i] <= pivot) { 58 | swap(arr, idx, i); 59 | idx++; 60 | } 61 | } 62 | 63 | swap(arr, idx - 1, start); // swap the pivot to its correct position 64 | 65 | return idx - 1; 66 | } 67 | 68 | /** 69 | * Swaps the elements at indices 'i' and 'j' in the given array. 70 | * 71 | * @param arr the array in which the elements should be swapped. 72 | * @param i the index of the first element to be swapped. 73 | * @param j the index of the second element to be swapped. 74 | */ 75 | private static void swap(int[] arr, int i, int j) { 76 | int temp = arr[i]; 77 | arr[i] = arr[j]; 78 | arr[j] = temp; 79 | } 80 | 81 | /** 82 | * Generates a random integer within the range [start, end]. 83 | * 84 | * @param start the lower bound of the random integer (inclusive). 85 | * @param end the upper bound of the random integer (inclusive). 86 | * @return a random integer within the specified range. 87 | */ 88 | private static int random(int start, int end) { 89 | return (int) (Math.random() * (end - start + 1)) + start; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/quickSort/lomuto/README.md: -------------------------------------------------------------------------------- 1 | # Lomuto's QuickSort 2 | 3 | ## Background 4 | QuickSort is a divide-and-conquer sorting algorithm. The basic idea behind Quicksort is to choose a pivot element, 5 | places it in its correct position in the sorted array, and then recursively sorts the sub-arrays on either side of 6 | the pivot. When we introduce randomization in pivot selection, every element has equal probability of being 7 | selected as the pivot. This means the chance of an extreme element getting chosen as the pivot is decreased, so we 8 | reduce the probability of encountering the worst-case scenario of imbalanced partitioning. 9 | 10 | This is how QuickSort works if we always pick the first element as the pivot with Lomuto's partitioning. 11 | 12 | ![QuickSort with first element as pivot](../../../../../../../docs/assets/images/QuickSortFirstPivot.png) 13 | 14 | Image Source: https://www.geeksforgeeks.org/implement-quicksort-with-first-element-as-pivot/ 15 | 16 | If we use randomised pivot selection, the idea is very similar to the above implementation. All we 17 | need to do is to swap the random pivot to the first element in the array, then partition as per usual, 18 | then swap the pivot back to its correct position. Below is an illustration: 19 | 20 | ![Lomuto's QuickSort with random pivot](../../../../../../../docs/assets/images/Lomutos.jpeg) 21 | 22 | ## Implementation Invariant 23 | The pivot is in the correct position, with elements to its left being <= it, and elements to its right being > it. 24 | 25 | ## Complexity Analysis: 26 | * The complexity analysis for Lomuto's quicksort is the same as that of Hoare's quicksort. 27 | Time: 28 | - Expected worst case (poor choice of pivot): O(n^2) 29 | - Expected average case: O(nlogn) 30 | - Expected best case (balanced pivot): O(nlogn) 31 | 32 | In the best case of a balanced pivot, the partitioning process divides the array in half, which leads to log n 33 | levels of recursion. Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we 34 | need to iterate through every element in the sub-array once. 35 | Therefore, the recurrence relation is: T(n) = 2T(n/2) + O(n) => O(nlogn). 36 | 37 | Even in the average case where the chosen pivot partitions the array by a fraction, there will still be log n levels 38 | of recursion. (e.g. T(n) = T(n/10) + T(9n/10) + O(n) => O(nlogn)) 39 | 40 | However, if there are many duplicates in the array, e.g. {1, 1, 1, 1}, the 1st pivot will be placed in the 3rd idx, 41 | and 2nd pivot in 2nd idx, 3rd pivot in the 1st idx and 4th pivot in the 0th idx. As we observe, the presence of many 42 | duplicates in the array leads to extremely unbalanced partitioning, leading to a O(n^2) time complexity. 43 | 44 | Space: 45 | - O(1) excluding memory allocated to the call stack, since partitioning is done in-place 46 | 47 | ## Notes 48 | ### Lomuto's vs Hoare's QuickSort 49 | 50 | Lomuto's partition scheme is in contrast to Hoare's partition scheme. Hoare's uses two pointers, while Lomuto's uses 51 | one. Hoare's partition scheme is generally more efficient as it requires less swaps. See more at 52 | https://www.geeksforgeeks.org/hoares-vs-lomuto-partition-scheme-quicksort/. -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/quickSort/paranoid/README.md: -------------------------------------------------------------------------------- 1 | # Paranoid QuickSort 2 | 3 | ## Background 4 | Paranoid Quicksort is the naive quicksort with that allow additional attempts to guarantee a good pivot selection. 5 | 6 | ![ParanoidQuickSort](../../../../../../../docs/assets/images/ParanoidQuickSort.jpeg) 7 | 8 | ## Complexity Analysis: 9 | Time: (this analysis assumes the absence of many duplicates in our array) 10 | - Expected worst case: O(nlogn) 11 | - Expected average case: O(nlogn) 12 | - Expected best case: O(nlogn) 13 | 14 | The additional check to guarantee a good pivot guards against the worst case scenario where the chosen pivot results 15 | in an extremely imbalanced partitioning. Since the chosen pivot has to at least partition the array into a 16 | 1/10, 9/10 split, the recurrence relation will be: T(n) = T(n/10) + T(9n/10) + n(# iterations of pivot selection). 17 | 18 | The number of iterations of pivot selection is expected to be <2 (more precisely, 1.25). This is because 19 | P(good pivot) = 8/10. Expected number of tries to get a good pivot = 1 / P(good pivot) = 10/8 = 1.25. 20 | 21 | Therefore, the expected time-complexity is: T(n) = T(n/10) + T(9n/10) + 1.25n => O(nlogn). 22 | 23 | - Edge case: does not terminate 24 | The presence of this additional check and repeating pivot selection means that if we have an array of 25 | length n >= 10 containing all/many duplicates of the same number, any pivot we pick will be a bad pivot and we will 26 | enter an infinite loop of repeating pivot selection. 27 | 28 | Space: 29 | - O(1) excluding memory allocated to the call stack, since partitioning is done in-place -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/quickSort/threeWayPartitioning/README.md: -------------------------------------------------------------------------------- 1 | # Three-Way Partitioning 2 | 3 | ## Background 4 | Three-way partitioning is an improved partitioning scheme, used in QuickSort, to tackle the scenario where there are 5 | many duplicate elements. This partitioning scheme will resolve the infinite loop error possibly faced by 6 | Paranoid Quicksort. 7 | 8 | The idea behind three-way partitioning is to divide the array into three sections: elements less than the pivot, 9 | elements equal to the pivot, and elements greater than the pivot. By doing so, we can avoid unnecessary comparisons 10 | and swaps with duplicate elements, making the sorting process more efficient. 11 | 12 | Note that during the partitioning process, there would be a 4th region - 'In Progress' region that will hold elements 13 | that haven't yet been placed in the right section (see below). 14 | 15 | ![ThreeWayPartitioning](../../../../../../../docs/assets/images/ThreeWayPartitioning.jpeg) 16 | 17 | ## Implementation Invariant: 18 | The pivot and any element numerically equal to the pivot will be in the correct positions in the array. Elements 19 | to their left are < them and elements to their right are > than them. 20 | 21 | ## Complexity Analysis: 22 | Time: 23 | - Worst case: O(nlogn) 24 | - Average case: O(nlogn) 25 | - Best case: O(nlogn) 26 | 27 | Space: 28 | - O(1) excluding memory allocated to the call stack, since partitioning is done in-place 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/radixSort/RadixSort.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.radixSort; 2 | 3 | /** 4 | * This class implements a Radix Sort Algorithm. 5 | */ 6 | public class RadixSort { 7 | 8 | private static final int NUM_BITS = 8; 9 | private static final int NUM_SEGMENTS = 4; 10 | 11 | /** 12 | * Creates masking on the segment to obtain the value of the digit. 13 | * 14 | * @param num The number. 15 | * @param segment The segment we are interested in. 16 | * @return The value of the digit in the number at the given segment. 17 | */ 18 | private static int getSegmentMasked(int num, int segment) { 19 | // bit masking here to extract each segment from the integer. 20 | int mask = (1 << NUM_BITS) - 1; 21 | return (num >> (segment * NUM_BITS)) & mask; // we do a right-shift on num to focus on the desired segment 22 | } 23 | 24 | /** 25 | * Radix sorts a given input array and updates the output array in-place. 26 | * 27 | * @param arr original input array. 28 | * @param sorted output array. 29 | */ 30 | private static void radixSort(int[] arr, int[] sorted) { 31 | // Code in the loop is essentially counting sort; sort the N numbers by segments, starting from right-most 32 | for (int i = 0; i < NUM_SEGMENTS; i++) { 33 | int[] freqMap = new int[1 << NUM_BITS]; // at most this number of elements 34 | 35 | // count each element 36 | for (int num : arr) { 37 | freqMap[getSegmentMasked(num, i)]++; 38 | } 39 | // get prefix sum 40 | for (int j = 1; j < freqMap.length; j++) { 41 | freqMap[j] += freqMap[j - 1]; 42 | } 43 | // place each number in its correct sorted position up until the given segment 44 | for (int k = arr.length - 1; k >= 0; k--) { 45 | int curr = arr[k]; 46 | int id = getSegmentMasked(curr, i); 47 | sorted[freqMap[id] - 1] = curr; 48 | freqMap[id]--; 49 | } 50 | // We do a swap so that our results above for this segment is 51 | // saved and passed as input to the next segment. 52 | // By doing this we no longer need to create a new array 53 | // every time we shift to a new segment to sort. 54 | // We can constantly reuse the array, allowing us to only use O(n) space. 55 | int[] tmp = arr; 56 | arr = sorted; 57 | sorted = tmp; 58 | } 59 | sorted = arr; 60 | } 61 | 62 | /** 63 | * Calls radix sort inplace on a given array. 64 | * 65 | * @param arr The array to be sorted. 66 | */ 67 | public static void radixSort(int[] arr) { 68 | int[] sorted = new int[arr.length]; 69 | radixSort(arr, sorted); 70 | arr = sorted; // swap back lol 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/selectionSort/README.md: -------------------------------------------------------------------------------- 1 | # Selection Sort 2 | 3 | ## Background 4 | 5 | Selection sort is another intuitive comparison-based sorting algorithm. It works similarly to other sorting algorithms 6 | like bubble and insertion in the sense that it maintains a sorted and unsorted region. It does so by repeatedly finding 7 | smallest (or largest) element in the unsorted region, and places the element in the correct and final position as it 8 | would be in the sorted array. 9 | 10 | ![SelectionSort](../../../../../../docs/assets/images/SelectionSort.png) 11 | 12 | ### Implementation Invariant 13 | Let the array of length n to be sorted be A. 14 | 15 | The loop invariant is: 16 | **At the end of the kth iteration, the smallest k items are correctly sorted in the first k positions of the array**. 17 | 18 | So, at the end of the (n-1)th iteration of the loop, the smallest (n-1) items are correctly sorted in the first (n-1) 19 | positions of the array, leaving the last item correctly positioned in the last index of the array. Therefore, 20 | (n-1) iterations of the loop is sufficient. 21 | 22 | ## Complexity Analysis 23 | 24 | **Time**: 25 | 26 | - Worst case: O(n^2) 27 | - Average case: O(n^2) 28 | - Best case: O(n^2) 29 | 30 | Regardless of how sorted the input array is, selectionSort will run the minimum element finding algorithm (n-1) 31 | times. For an input array of length m, finding the minimum element necessarily takes O(m) time. Therefore, the 32 | time complexity of selectionSort is n + (n-1) + (n-2) + ... + 2 = O(n^2) 33 | 34 | **Space**: O(1) since sorting is done in-place 35 | 36 | Image Source: https://www.hackerearth.com/practice/algorithms/sorting/selection-sort/tutorial/ 37 | 38 | ## Notes 39 | The number of swaps made is always O(n) (careful not to confuse this with the number of comparisons). 40 | -------------------------------------------------------------------------------- /src/main/java/algorithms/sorting/selectionSort/SelectionSort.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.selectionSort; 2 | 3 | /** 4 | * Here, we are implementing SelectionSort where we sort the array in increasing (or more precisely, non-decreasing) 5 | * order. 6 | */ 7 | 8 | public class SelectionSort { 9 | /** 10 | * Sorts the given array in-place in non-decreasing order. 11 | * 12 | * @param arr array to be sorted. 13 | * @return the same array arr that is sorted. 14 | */ 15 | public static int[] sort(int[] arr) { 16 | int n = arr.length; 17 | for (int i = 0; i < n - 1; i++) { //loop which supports the invariant 18 | int minElemIdx = minElemIdx(i, n, arr); 19 | int temp = arr[i]; 20 | arr[i] = arr[minElemIdx]; 21 | arr[minElemIdx] = temp; 22 | } 23 | return arr; 24 | } 25 | 26 | /** 27 | * Finds the index of the minimum element within the specified range of the array. 28 | * The range is from 'start' (inclusive) to 'end' (exclusive). 29 | * 30 | * @param start the starting index (inclusive) of the range to be considered. 31 | * @param end the ending index (exclusive) of the range to be considered. 32 | * @param arr the array to be sorted. 33 | * @return the index of the minimum element within the range. 34 | *

35 | * We can easily modify this method to find maxElemIdx instead to sort the array in non-increasing order. 36 | */ 37 | private static int minElemIdx(int start, int end, int[] arr) { 38 | int min = Integer.MAX_VALUE; 39 | int idx = -1; 40 | for (int i = start; i < end; i++) { 41 | if (arr[i] < min) { 42 | min = arr[i]; 43 | idx = i; 44 | } 45 | } 46 | return idx; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/dataStructures/avlTree/Node.java: -------------------------------------------------------------------------------- 1 | package dataStructures.avlTree; 2 | 3 | /** 4 | * TreeNode implementation for AVL Tree. 5 | * Note: Properties should rightfully be kept private 6 | * and accessed/modified via public getters/setters. 7 | * But it was left as such to avoid clutter. 8 | * 9 | * @param generic type of objects to be stored in the tree; must be comparable 10 | */ 11 | public class Node> { 12 | private T key; 13 | private Node left; 14 | private Node right; 15 | private Node parent; 16 | private int height; 17 | /* 18 | * Can insert more properties here for augmentation 19 | * e.g. If key is not unique, introduce a value property as a tie-breaker 20 | * or weight property for order statistics 21 | */ 22 | 23 | public Node(T key) { 24 | this.key = key; 25 | } 26 | 27 | public boolean isLeaf() { 28 | return this.left == null && this.right == null; 29 | } 30 | 31 | public T getKey() { 32 | return key; 33 | } 34 | 35 | public void setKey(T key) { 36 | this.key = key; 37 | } 38 | 39 | public Node getLeft() { 40 | return left; 41 | } 42 | 43 | public void setLeft(Node left) { 44 | this.left = left; 45 | } 46 | 47 | public Node getRight() { 48 | return right; 49 | } 50 | 51 | public void setRight(Node right) { 52 | this.right = right; 53 | } 54 | 55 | public Node getParent() { 56 | return parent; 57 | } 58 | 59 | public void setParent(Node parent) { 60 | this.parent = parent; 61 | } 62 | 63 | public int getHeight() { 64 | return height; 65 | } 66 | 67 | public void setHeight(int height) { 68 | this.height = height; 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return key.toString(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/dataStructures/binarySearchTree/Node.java: -------------------------------------------------------------------------------- 1 | package dataStructures.binarySearchTree; 2 | 3 | /** 4 | * Node implementation for Binary Search Tree. 5 | * Note: Properties should rightfully be kept private 6 | * and accessed/modified via public getters/setters. 7 | * But it was left as such to avoid clutter. 8 | * 9 | * @param generic type of objects to be stored in the tree; must be comparable 10 | */ 11 | public class Node, V> { 12 | private T key; 13 | private Node left; 14 | private Node right; 15 | private Node parent; 16 | private V value; 17 | 18 | /** 19 | * Constructor for a BST node. 20 | * Can insert more properties here. 21 | * If key is not unique, introduce a value property, so when nodes are being compared, a distinction can be made. 22 | * @param key the key for the BST Node. 23 | * @param value the value encapsulated by the BST node. 24 | */ 25 | public Node(T key, V value) { 26 | this.key = key; 27 | this.value = value; 28 | } 29 | 30 | /** 31 | * Determines whether the node is a leaf node. 32 | * @return true if the node is a leaf node, false if not. 33 | */ 34 | public boolean isLeaf() { 35 | return this.left == null && this.right == null; 36 | } 37 | 38 | public T getKey() { 39 | return key; 40 | } 41 | 42 | public void setKey(T key) { 43 | this.key = key; 44 | } 45 | 46 | public Node getLeft() { 47 | return left; 48 | } 49 | 50 | public void setLeft(Node left) { 51 | this.left = left; 52 | } 53 | 54 | public Node getRight() { 55 | return right; 56 | } 57 | 58 | public void setRight(Node right) { 59 | this.right = right; 60 | } 61 | 62 | public Node getParent() { 63 | return parent; 64 | } 65 | 66 | public void setParent(Node parent) { 67 | this.parent = parent; 68 | } 69 | 70 | public V getValue() { 71 | return value; 72 | } 73 | 74 | public void setValue(V value) { 75 | this.value = value; 76 | } 77 | 78 | @Override 79 | public String toString() { 80 | return "Key: " + key.toString() + ", Value: " + (value == null ? "null" : value.toString()); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/dataStructures/binarySearchTree/README.md: -------------------------------------------------------------------------------- 1 | # Binary Search Tree 2 | 3 | ## Overview 4 | 5 | A Binary Search Tree (BST) is a tree-based data structure in which each node has at most two children, referred to as 6 | the left child and the right child. Each node in a BST contains a unique key and an associated value. The tree is 7 | structured so that, for any given node: 8 | 9 | 1. The left subtree contains nodes with keys less than the node's key. 10 | 2. The right subtree contains nodes with keys greater than the node's key. 11 | 12 | This property makes BSTs efficient for operations like searching, as the average time complexity for many operations is 13 | proportional to the tree's height. 14 | 15 | Note: in the following explanation a "smaller" node refers to a node with a smaller key and a "larger" node refers to a 16 | node with a larger key. 17 | 18 | ## Implementation 19 | 20 | ### BinarySearchTree Class 21 | 22 | The BinarySearchTree class is a generic implementation of a BST. It supports a variety of operations that allow 23 | interaction with the tree: 24 | 25 | - root(): Retrieve the root node of the tree. 26 | - insert(T key, V value): Insert a key-value pair into the tree. 27 | - delete(T key): Remove a key and its associated value from the tree. 28 | - search(T key): Find a node with a specified key. 29 | - predecessor(T key): Find the key of the predecessor of a specified key. 30 | - successor(T key): Find the key of the successor of a specified key. 31 | - searchMin(): Find the node with the minimum key in the tree. 32 | - searchMax(): Find the node with the maximum key in the tree. 33 | - getInorder(): Return an in-order traversal of the tree. 34 | - getPreorder(): Return a pre-order traversal of the tree. 35 | - getPostorder(): Return a post-order traversal of the tree. 36 | - getLevelorder(): Return a level-order traversal of the tree. 37 | 38 | We will expand on the delete implementation due to its relative complexity. 39 | 40 | #### Delete Implementation Details 41 | 42 | The delete operation is split into three different cases - when the node to be deleted has no children, one child or 43 | two children. 44 | 45 | **No children:** Simply delete the node. 46 | 47 | **One child:** Reassign the parent attribute of the child to the parent of the node to be deleted. This will not violate 48 | the binary search tree property as the right child will definitely be smaller than the parent of the deleted node. 49 | 50 | **Two children:** Replace the deleted node with its successor. This works because the binary search tree property is 51 | maintained: 52 | 53 | 1. the entire left subtree will definitely be smaller than the successor as the successor is larger than the deleted 54 | node 55 | 2. the entire right subtree will definitely be larger than the successor as the successor will be the smallest node in 56 | the right subtree 57 | 58 | ### Node 59 | 60 | The Node class represents the nodes within the BinarySearchTree. Each Node instance contains: 61 | 62 | - key: The unique key associated with the node. 63 | - value: The value associated with the key. 64 | - left: Reference to the left child. 65 | - right: Reference to the right child. 66 | - parent: Reference to the parent node. 67 | 68 | ## Complexity Analysis 69 | 70 | **Time Complexity:** For a balanced tree, most operations (insert, delete, search) can be performed in O(log n) time, 71 | except tree traversal operations which can be performed in O(n) time. However, in the worst case (an unbalanced tree), 72 | these operations may degrade to O(n). 73 | 74 | **Space Complexity:** O(n), where n is the number of elements in the tree. 75 | -------------------------------------------------------------------------------- /src/main/java/dataStructures/disjointSet/README.md: -------------------------------------------------------------------------------- 1 | # Union Find / Disjoint Set 2 | 3 | ## Background 4 | 5 | A disjoint-set structure also known as a union-find or merge-find set, is a data structure that tracks a set of elements 6 | partitioned into a number of disjoint (non-overlapping) subsets. It is commonly used to check for connectivity 7 | (e.g. if two objects are 'grouped' together/belong to some component). 8 | 9 | In CS2040s, this is introduced in the context of checking for dynamic connectivity. For instance, Kruskal's algorithm 10 | in graph theory to find minimum spanning tree of a graph utilizes disjoint set to efficiently 11 | query if there already exists a path between 2 nodes. 12 | 13 | Generally, there are 2 main operations: 14 | 15 | 1. **Union**: Join two subsets into a single subset 16 | 2. **Find**: Determine which subset a particular element is in. In practice, this is often done to check 17 | if two elements are in the same subset or component. 18 | 19 | The Disjoint Set structure is often introduced in 3 parts, with each iteration being better than the 20 | previous either in time or space complexity (or both). More details can be found in the respective folders. 21 | Below is a brief overview: 22 | 23 | 1. **Quick Find** - Elements are assigned a component identity. 24 | Querying for connectivity and updating usually tracked with an internal array. 25 | 26 | 2. **Quick Union** - Component an element belongs to is now tracked with a tree structure. Nothing to enforce 27 | a balanced tree and hence complexity does not necessarily improve 28 | - Note, this is not implemented but details can be found under weighted union folder. 29 | 30 | 3. **Weighted Union** - Same idea of using a tree, but constructed in a way that the tree is balanced, leading to 31 | improved complexities. 32 | - Can be further augmented with path compression. 33 | 34 | ## Applications 35 | Because of its efficiency and simplicity in implementing, Disjoint Set structures are widely used in practice: 36 | 1. As mentioned, it is often used as a helper structure for Kruskal's MST algorithm 37 | 2. It can be used in the context of network connectivity 38 | - Managing a network of computers 39 | - Or even analyse social networks, finding communities and determining if two users are connected through a chain 40 | 3. Can be part of clustering algorithms to group data points based on similarity - useful for ML 41 | 4. It can be used to detect cycles in dependency graphs, e.g, software dependency management systems 42 | 1. Static analysis tools like `mypy` use this to identify circular dependencies! 43 | 5. It can be used for image processing, in labelling different connected components of an image 44 | 45 | ## Notes 46 | Disjoint Set is a data structure designed to keep track of a set of elements partitioned into a number of 47 | non-overlapping subsets. **It is not suited for handling duplicates**, so our implementation ignores duplicates. 48 | -------------------------------------------------------------------------------- /src/main/java/dataStructures/disjointSet/quickFind/DisjointSet.java: -------------------------------------------------------------------------------- 1 | package dataStructures.disjointSet.quickFind; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * Implementation of quick-find structure; Turns a list of objects into a data structure that supports union operations 10 | * Note DS structure is not suited with duplicate elements! 11 | * 12 | * @param generic type of object to be stored 13 | */ 14 | public class DisjointSet { 15 | private final Map identifier; 16 | 17 | /** 18 | * Basic constructor to create the Disjoint Set data structure. 19 | */ 20 | public DisjointSet() { 21 | identifier = new HashMap<>(); 22 | } 23 | 24 | /** 25 | * Constructor to initialize Disjoint Set with a known list of objects. 26 | * @param objects 27 | */ 28 | public DisjointSet(List objects) { 29 | identifier = new HashMap<>(); 30 | int size = objects.size(); 31 | for (int i = 0; i < size; i++) { 32 | // internally, component identity is tracked with integers 33 | identifier.put(objects.get(i), identifier.size()); // each obj initialize with a unique identity using size; 34 | } 35 | } 36 | 37 | /** 38 | * Constructor to initialize Disjoint Set with a known array of objects. 39 | * @param objects 40 | */ 41 | public DisjointSet(T[] objects) { 42 | identifier = new HashMap<>(); 43 | int size = objects.length; 44 | for (int i = 0; i < size; i++) { 45 | // internally, component identity is tracked with integers 46 | identifier.put(objects[i], identifier.size()); // each obj initialize with a unique identity using size; 47 | } 48 | } 49 | 50 | public int size() { 51 | return identifier.size(); 52 | } 53 | 54 | /** 55 | * Adds an object into the structure. 56 | * @param obj 57 | */ 58 | public void add(T obj) { 59 | identifier.put(obj, identifier.size()); 60 | } 61 | 62 | /** 63 | * Checks if object a and object b are in the same component. 64 | * @param a 65 | * @param b 66 | * @return a boolean value 67 | */ 68 | public boolean find(T a, T b) { 69 | if (!identifier.containsKey(a) || !identifier.containsKey(b)) { // key(s) does not even exist 70 | return false; 71 | } 72 | return identifier.get(a).equals(identifier.get(b)); 73 | } 74 | 75 | /** 76 | * Merge the components of object a and object b. 77 | * @param a 78 | * @param b 79 | */ 80 | public void union(T a, T b) { 81 | if (!identifier.containsKey(a) || !identifier.containsKey(b)) { // key(s) does not even exist; do nothing 82 | return; 83 | } 84 | 85 | if (identifier.get(a).equals(identifier.get(b))) { // already same; do nothing 86 | return; 87 | } 88 | 89 | int compOfA = identifier.get(a); 90 | int compOfB = identifier.get(b); 91 | for (T obj : identifier.keySet()) { 92 | if (identifier.get(obj).equals(compOfA)) { 93 | identifier.put(obj, compOfB); 94 | } 95 | } 96 | } 97 | 98 | /** 99 | * Retrieves all elements that are in the same component as the specified object. Not a typical operation 100 | * but here to illustrate other use case. 101 | * @param a 102 | * @return a list of objects 103 | */ 104 | public List retrieveFromSameComponent(T a) { 105 | List ret = new ArrayList<>(); 106 | for (T obj : identifier.keySet()) { 107 | if (find(a, obj)) { 108 | ret.add(obj); 109 | } 110 | } 111 | return ret; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/dataStructures/disjointSet/quickFind/README.md: -------------------------------------------------------------------------------- 1 | # Quick Find 2 | 3 | ## Background 4 | Every object will be assigned a component identity. The implementation of Quick Find often involves 5 | an underlying array or hash map that tracks the component identity of each object. 6 | Our implementation uses a hash map (to easily handle the case when objects aren't integers). 7 | 8 |

9 | 10 |
11 | Credits: CS2040s Lecture Slides 12 |
13 | 14 | ### Union 15 | Between the two components, decide on the component d, to represent the combined set. Let the other 16 | component's identity be d'. Simply iterate over the component identifier array / map, and for any element with 17 | identity d', assign it to d. 18 | 19 | ### Find 20 | Simply use the component identifier array to query for the component identity of the two elements 21 | and check if they are equal. This is why this implementation is known as "Quick Find". 22 | 23 | ## Complexity Analysis 24 | Let n be the number of elements in consideration. 25 | 26 | **Time**: 27 | - Union: O(n) 28 | - Find: O(1) 29 | 30 | **Space**: O(n) auxiliary space for the component identifier 31 | 32 | ## Notes -------------------------------------------------------------------------------- /src/main/java/dataStructures/lruCache/LRU.java: -------------------------------------------------------------------------------- 1 | package dataStructures.lruCache; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * Implementation of Least Recently Used (LRU) Cache 7 | * 8 | * @param generic type of key to be stored 9 | * @param generic type of associated value corresponding to a given key 10 | * Constructor: 11 | * LRU(int capacity) 12 | * Client methods: 13 | * get(K key) 14 | * put(K key, V value) 15 | * Both methods above run in expected O(1) time complexity 16 | */ 17 | class LRU { 18 | /** 19 | * Helper node class that implements doubly linked list 20 | */ 21 | private class DoublyLinkedListNode { 22 | private K key; 23 | private V val; 24 | private DoublyLinkedListNode next; 25 | private DoublyLinkedListNode prev; 26 | } 27 | 28 | private DoublyLinkedListNode dllHead; 29 | private DoublyLinkedListNode dllTail; 30 | private HashMap> keyToNode = new HashMap<>(); 31 | private int capacity; 32 | private int lengthOfList = 0; 33 | 34 | /** 35 | * Constructs an instance of Least Recently Used Cache 36 | * 37 | * @param capacity the maximum capacity of the cache 38 | */ 39 | public LRU(int capacity) { 40 | this.capacity = capacity; 41 | 42 | dllHead = new DoublyLinkedListNode<>(); 43 | dllTail = new DoublyLinkedListNode<>(); 44 | dllHead.next = dllTail; 45 | dllTail.prev = dllHead; 46 | } 47 | 48 | /** 49 | * Return the value of the key if it exists or return null 50 | * 51 | * @param key key of the value to be obtained from LRU cache 52 | */ 53 | public V get(K key) { 54 | if (!keyToNode.containsKey(key)) { 55 | return null; 56 | } 57 | 58 | DoublyLinkedListNode temp = keyToNode.get(key); 59 | temp.prev.next = temp.next; 60 | temp.next.prev = temp.prev; 61 | 62 | temp.next = dllHead.next; 63 | dllHead.next.prev = temp; 64 | temp.prev = dllHead; 65 | dllHead.next = temp; 66 | 67 | return keyToNode.get(key).val; 68 | } 69 | 70 | /** 71 | * Insert key-value pair to LRU cache 72 | * 73 | * @param key key of the value to be inserted to LRU cache 74 | * @param value value to be inserted to LRU cache 75 | */ 76 | public void put(K key, V value) { 77 | boolean addingNewNode = true; 78 | 79 | DoublyLinkedListNode newlyCached; 80 | 81 | if (!keyToNode.containsKey(key)) { 82 | newlyCached = new DoublyLinkedListNode<>(); 83 | newlyCached.key = key; 84 | newlyCached.val = value; 85 | keyToNode.put(key, newlyCached); 86 | } else { 87 | newlyCached = keyToNode.get(key); 88 | newlyCached.val = value; 89 | addingNewNode = false; 90 | 91 | newlyCached.prev.next = newlyCached.next; 92 | newlyCached.next.prev = newlyCached.prev; 93 | } 94 | 95 | newlyCached.next = dllHead.next; 96 | dllHead.next.prev = newlyCached; 97 | newlyCached.prev = dllHead; 98 | dllHead.next = newlyCached; 99 | 100 | if (addingNewNode) { 101 | if (lengthOfList == capacity) { 102 | keyToNode.remove(dllTail.prev.key); 103 | dllTail.prev.prev.next = dllTail; 104 | dllTail.prev = dllTail.prev.prev; 105 | } else { 106 | lengthOfList++; 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/dataStructures/queue/Deque/README.md: -------------------------------------------------------------------------------- 1 | # Double Ended Queue (Deque) 2 | 3 | ![Deque](https://media.geeksforgeeks.org/wp-content/uploads/anod.png) 4 | 5 | *Source: GeeksForGeeks* 6 | 7 | Deque is a variant of queue where elements can be removed or added from the head and tail of the queue. 8 | Deque could come in handy when trying to solve sliding window problems. This means it neither follows a fixed FIFO 9 | or LIFO order but rather can utilise either orders flexibly. 10 | 11 | A deque can be implemented in multiple ways, using doubly linked lists, arrays or two stacks. 12 | 13 | ## Analysis 14 | 15 | Much like a queue, deque operations involves the head / tail, resulting in *O(1)* complexity for most operations. 16 | 17 | ## Notes 18 | 19 | Just like a queue, a monotonic deque could also be created to solve more specific sliding window problems. 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/dataStructures/queue/Queue.java: -------------------------------------------------------------------------------- 1 | package dataStructures.queue; 2 | 3 | /** 4 | * Implementation of a queue structure using LinkedList. 5 | * Node class to be used as nodes for the LinkedList 6 | * is documented at the bottom. 7 | *

8 | * Callable methods / commonly supported ops: 9 | * size() 10 | * isEmpty() 11 | * enqueue(T item) - java's offer() / add() equivalent 12 | * dequeue() - java's poll() / remove() equivalent 13 | * peek() 14 | *

15 | * Note: calling dequeue() or peek() on an empty queue 16 | * returns null 17 | * 18 | * @param generic type of object to be stored in the queue 19 | */ 20 | public class Queue { 21 | private Node first; 22 | private Node last; 23 | private int size; 24 | 25 | /** 26 | * Constructor to initialize an empty queue. 27 | */ 28 | public Queue() { 29 | first = last = null; 30 | size = 0; 31 | } 32 | 33 | /** 34 | * Gets the size of the queue. 35 | * 36 | * @return size of queue 37 | */ 38 | public int size() { 39 | return this.size; 40 | } 41 | 42 | /* 43 | * Checks if queue is empty. 44 | * @return boolean value 45 | */ 46 | public boolean isEmpty() { 47 | return this.size == 0; 48 | } 49 | 50 | /** 51 | * Add an element to the end of the queue. 52 | * Note: java's equivalent methods are offer() / add(). 53 | * 54 | * @param item item to be pushed 55 | */ 56 | public void enqueue(T item) { 57 | Node toInsert = new Node<>(item); 58 | if (this.isEmpty()) { 59 | this.first = this.last = toInsert; 60 | } else { 61 | this.last.next = toInsert; 62 | this.last = toInsert; 63 | } 64 | this.size++; 65 | } 66 | 67 | /** 68 | * Remove an element from the start of the queue. 69 | * Note: java's equivalent methods are poll() / remove(). 70 | * 71 | * @return head of the queue; null is empty 72 | */ 73 | public T dequeue() { 74 | if (this.isEmpty()) { 75 | return null; 76 | } 77 | this.size--; 78 | Node removed = this.first; 79 | this.first = this.first.next; 80 | return removed.val; 81 | } 82 | 83 | /** 84 | * Display the element at the head of the queue. 85 | * 86 | * @return first item of the queue; null is queue is empty 87 | */ 88 | public T peek() { 89 | if (this.isEmpty()) { 90 | return null; 91 | } 92 | return this.first.val; 93 | } 94 | 95 | /** 96 | * Node class to wrap the object. 97 | * 98 | * @param generic type of object to be stored in the queue 99 | */ 100 | private static class Node { 101 | private final T val; 102 | private Node next; 103 | 104 | private Node(T val) { 105 | this.val = val; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/dataStructures/queue/README.md: -------------------------------------------------------------------------------- 1 | # Queue 2 | 3 | ## Background 4 | A queue is a linear data structure that restricts the order in which operations can be performed on its elements. 5 | 6 | ### Operation Orders 7 | 8 | ![Queue](https://media.geeksforgeeks.org/wp-content/cdn-uploads/20221213113312/Queue-Data-Structures.png) 9 | 10 | *Source: GeeksForGeeks* 11 | 12 | Queue follows a FIFO, first in first out order. 13 | This means the earliest element 14 | added to the stack is the one operations are conducted on first. 15 | 16 | A [stack](../stack/README.md) is a queue with operations conducted in an opposite manner. 17 | 18 | ## Analysis 19 | 20 | As a queue only interacts with either the first or last element regardless during its operations, 21 | it only needs to keep the pointers of the two element at hand, which is constantly updated as more 22 | elements are removed / added. This allows queue operations to only incur a *O(1)* time complexity. 23 | 24 | ## Notes 25 | 26 | ### Stack vs Queues 27 | 28 | Stack and queues only differ in terms of operation order, you should aim to use a stack when 29 | you want the most recent elements to be operated on. 30 | Some situations where a stack would work well include build redo / undo systems and backtracking problems. 31 | 32 | On the other hand, a queue allows you to operate on elements that enter first. Some situations where 33 | this would be useful include Breadth First Search algorithms and task / resource allocation systems. 34 | 35 | ### Arrays vs Linked List 36 | 37 | It is worth noting that queues can be implemented with either a array or with a [linked list](../linkedList/README.md). 38 | In the context of ordered operations, the lookup is only restricted to the first element. 39 | 40 | Hence, there is not much advantage in using a array, which only has a better lookup speed (*O(1)* time complexity) 41 | to implement a queue. Especially when using a linked list to construct your queue 42 | would allow you to grow or shrink the queue as you wish. 43 | -------------------------------------------------------------------------------- /src/main/java/dataStructures/queue/monotonicQueue/MonotonicQueue.java: -------------------------------------------------------------------------------- 1 | package dataStructures.queue; 2 | 3 | 4 | import java.util.ArrayDeque; 5 | import java.util.Deque; 6 | 7 | /** 8 | * Implementation of a non-increasing (decreasing) monotonic queue 9 | * for certain (but common) use case: 10 | * When larger objects pushed to the queue are able to replace and represent 11 | * smaller objects that come before it. 12 | * Callable methods are: 13 | * isEmpty() 14 | * max() 15 | * pop() 16 | * push() 17 | *

18 | * Movement within the monotonic-queue: 19 | * index v Increasing queue Decreasing queue 20 | * 1 5 [5] [5] 21 | * 2 3 [3] 3 kick out 5 [5, 3] #3->5 22 | * 3 1 [1] 1 kick out 3 [5, 3, 1] #1->3 23 | * 4 2 [1, 2] #2->1 [5, 3, 2] 2 kick out 1 #2->3 24 | * 5 4 [1, 2, 4] #4->2 [5,4] 4 kick out 2, 3 #4->2 25 | * 26 | * @param generic type for objects to be stored or queried 27 | */ 28 | 29 | public class MonotonicQueue> { 30 | private final Deque dq = new ArrayDeque<>(); // or LinkedList 31 | 32 | /** 33 | * Checks if queue is empty. 34 | * 35 | * @return boolean 36 | */ 37 | public boolean isEmpty() { 38 | return dq.isEmpty(); 39 | } 40 | 41 | /** 42 | * Push an object into the queue and maintain the non-increasing property. 43 | * 44 | * @param obj to be inserted. 45 | */ 46 | public void push(T obj) { 47 | Integer count = 0; 48 | while (!dq.isEmpty() && obj.compareTo(dq.peekLast()) > 0) { 49 | dq.pollLast(); // Removes elements that do not conform the non-increasing sequence. 50 | } 51 | dq.offerLast(obj); 52 | } 53 | 54 | /** 55 | * Returns the highest value stored in the queue. 56 | * 57 | * @return the first value of the queue. 58 | */ 59 | public T max() { 60 | if (isEmpty()) { 61 | return null; 62 | } 63 | return dq.peek(); 64 | } 65 | 66 | /** 67 | * Returns the highest valued object in the queue; i.e the first object; 68 | * Removal will only be done all representation of the object has been accounted for. 69 | * 70 | * @return The first value in the deque. (Highest valued object) 71 | */ 72 | public T pop() { 73 | if (dq.isEmpty()) { 74 | return null; 75 | } 76 | return dq.poll(); // Returns & remove head of deque 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/dataStructures/queue/monotonicQueue/README.md: -------------------------------------------------------------------------------- 1 | # Monotonic Queue 2 | 3 | This is a variant of queue where elements within the queue are either strictly increasing or decreasing. 4 | Monotonic queues are often implemented with a deque. 5 | 6 | Within a increasing monotonic queue, any element that is smaller than the current minimum is removed. 7 | Within a decreasing monotonic queue, any element that is larger than the current maximum is removed. 8 | 9 | It is worth mentioning that the most elements added to the monotonic queue would always be in a 10 | increasing / decreasing order, 11 | hence, we only need to compare down the monotonic queue from the back when adding new elements. 12 | 13 | ## Operation Orders 14 | 15 | Just like a queue, a monotonic queue mainly has *O(1)* operations, 16 | which is unique given that it ensures a certain order, which usually incurs operations of a higher complexity. 17 | -------------------------------------------------------------------------------- /src/main/java/dataStructures/stack/README.md: -------------------------------------------------------------------------------- 1 | # Stack 2 | 3 | ## Background 4 | Stack is a linear data structure that restricts 5 | the order in which operations can be performed on its elements. 6 | 7 | ![Stack data structure](https://cdn.programiz.com/sites/tutorial2program/files/stack.png) 8 | 9 | *Source: Programiz* 10 | 11 | ### Operation Orders 12 | 13 | Stack follows a LIFO, last in first out order. 14 | This means the most recent element 15 | added to the stack is the one operations are conducted on first. 16 | 17 | A [queue](../queue/README.md) conducts operations in the opposite order. 18 | 19 | ## Analysis 20 | 21 | As a stack only interacts with the most recent element regardless of operation, 22 | it keeps the pointer of the most recent element at hand, which is constantly updated as more 23 | elements are removed / added. This allows stack operations to only incur a *O(1)* time complexity. 24 | 25 | ## Notes 26 | 27 | ### Stack vs Queues 28 | 29 | Stack and queues only differ in terms of operation order, you should aim to use a stack when 30 | you want the most recent elements to be operated on. 31 | Some situations where a stack would work well include build redo / undo systems and backtracking problems. 32 | 33 | On the other hand, a queue allows you to operate on elements that enter first. Some situations where 34 | this would be useful include Breadth First Search algorithms and task / resource allocation systems. 35 | 36 | ### Arrays vs Linked List 37 | 38 | It is worth noting that stacks can be implemented with either a array or with a [linked list](../linkedList/README.md). 39 | In the context of ordered operations, the lookup is only restricted to the first element. 40 | 41 | Hence, there is not much advantage in using a array, which only has a better lookup speed (*O(1)* time complexity) 42 | to implement a stack. Especially when using a linked list to construct your stack 43 | would allow you to grow or shrink the stack as you wish. 44 | -------------------------------------------------------------------------------- /src/main/java/dataStructures/stack/Stack.java: -------------------------------------------------------------------------------- 1 | package dataStructures.stack; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | /** 8 | * Simple implementation of a stack using ArrayList. 9 | * Note: ArrayList can be implemented using LinkedList 10 | * and hence the code below can be replicated with 11 | * LinkedList. 12 | * Callable methods: 13 | * push(T item) 14 | * pop() 15 | * peek() 16 | * isEmpty() 17 | * Note: Calling pop() or peek() on an empty stack returns null 18 | * 19 | * @param generic type of objects to be stored in the stack 20 | */ 21 | public class Stack { 22 | private final List stack; 23 | 24 | /** 25 | * Constructor to create an empty stack 26 | */ 27 | public Stack() { 28 | stack = new ArrayList<>(); 29 | } 30 | 31 | /** 32 | * Constructor to create a stack with a given list holding the same type 33 | */ 34 | public Stack(List lst) { 35 | stack = lst; 36 | } 37 | 38 | /** 39 | * Constructor to create a stack given a sequence of objects of the same generic type 40 | */ 41 | public Stack(T... lst) { 42 | stack = new ArrayList(); 43 | Collections.addAll(stack, lst); 44 | } 45 | 46 | /** 47 | * @param item element to be pushed as the last element into the stack 48 | */ 49 | public void push(T item) { 50 | stack.add(item); 51 | } 52 | 53 | /** 54 | * Removes the last element from the stack. 55 | * 56 | * @return last element of the stack; null if the stack is empty 57 | */ 58 | public T pop() { 59 | if (this.isEmpty()) { 60 | return null; 61 | } 62 | // note that the operation below is O(1) when called on the last index 63 | return stack.remove(stack.size() - 1); 64 | } 65 | 66 | /** 67 | * Displays the last element of the stack. 68 | * 69 | * @return last element of the stack; null if the stack is empty 70 | */ 71 | public T peek() { 72 | if (this.isEmpty()) { 73 | return null; 74 | } 75 | // similarly, below is an O(1) operation 76 | return stack.get(stack.size() - 1); 77 | } 78 | 79 | /** 80 | * Checks if the stack is empty. 81 | * 82 | * @return boolean value representing whether the stack is empty 83 | */ 84 | public boolean isEmpty() { 85 | return stack.isEmpty(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/algorithms/binarySearch/BinarySearchTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.binarySearch; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | import algorithms.binarySearch.binarySearch.BinarySearch; 8 | import algorithms.binarySearch.binarySearchTemplated.BinarySearchTemplated; 9 | 10 | public class BinarySearchTest { 11 | @Test 12 | public void test_binarySearch() { 13 | // Test 1: even number of elements 14 | int[] firstArray = {1, 5, 10, 12}; 15 | int firstResult = BinarySearch.search(firstArray, 1); 16 | 17 | // Test 2: odd number of elements 18 | int[] secondArray = {1, 5, 10, 11, 12}; 19 | int secondResult = BinarySearch.search(secondArray, 11); 20 | 21 | // Test 3: target not in array 22 | int[] thirdArray = {1, 5, 10, 11, 12}; 23 | int thirdResult = BinarySearch.search(thirdArray, 3); 24 | 25 | assertEquals(0, firstResult); 26 | assertEquals(3, secondResult); 27 | assertEquals(-1, thirdResult); 28 | } 29 | 30 | @Test 31 | public void test_binarySearchTemplated() { 32 | // Test 1: even number of elements 33 | int[] firstArray = {1, 5, 10, 12}; 34 | int firstResult = BinarySearchTemplated.search(firstArray, 1); 35 | 36 | // Test 2: odd number of elements 37 | int[] secondArray = {1, 5, 10, 11, 12}; 38 | int secondResult = BinarySearchTemplated.search(secondArray, 11); 39 | 40 | // Test 3: target not in array but could exist within search space 41 | int[] thirdArray = {1, 5, 10, 11, 12}; 42 | int thirdResult = BinarySearchTemplated.search(thirdArray, 3); 43 | 44 | // Test 4: target not in array but could exist on the right of search space 45 | int[] fourthArray = {1, 5, 10, 11, 12}; 46 | int fourthResult = BinarySearchTemplated.search(thirdArray, 13); 47 | 48 | // Test 3: target not in array but could exist on the left of search space 49 | int[] fifthArray = {1, 5, 10, 11, 12}; 50 | int fifthResult = BinarySearchTemplated.search(thirdArray, 0); 51 | 52 | assertEquals(0, firstResult); 53 | assertEquals(3, secondResult); 54 | assertEquals(-1, thirdResult); 55 | assertEquals(-1, fourthResult); 56 | assertEquals(-1, fifthResult); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/algorithms/minimumSpanningTree/kruskal/KruskalTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.minimumSpanningTree.kruskal; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import org.junit.Test; 6 | 7 | public class KruskalTest { 8 | @Test 9 | public void test_simpleGraph() { 10 | // Graph setup (Adjacency Matrix) 11 | // B 12 | // / \ 13 | // 1 1 14 | // / \ 15 | // A - 1 - C 16 | int[][] adjacencyMatrix = { 17 | {0, 1, 1}, // A: A-B, A-C 18 | {1, 0, 1}, // B: B-A, B-C 19 | {1, 1, 0} // C: C-A, C-B 20 | }; 21 | 22 | Kruskal.Node[] nodes = { 23 | new Kruskal.Node("A", 0), 24 | new Kruskal.Node("B", 1), 25 | new Kruskal.Node("C", 2) 26 | }; 27 | 28 | // Run Kruskal's algorithm 29 | int[][] actualMST = Kruskal.getKruskalMST(nodes, adjacencyMatrix); 30 | 31 | // Expected MST 32 | // A -1- B -1- C 33 | int[][] expectedMST = { 34 | {0, 1, 1}, // A: A-B, A-C 35 | {1, 0, Integer.MAX_VALUE}, // B: B-A 36 | {1, Integer.MAX_VALUE, 0} // C: C-A 37 | }; 38 | 39 | // Assertion 40 | assertArrayEquals(expectedMST, actualMST); 41 | } 42 | 43 | @Test 44 | public void test_complexGraph() { 45 | // Graph setup 46 | // A 47 | // / | \ 48 | // 1 4 3 49 | /// | \ 50 | //B --3-- D 51 | // \ | / 52 | // 2 4 1 53 | // \|/ 54 | // C 55 | int[][] adjacencyMatrix = { 56 | {0, 1, 4, 3}, // A: A-B, A-C, A-D 57 | {1, 0, 2, 3}, // B: B-A, B-C, B-D 58 | {4, 2, 0, 1}, // C: C-A, C-B, C-D 59 | {3, 3, 1, 0} // D: D-A, D-B, D-C 60 | }; 61 | 62 | Kruskal.Node[] nodes = { 63 | new Kruskal.Node("A", 0), 64 | new Kruskal.Node("B", 1), 65 | new Kruskal.Node("C", 2), 66 | new Kruskal.Node("D", 3) 67 | }; 68 | 69 | // Run Prim's algorithm 70 | int[][] actualMST = Kruskal.getKruskalMST(nodes, adjacencyMatrix); 71 | 72 | // Expected MST 73 | // Based on the graph, assuming the MST is correctly computed 74 | int[][] expectedMST = { 75 | {0, 1, Integer.MAX_VALUE, Integer.MAX_VALUE}, // A: A-B 76 | {1, 0, 2, Integer.MAX_VALUE}, // B: B-A, B-C 77 | {Integer.MAX_VALUE, 2, 0, 1}, // C: C-B, C-D 78 | {Integer.MAX_VALUE, Integer.MAX_VALUE, 1, 0} // D: D-C 79 | }; 80 | 81 | // Assertion 82 | assertArrayEquals(expectedMST, actualMST); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/algorithms/minimumSpanningTree/prim/PrimTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.minimumSpanningTree.prim; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import org.junit.Test; 6 | 7 | public class PrimTest { 8 | 9 | @Test 10 | public void test_simpleGraph() { 11 | // Graph setup (Adjacency Matrix) 12 | // B 13 | // / \ 14 | // 1 1 15 | // / \ 16 | // A - 1 - C 17 | int[][] adjacencyMatrix = { 18 | {0, 1, 1}, // A: A-B, A-C 19 | {1, 0, 1}, // B: B-A, B-C 20 | {1, 1, 0} // C: C-A, C-B 21 | }; 22 | 23 | Prim.Node[] nodes = { 24 | new Prim.Node("A", 0), 25 | new Prim.Node("B", 1), 26 | new Prim.Node("C", 2) 27 | }; 28 | 29 | // Run Prim's algorithm 30 | int[][] actualMST = Prim.getPrimsMST(nodes, adjacencyMatrix); 31 | 32 | // Expected MST 33 | // A -1- B -1- C 34 | int[][] expectedMST = { 35 | {0, 1, Integer.MAX_VALUE}, // A: A-B 36 | {1, 0, 1}, // B: B-A, B-C 37 | {Integer.MAX_VALUE, 1, 0} // C: C-B 38 | }; 39 | 40 | // Assertion 41 | assertArrayEquals(expectedMST, actualMST); 42 | } 43 | 44 | @Test 45 | public void test_complexGraph() { 46 | // Graph setup 47 | // A 48 | // / | \ 49 | // 1 4 3 50 | /// | \ 51 | //B --3-- D 52 | // \ | / 53 | // 2 4 1 54 | // \|/ 55 | // C 56 | int[][] adjacencyMatrix = { 57 | {0, 1, 4, 3}, // A: A-B, A-C, A-D 58 | {1, 0, 2, 3}, // B: B-A, B-C, B-D 59 | {4, 2, 0, 1}, // C: C-A, C-B, C-D 60 | {3, 3, 1, 0} // D: D-A, D-B, D-C 61 | }; 62 | 63 | Prim.Node[] nodes = { 64 | new Prim.Node("A", 0), 65 | new Prim.Node("B", 1), 66 | new Prim.Node("C", 2), 67 | new Prim.Node("D", 3) 68 | }; 69 | 70 | // Run Prim's algorithm 71 | int[][] actualMST = Prim.getPrimsMST(nodes, adjacencyMatrix); 72 | 73 | // Expected MST 74 | // Based on the graph, assuming the MST is correctly computed 75 | int[][] expectedMST = { 76 | {0, 1, Integer.MAX_VALUE, Integer.MAX_VALUE}, // A: A-B 77 | {1, 0, 2, Integer.MAX_VALUE}, // B: B-A, B-C 78 | {Integer.MAX_VALUE, 2, 0, 1}, // C: C-B, C-D 79 | {Integer.MAX_VALUE, Integer.MAX_VALUE, 1, 0} // D: D-C 80 | }; 81 | 82 | // Assertion 83 | assertArrayEquals(expectedMST, actualMST); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/algorithms/orthogonalRangeSearching/oneDim/OrthogonalRangeSearchingTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.orthogonalRangeSearching.oneDim; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import org.junit.Test; 6 | 7 | import algorithms.orthogonalRangeSearching.RangeTreeNode; 8 | 9 | public class OrthogonalRangeSearchingTest { 10 | @Test 11 | public void test_orthogonalRangeSearching() { 12 | 13 | int[] firstInput = new int[]{7, 12, 25, 26, 40, 45}; 14 | Object[] firstExpected = new Object[]{12, 25, 26, 40, 45}; 15 | RangeTreeNode firstTree = OrthogonalRangeSearching.buildTree(firstInput, 0, firstInput.length - 1); 16 | Object[] firstResult = OrthogonalRangeSearching.search(firstTree, 10, 50); 17 | assertArrayEquals(firstExpected, firstResult); 18 | 19 | int[] secondInput = new int[]{-26, -7, -10, -40, -43}; 20 | Object[] secondExpected = new Object[]{-26, -10, -7}; 21 | RangeTreeNode secondTree = OrthogonalRangeSearching.buildTree(secondInput, 0, secondInput.length - 1); 22 | Object[] secondResult = OrthogonalRangeSearching.search(secondTree, -30, -2); 23 | assertArrayEquals(secondExpected, secondResult); 24 | 25 | int[] thirdInput = new int[]{26, 7, 12, 40, 45}; 26 | Object[] thirdExpected = new Object[]{12, 26}; 27 | RangeTreeNode thirdTree = OrthogonalRangeSearching.buildTree(thirdInput, 0, thirdInput.length - 1); 28 | Object[] thirdResult = OrthogonalRangeSearching.search(thirdTree, 10, 35); 29 | assertArrayEquals(thirdExpected, thirdResult); 30 | 31 | // for fourth input 32 | // static queries 33 | int[] fourthInput = new int[]{3, 19, 30, 49, 59, 70, 89, 100}; 34 | Object[] fourthExpected = new Object[]{30, 49, 59, 70}; 35 | RangeTreeNode fourthTree = OrthogonalRangeSearching.buildTree(fourthInput, 0, fourthInput.length - 1); 36 | Object[] fourthResult = OrthogonalRangeSearching.search(fourthTree, 20, 71); 37 | assertArrayEquals(fourthExpected, fourthResult); 38 | 39 | Object[] fifthExpected = new Object[]{49, 59, 70, 89, 100}; 40 | Object[] fifthResult = OrthogonalRangeSearching.search(fourthTree, 31, 130); 41 | assertArrayEquals(fifthExpected, fifthResult); 42 | 43 | Object[] sixthExpected = new Object[]{59}; 44 | Object[] sixthResult = OrthogonalRangeSearching.search(fourthTree, 59, 59); 45 | assertArrayEquals(sixthExpected, sixthResult); 46 | 47 | // dynamic updates then query 48 | 49 | OrthogonalRangeSearching.configureTree(fourthTree); 50 | fourthTree = OrthogonalRangeSearching.insert(fourthTree, 101); 51 | Object[] seventhExpected = new Object[]{49, 59, 70, 89, 100, 101}; 52 | Object[] seventhResult = OrthogonalRangeSearching.search(fourthTree, 31, 130); 53 | assertArrayEquals(seventhExpected, seventhResult); 54 | 55 | fourthTree = OrthogonalRangeSearching.insert(fourthTree, 46); 56 | fourthTree = OrthogonalRangeSearching.insert(fourthTree, 32); 57 | Object[] eighthExpected = new Object[]{30, 32, 46, 49, 59, 70}; 58 | Object[] eighthResult = OrthogonalRangeSearching.search(fourthTree, 20, 71); 59 | assertArrayEquals(eighthExpected, eighthResult); 60 | 61 | fourthTree = OrthogonalRangeSearching.delete(fourthTree, 32); 62 | fourthTree = OrthogonalRangeSearching.delete(fourthTree, 59); 63 | Object[] ninthExpected = new Object[]{30, 46, 49, 70}; 64 | Object[] ninthResult = OrthogonalRangeSearching.search(fourthTree, 20, 72); 65 | assertArrayEquals(ninthExpected, ninthResult); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/algorithms/orthogonalRangeSearching/twoDim/OrthogonalRangeSearchingTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.orthogonalRangeSearching.twoDim; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import org.junit.Test; 9 | 10 | import algorithms.orthogonalRangeSearching.RangeTreeNode; 11 | 12 | public class OrthogonalRangeSearchingTest { 13 | @Test 14 | public void test_orthogonalRangeSearching() { 15 | 16 | ArrayList firstInput = new ArrayList<>(); 17 | firstInput.add(new Integer[]{4, 5}); 18 | firstInput.add(new Integer[]{3, 3}); 19 | firstInput.add(new Integer[]{2, 4}); 20 | firstInput.add(new Integer[]{1, 2}); 21 | firstInput.add(new Integer[]{5, 2}); 22 | firstInput.add(new Integer[]{6, 3}); 23 | firstInput.add(new Integer[]{7, 1}); 24 | firstInput.add(new Integer[]{8, 4}); 25 | 26 | ArrayList firstExpected = new ArrayList<>(); 27 | firstExpected.add(new Integer[]{1, 2}); 28 | firstExpected.add(new Integer[]{3, 3}); 29 | firstExpected.add(new Integer[]{5, 2}); 30 | firstExpected.add(new Integer[]{6, 3}); 31 | 32 | RangeTreeNode firstTree = OrthogonalRangeSearching.buildXTree(firstInput, 33 | 0, firstInput.size() - 1); 34 | List firstResult = OrthogonalRangeSearching.search(firstTree, 1, 6, 1, 3); 35 | assertArrayEquals(firstExpected.toArray(), firstResult.toArray()); 36 | 37 | ArrayList secondExpected = new ArrayList<>(); 38 | secondExpected.add(new Integer[]{6, 3}); 39 | secondExpected.add(new Integer[]{7, 1}); 40 | secondExpected.add(new Integer[]{8, 4}); 41 | List secondResult = OrthogonalRangeSearching.search(firstTree, 6, 9, 0, 4); 42 | assertArrayEquals(secondExpected.toArray(), secondResult.toArray()); 43 | 44 | ArrayList thirdExpected = new ArrayList<>(); 45 | List thirdResult = OrthogonalRangeSearching.search(firstTree, 6, 9, 2, 2); 46 | assertArrayEquals(thirdExpected.toArray(), thirdResult.toArray()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/algorithms/patternFinding/KmpTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.patternFinding; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | /** 11 | * Test cases for {@link KMP}. 12 | */ 13 | public class KmpTest { 14 | @Test 15 | public void test_findOccurrences_shouldReturnStartIndices() { 16 | String seq = "abclaabcabcabc"; 17 | String pattern = "abc"; 18 | 19 | List indices = KMP.findOccurrences(seq, pattern); 20 | List expected = Arrays.asList(0, 5, 8, 11); 21 | Assert.assertEquals(expected, indices); 22 | } 23 | 24 | @Test 25 | public void testEmptySequence_findOccurrences_shouldReturnStartIndices() { 26 | String seq = ""; 27 | String pattern = "a"; 28 | 29 | List indices = KMP.findOccurrences(seq, pattern); 30 | List expected = new ArrayList<>(); 31 | Assert.assertEquals(expected, indices); 32 | } 33 | 34 | @Test 35 | public void testNoOccurence_findOccurrences_shouldReturnStartIndices() { 36 | String seq = "abcabcabc"; 37 | String patternOne = "noway"; 38 | String patternTwo = "cbc"; 39 | 40 | List indicesOne = KMP.findOccurrences(seq, patternOne); 41 | List expectedOne = new ArrayList<>(); 42 | Assert.assertEquals(expectedOne, indicesOne); 43 | 44 | List indicesTwo = KMP.findOccurrences(seq, patternTwo); 45 | List expectedTwo = new ArrayList<>(); 46 | Assert.assertEquals(expectedTwo, indicesTwo); 47 | } 48 | 49 | @Test 50 | public void testRepeatPatternOnly_findOccurrences_shouldReturnStartIndices() { 51 | String seq = "abcabcabcabcabc"; 52 | String pattern = "abc"; 53 | 54 | List indices = KMP.findOccurrences(seq, pattern); 55 | List expected = Arrays.asList(0, 3, 6, 9, 12); 56 | Assert.assertEquals(expected, indices); 57 | } 58 | 59 | @Test 60 | public void testAllSame_findOccurrences_shouldReturnStartIndices() { 61 | String seq = "aaaaaaaaaaaaa"; 62 | String pattern = "aa"; 63 | 64 | List indices = KMP.findOccurrences(seq, pattern); 65 | List expected = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); 66 | Assert.assertEquals(expected, indices); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/bubbleSort/BubbleSortTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.bubbleSort; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | /** 10 | * Test cases for {@link BubbleSort}. 11 | */ 12 | public class BubbleSortTest { 13 | @Test 14 | public void test_bubbleSort_shouldReturnSortedArray() { 15 | int[] firstArray = 16 | new int[] { 17 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, 18 | 85, 30 19 | }; 20 | int[] firstResult = BubbleSort.sort(Arrays.copyOf(firstArray, firstArray.length)); 21 | 22 | int[] secondArray = 23 | new int[] { 24 | 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 25 | 9, 10 26 | }; 27 | int[] secondResult = BubbleSort.sort(Arrays.copyOf(secondArray, secondArray.length)); 28 | 29 | int[] thirdArray = new int[] {}; 30 | int[] thirdResult = BubbleSort.sort(Arrays.copyOf(thirdArray, thirdArray.length)); 31 | 32 | int[] fourthArray = new int[] {1}; 33 | int[] fourthResult = BubbleSort.sort(Arrays.copyOf(fourthArray, fourthArray.length)); 34 | 35 | Arrays.sort(firstArray); 36 | Arrays.sort(secondArray); 37 | Arrays.sort(thirdArray); 38 | Arrays.sort(fourthArray); 39 | 40 | assertArrayEquals(firstResult, firstArray); 41 | assertArrayEquals(secondResult, secondArray); 42 | assertArrayEquals(thirdResult, thirdArray); 43 | assertArrayEquals(fourthResult, fourthArray); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/countingSort/CountingSortTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.countingSort; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | /** 10 | * Test cases for {@link CountingSort}. 11 | */ 12 | public class CountingSortTest { 13 | 14 | @Test 15 | public void countingSort_emptyArray_shouldReturnEmptyArray() { 16 | int[] emptyArray = new int[10]; 17 | int[] result = CountingSort.sort(emptyArray); 18 | assertArrayEquals(emptyArray, result); 19 | } 20 | 21 | @Test 22 | public void test_countingSort_shouldReturnSortedArray() { 23 | int[] firstArray = 24 | new int[] { 25 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, 26 | 85, 30 27 | }; 28 | int[] firstResult = CountingSort.sort(firstArray); 29 | 30 | int[] secondArray = 31 | new int[] { 32 | 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 33 | 9, 10 34 | }; 35 | int[] secondResult = CountingSort.sort(secondArray); 36 | 37 | int[] thirdArray = new int[] {}; 38 | int[] thirdResult = CountingSort.sort(thirdArray); 39 | 40 | Arrays.sort(firstArray); // get expected result 41 | Arrays.sort(secondArray); // get expected result 42 | Arrays.sort(thirdArray); // get expected result 43 | 44 | assertArrayEquals(firstResult, firstArray); 45 | assertArrayEquals(secondResult, secondArray); 46 | assertArrayEquals(thirdResult, thirdArray); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/cyclicSort/generalised/CyclicSortTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.cyclicSort.generalised; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | /** 10 | * Test cases for generalized {@link CyclicSort}. 11 | */ 12 | public class CyclicSortTest { 13 | @Test 14 | public void cyclicSort_emptyArray_shouldReturnEmptyArray() { 15 | int[] emptyArray = new int[10]; 16 | int[] expected = new int[10]; 17 | 18 | CyclicSort.sort(emptyArray); 19 | assertArrayEquals(emptyArray, expected); 20 | } 21 | 22 | @Test 23 | public void cyclicSort_positiveInputs_shouldReturnSortedArray() { 24 | int[] array = 25 | new int[] { 26 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, 27 | 85, 30 28 | }; 29 | int[] expected = Arrays.copyOf(array, array.length); 30 | Arrays.sort(expected); 31 | 32 | CyclicSort.sort(array); 33 | assertArrayEquals(array, expected); 34 | } 35 | 36 | @Test 37 | public void cyclicSort_someNegativeInputs_shouldReturnSortedArray() { 38 | int[] array = 39 | new int[] { 40 | 2, 3, 4, 1, 2, -5, 6, 7, 10, -15, 20, 13, 15, 1, 2, 15, -12, 20, 21, 120, 11, 5, 41 | 7, -85, 30 42 | }; 43 | int[] expected = Arrays.copyOf(array, array.length); 44 | Arrays.sort(expected); 45 | 46 | CyclicSort.sort(array); 47 | assertArrayEquals(array, expected); 48 | } 49 | 50 | @Test 51 | public void cyclicSort_alreadySorted_shouldReturnSortedArray() { 52 | int[] array = 53 | new int[] {1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15}; 54 | int[] expected = Arrays.copyOf(array, array.length); 55 | Arrays.sort(expected); 56 | 57 | CyclicSort.sort(array); 58 | assertArrayEquals(array, expected); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/cyclicSort/simple/FindFirstMissingNonNegativeTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.cyclicSort.simple; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Test cases for simple {@link FindFirstMissingNonNegative}. 9 | */ 10 | public class FindFirstMissingNonNegativeTest { 11 | @Test 12 | public void findMissing_randomArray_shouldReturnFirstMissing() { 13 | int[] arrayMissing3 = {0, 7, 1, 2, 4, 5, 6, 8}; // 3 14 | assertEquals(FindFirstMissingNonNegative.findMissing(arrayMissing3), 3); 15 | } 16 | 17 | @Test 18 | public void findMissing_duplicatesPresent_shouldReturnFirstMissing() { 19 | int[] arrayMissing5 = {1, 3, 2, 0, 2, 7, 4}; // 5 20 | assertEquals(FindFirstMissingNonNegative.findMissing(arrayMissing5), 5); 21 | } 22 | 23 | @Test 24 | public void findMissing_firstNthPresent_shouldReturnFirstMissing() { 25 | int[] arrayMissing7 = {0, 1, 2, 3, 4, 5, 6}; // 7 26 | assertEquals(FindFirstMissingNonNegative.findMissing(arrayMissing7), 7); 27 | } 28 | 29 | @Test 30 | public void findMissing_allNegatives_shouldReturnZero() { 31 | int[] arrayAllNegatives = {-5, -10, -25, -500, -1}; // 0 32 | assertEquals(FindFirstMissingNonNegative.findMissing(arrayAllNegatives), 0); 33 | } 34 | 35 | @Test 36 | public void findMissing_allPositives_shouldReturnZero() { 37 | int[] arrayAllPositives = {5, 10, 25, 500, 1}; // 0 38 | assertEquals(FindFirstMissingNonNegative.findMissing(arrayAllPositives), 0); 39 | } 40 | 41 | @Test 42 | public void findMissing_empty_shouldReturnZero() { 43 | int[] empty = {}; 44 | assertEquals(FindFirstMissingNonNegative.findMissing(empty), 0); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/cyclicSort/simple/SimpleCyclicSortTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.cyclicSort.simple; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Test cases for Simple {@link CyclicSort}. 9 | */ 10 | public class SimpleCyclicSortTest { 11 | @Test 12 | public void test_cyclicSort_shouldReturnSortedArray() { 13 | // unsorted array of 16 integers, from 0 to 15 14 | int[] firstArray = 15 | new int[] {2, 5, 6, 4, 8, 15, 3, 1, 7, 12, 14, 0, 9, 13, 11, 10}; 16 | int[] firstExpected = 17 | new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; 18 | 19 | // already sorted, do nothing 20 | int[] secondArray = 21 | new int[] {0, 1, 2, 3, 4, 5, 6}; 22 | int[] secondExpected = 23 | new int[] {0, 1, 2, 3, 4, 5, 6}; 24 | 25 | // empty, do nothing 26 | int[] thirdArray = new int[] {}; 27 | int[] thirdExpected = new int[] {}; 28 | 29 | CyclicSort.sort(firstArray); 30 | CyclicSort.sort(secondArray); 31 | CyclicSort.sort(thirdArray); 32 | 33 | assertArrayEquals(firstArray, firstExpected); 34 | assertArrayEquals(secondArray, secondExpected); 35 | assertArrayEquals(thirdArray, thirdExpected); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/insertionSort/InsertionSortTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.insertionSort; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | /** 10 | * Test cases for {@link InsertionSort}. 11 | */ 12 | public class InsertionSortTest { 13 | 14 | @Test 15 | public void test_insertionSort_shouldReturnSortedArray() { 16 | int[] firstArray = 17 | new int[] { 18 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, 19 | 85, 30 20 | }; 21 | int[] firstResult = InsertionSort.sort(Arrays.copyOf(firstArray, firstArray.length)); 22 | 23 | int[] secondArray = 24 | new int[] { 25 | 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 26 | 9, 10 27 | }; 28 | int[] secondResult = InsertionSort.sort(Arrays.copyOf(secondArray, secondArray.length)); 29 | 30 | int[] thirdArray = new int[] {}; 31 | int[] thirdResult = InsertionSort.sort(Arrays.copyOf(thirdArray, thirdArray.length)); 32 | 33 | int[] fourthArray = new int[] {1}; 34 | int[] fourthResult = InsertionSort.sort(Arrays.copyOf(fourthArray, fourthArray.length)); 35 | 36 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; 37 | int[] fifthResult = InsertionSort.sort(Arrays.copyOf(fifthArray, fifthArray.length)); 38 | 39 | Arrays.sort(firstArray); // get expected result 40 | Arrays.sort(secondArray); // get expected result 41 | Arrays.sort(thirdArray); // get expected result 42 | Arrays.sort(fourthArray); // get expected result 43 | Arrays.sort(fifthArray); // get expected result 44 | 45 | assertArrayEquals(firstResult, firstArray); 46 | assertArrayEquals(secondResult, secondArray); 47 | assertArrayEquals(thirdResult, thirdArray); 48 | assertArrayEquals(fourthResult, fourthArray); 49 | assertArrayEquals(fifthResult, fifthArray); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/mergeSort/iterative/MergeSortTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.mergeSort.iterative; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | /** 10 | * Test cases for iterative {@link MergeSort}. 11 | */ 12 | public class MergeSortTest { 13 | 14 | @Test 15 | public void test_insertionSort_shouldReturnSortedArray() { 16 | int[] firstArray = 17 | new int[] { 18 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, 19 | 85, 30 20 | }; 21 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); 22 | MergeSort.sort(firstResult); 23 | 24 | int[] secondArray = 25 | new int[] { 26 | 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 27 | 9, 10 28 | }; 29 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); 30 | MergeSort.sort(secondResult); 31 | 32 | int[] thirdArray = new int[] {}; 33 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); 34 | MergeSort.sort(thirdResult); 35 | 36 | int[] fourthArray = new int[] {1}; 37 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); 38 | MergeSort.sort(fourthResult); 39 | 40 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; 41 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); 42 | MergeSort.sort(fifthResult); 43 | 44 | Arrays.sort(firstArray); // get expected result 45 | Arrays.sort(secondArray); // get expected result 46 | Arrays.sort(thirdArray); // get expected result 47 | Arrays.sort(fourthArray); // get expected result 48 | Arrays.sort(fifthArray); // get expected result 49 | 50 | assertArrayEquals(firstResult, firstArray); 51 | assertArrayEquals(secondResult, secondArray); 52 | assertArrayEquals(thirdResult, thirdArray); 53 | assertArrayEquals(fourthResult, fourthArray); 54 | assertArrayEquals(fifthResult, fifthArray); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/mergeSort/recursive/MergeSortTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.mergeSort.recursive; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | 10 | /** 11 | * Test cases for recursive {@link MergeSort}. 12 | */ 13 | public class MergeSortTest { 14 | 15 | @Test 16 | public void test_insertionSort_shouldReturnSortedArray() { 17 | int[] firstArray = 18 | new int[] { 19 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, 20 | 85, 30 21 | }; 22 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); 23 | MergeSort.sort(firstResult); 24 | 25 | int[] secondArray = 26 | new int[] { 27 | 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 28 | 9, 10 29 | }; 30 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); 31 | MergeSort.sort(secondResult); 32 | 33 | int[] thirdArray = new int[] {}; 34 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); 35 | MergeSort.sort(thirdResult); 36 | 37 | int[] fourthArray = new int[] {1}; 38 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); 39 | MergeSort.sort(fourthResult); 40 | 41 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; 42 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); 43 | MergeSort.sort(fifthResult); 44 | 45 | Arrays.sort(firstArray); // get expected result 46 | Arrays.sort(secondArray); // get expected result 47 | Arrays.sort(thirdArray); // get expected result 48 | Arrays.sort(fourthArray); // get expected result 49 | Arrays.sort(fifthArray); // get expected result 50 | 51 | assertArrayEquals(firstResult, firstArray); 52 | assertArrayEquals(secondResult, secondArray); 53 | assertArrayEquals(thirdResult, thirdArray); 54 | assertArrayEquals(fourthResult, fourthArray); 55 | assertArrayEquals(fifthResult, fifthArray); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/quickSort/hoares/QuickSortTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.quickSort.hoares; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | /** 10 | * Test cases for {@link QuickSort} implemented using Hoares. 11 | */ 12 | public class QuickSortTest { 13 | 14 | @Test 15 | public void test_selectionSort_shouldReturnSortedArray() { 16 | int[] firstArray = 17 | new int[] {22, 1, 6, 40, 32, 10, 18, 4, 50}; 18 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); 19 | QuickSort.sort(firstResult); 20 | 21 | int[] secondArray = 22 | new int[] { 23 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, 24 | 85, 30 25 | }; 26 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); 27 | QuickSort.sort(secondResult); 28 | 29 | int[] thirdArray = 30 | new int[] { 31 | 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 32 | 9, 10 33 | }; 34 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); 35 | QuickSort.sort(thirdResult); 36 | 37 | int[] fourthArray = new int[] {}; 38 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); 39 | QuickSort.sort(fourthResult); 40 | 41 | int[] fifthArray = new int[] {1}; 42 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); 43 | QuickSort.sort(fifthResult); 44 | 45 | int[] sixthArray = new int[] {5, 1, 1, 2, 0, 0}; 46 | int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length); 47 | QuickSort.sort(sixthResult); 48 | 49 | int[] seventhArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 50 | int[] seventhResult = Arrays.copyOf(seventhArray, seventhArray.length); 51 | QuickSort.sort(seventhResult); 52 | 53 | int[] eighthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 54 | int[] eighthResult = Arrays.copyOf(eighthArray, eighthArray.length); 55 | QuickSort.sort(eighthResult); 56 | 57 | Arrays.sort(firstArray); // get expected result 58 | Arrays.sort(secondArray); // get expected result 59 | Arrays.sort(thirdArray); // get expected result 60 | Arrays.sort(fourthArray); // get expected result 61 | Arrays.sort(fifthArray); // get expected result 62 | Arrays.sort(sixthArray); // get expected result 63 | Arrays.sort(seventhArray); // get expected result 64 | Arrays.sort(eighthArray); // get expected result 65 | 66 | assertArrayEquals(firstResult, firstArray); 67 | assertArrayEquals(secondResult, secondArray); 68 | assertArrayEquals(thirdResult, thirdArray); 69 | assertArrayEquals(fourthResult, fourthArray); 70 | assertArrayEquals(fifthResult, fifthArray); 71 | assertArrayEquals(sixthResult, sixthArray); 72 | assertArrayEquals(seventhResult, seventhArray); 73 | assertArrayEquals(eighthResult, eighthArray); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/quickSort/lomuto/QuickSortTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.quickSort.lomuto; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | /** 10 | * Test cases for Lomuto {@link QuickSort}. 11 | */ 12 | public class QuickSortTest { 13 | 14 | @Test 15 | public void test_selectionSort_shouldReturnSortedArray() { 16 | int[] firstArray = 17 | new int[] { 18 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, 19 | 85, 30 20 | }; 21 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); 22 | QuickSort.sort(firstResult); 23 | 24 | int[] secondArray = 25 | new int[] { 26 | 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 27 | 9, 10 28 | }; 29 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); 30 | QuickSort.sort(secondResult); 31 | 32 | int[] thirdArray = new int[] {}; 33 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); 34 | QuickSort.sort(thirdResult); 35 | 36 | int[] fourthArray = new int[] {1}; 37 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); 38 | QuickSort.sort(fourthResult); 39 | 40 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; 41 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); 42 | QuickSort.sort(fifthResult); 43 | 44 | int[] sixthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 45 | int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length); 46 | QuickSort.sort(sixthResult); 47 | 48 | Arrays.sort(firstArray); // get expected result 49 | Arrays.sort(secondArray); // get expected result 50 | Arrays.sort(thirdArray); // get expected result 51 | Arrays.sort(fourthArray); // get expected result 52 | Arrays.sort(fifthArray); // get expected result 53 | Arrays.sort(sixthArray); // get expected result 54 | 55 | assertArrayEquals(firstResult, firstArray); 56 | assertArrayEquals(secondResult, secondArray); 57 | assertArrayEquals(thirdResult, thirdArray); 58 | assertArrayEquals(fourthResult, fourthArray); 59 | assertArrayEquals(fifthResult, fifthArray); 60 | assertArrayEquals(sixthResult, sixthArray); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/quickSort/paranoid/QuickSortTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.quickSort.paranoid; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | /** 10 | * Test cases for paranoid {@link QuickSort}. 11 | */ 12 | public class QuickSortTest { 13 | 14 | @Test 15 | public void test_selectionSort_shouldReturnSortedArray() { 16 | int[] firstArray = 17 | new int[] { 18 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, 19 | 85, 30 20 | }; 21 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); 22 | QuickSort.sort(firstResult); 23 | 24 | int[] secondArray = 25 | new int[] { 26 | 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 27 | 9, 10 28 | }; 29 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); 30 | QuickSort.sort(secondResult); 31 | 32 | int[] thirdArray = new int[] {}; 33 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); 34 | QuickSort.sort(thirdResult); 35 | 36 | int[] fourthArray = new int[] {1}; 37 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); 38 | QuickSort.sort(fourthResult); 39 | 40 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; 41 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); 42 | QuickSort.sort(fifthResult); 43 | 44 | int[] sixthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 45 | int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length); 46 | // testing for duplicate arrays of length >= 10, stack overflow is expected to happen 47 | try { 48 | QuickSort.sort(sixthResult); 49 | } catch (StackOverflowError e) { 50 | System.out.println("Stack overflow occurred for sixthResult"); 51 | } 52 | 53 | Arrays.sort(firstArray); // get expected result 54 | Arrays.sort(secondArray); // get expected result 55 | Arrays.sort(thirdArray); // get expected result 56 | Arrays.sort(fourthArray); // get expected result 57 | Arrays.sort(fifthArray); // get expected result 58 | 59 | assertArrayEquals(firstResult, firstArray); 60 | assertArrayEquals(secondResult, secondArray); 61 | assertArrayEquals(thirdResult, thirdArray); 62 | assertArrayEquals(fourthResult, fourthArray); 63 | assertArrayEquals(fifthResult, fifthArray); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/quickSort/threeWayPartitioning/QuickSortTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.quickSort.threeWayPartitioning; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | /** 10 | * Test cases for {@link QuickSort} implemented using three way partitioning. 11 | */ 12 | public class QuickSortTest { 13 | 14 | @Test 15 | public void test_selectionSort_shouldReturnSortedArray() { 16 | int[] firstArray = 17 | new int[] { 18 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, 19 | 85, 30 20 | }; 21 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); 22 | QuickSort.sort(firstResult); 23 | 24 | int[] secondArray = 25 | new int[] { 26 | 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 27 | 9, 10 28 | }; 29 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); 30 | QuickSort.sort(secondResult); 31 | 32 | int[] thirdArray = new int[] {}; 33 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); 34 | QuickSort.sort(thirdResult); 35 | 36 | int[] fourthArray = new int[] {1}; 37 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); 38 | QuickSort.sort(fourthResult); 39 | 40 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; 41 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); 42 | QuickSort.sort(fifthResult); 43 | 44 | int[] sixthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 45 | int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length); 46 | QuickSort.sort(sixthResult); 47 | 48 | Arrays.sort(firstArray); // get expected result 49 | Arrays.sort(secondArray); // get expected result 50 | Arrays.sort(thirdArray); // get expected result 51 | Arrays.sort(fourthArray); // get expected result 52 | Arrays.sort(fifthArray); // get expected result 53 | Arrays.sort(sixthArray); // get expected result 54 | 55 | assertArrayEquals(firstResult, firstArray); 56 | assertArrayEquals(secondResult, secondArray); 57 | assertArrayEquals(thirdResult, thirdArray); 58 | assertArrayEquals(fourthResult, fourthArray); 59 | assertArrayEquals(fifthResult, fifthArray); 60 | assertArrayEquals(sixthResult, sixthArray); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/radixSort/RadixSortTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.radixSort; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | public class RadixSortTest { 10 | @Test 11 | public void test_radixSort_shouldReturnSortedArray() { 12 | int[] firstArray = new int[] { 13 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 14 | 15, 12, 20, 21, 120, 11, 5, 7, 85, 30 15 | }; 16 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); 17 | RadixSort.radixSort(firstResult); 18 | 19 | int[] secondArray = new int[] { 20 | 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 21 | 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 22 | }; 23 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); 24 | RadixSort.radixSort(secondResult); 25 | 26 | int[] thirdArray = new int[] {}; 27 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); 28 | RadixSort.radixSort(thirdResult); 29 | 30 | int[] fourthArray = new int[] {1}; 31 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); 32 | RadixSort.radixSort(fourthResult); 33 | 34 | int[] fifthArray = 35 | new int[] {157394, 93495939, 495839239, 485384, 38439958, 3948585, 39585939, 6000999, 111111111, 98162}; 36 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); 37 | RadixSort.radixSort(fifthResult); 38 | 39 | Arrays.sort(firstArray); 40 | Arrays.sort(secondArray); 41 | Arrays.sort(thirdArray); 42 | Arrays.sort(fourthArray); 43 | Arrays.sort(fifthArray); 44 | 45 | assertArrayEquals(firstResult, firstArray); 46 | assertArrayEquals(secondResult, secondArray); 47 | assertArrayEquals(thirdResult, thirdArray); 48 | assertArrayEquals(fourthResult, fourthArray); 49 | assertArrayEquals(fifthResult, fifthArray); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/algorithms/sorting/selectionSort/SelectionSortTest.java: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.selectionSort; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | /** 10 | * Test cases for {@link SelectionSort}. 11 | */ 12 | public class SelectionSortTest { 13 | 14 | @Test 15 | public void test_selectionSort_shouldReturnSortedArray() { 16 | int[] firstArray = 17 | new int[] { 18 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, 19 | 85, 30 20 | }; 21 | int[] firstResult = SelectionSort.sort(Arrays.copyOf(firstArray, firstArray.length)); 22 | 23 | int[] secondArray = 24 | new int[] { 25 | 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 26 | 9, 10 27 | }; 28 | int[] secondResult = SelectionSort.sort(Arrays.copyOf(secondArray, secondArray.length)); 29 | 30 | int[] thirdArray = new int[] {}; 31 | int[] thirdResult = SelectionSort.sort(Arrays.copyOf(thirdArray, thirdArray.length)); 32 | 33 | int[] fourthArray = new int[] {1}; 34 | int[] fourthResult = SelectionSort.sort(Arrays.copyOf(fourthArray, fourthArray.length)); 35 | 36 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; 37 | int[] fifthResult = SelectionSort.sort(Arrays.copyOf(fifthArray, fifthArray.length)); 38 | 39 | Arrays.sort(firstArray); // get expected result 40 | Arrays.sort(secondArray); // get expected result 41 | Arrays.sort(thirdArray); // get expected result 42 | Arrays.sort(fourthArray); // get expected result 43 | Arrays.sort(fifthArray); // get expected result 44 | 45 | assertArrayEquals(firstResult, firstArray); 46 | assertArrayEquals(secondResult, secondArray); 47 | assertArrayEquals(thirdResult, thirdArray); 48 | assertArrayEquals(fourthResult, fourthArray); 49 | assertArrayEquals(fifthResult, fifthArray); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/dataStructures/bTree/BTreeTest.java: -------------------------------------------------------------------------------- 1 | package dataStructures.bTree; 2 | import static org.junit.Assert.assertArrayEquals; 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | public class BTreeTest { 8 | @Test 9 | public void testBTree() { 10 | BTree bTree = new BTree(3); // Specify the order of the B-tree 11 | 12 | int[] keys = {3, 7, 1, 8, 5, 12, 2, 9, 6, 10, 11, 4}; 13 | for (int key : keys) { 14 | bTree.insert(key); 15 | } 16 | 17 | Object[] expectedLevelOrderTraversal1 = {5, 8, 1, 2, 3, 4, 6, 7, 9, 10, 11, 12}; 18 | Object[] expectedPreOrderTraversal1 = {5, 8, 1, 2, 3, 4, 6, 7, 9, 10, 11, 12}; 19 | assertArrayEquals(bTree.levelOrderTraversal(), expectedLevelOrderTraversal1); 20 | assertArrayEquals(bTree.preOrderTraversal(), expectedPreOrderTraversal1); 21 | 22 | assertEquals(true, bTree.search(8)); 23 | assertEquals(true, bTree.search(4)); 24 | assertEquals(true, bTree.search(7)); 25 | assertEquals(false, bTree.search(13)); 26 | assertEquals(false, bTree.search(0)); 27 | 28 | bTree.delete(7); 29 | assertEquals(false, bTree.search(7)); 30 | 31 | Object[] expectedLevelOrderTraversal2 = {4, 8, 1, 2, 3, 5, 6, 9, 10, 11, 12}; 32 | Object[] expectedPreOrderTraversal2 = {4, 8, 1, 2, 3, 5, 6, 9, 10, 11, 12}; 33 | assertArrayEquals(bTree.levelOrderTraversal(), expectedLevelOrderTraversal2); 34 | assertArrayEquals(bTree.preOrderTraversal(), expectedPreOrderTraversal2); 35 | 36 | bTree.delete(4); 37 | assertEquals(false, bTree.search(4)); 38 | 39 | Object[] expectedLevelOrderTraversal3 = {3, 8, 1, 2, 5, 6, 9, 10, 11, 12}; 40 | Object[] expectedPreOrderTraversal3 = {3, 8, 1, 2, 5, 6, 9, 10, 11, 12}; 41 | assertArrayEquals(bTree.levelOrderTraversal(), expectedLevelOrderTraversal3); 42 | assertArrayEquals(bTree.preOrderTraversal(), expectedPreOrderTraversal3); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/dataStructures/binarySearchTree/BinarySearchTreeTest.java: -------------------------------------------------------------------------------- 1 | package dataStructures.binarySearchTree; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import org.junit.Test; 7 | 8 | public class BinarySearchTreeTest { 9 | @Test 10 | public void insert_shouldCorrectlyInsertNodes() { 11 | BinarySearchTree bst = new BinarySearchTree<>(); 12 | bst.insert(5, "Five"); 13 | bst.insert(3, "Three"); 14 | bst.insert(7, "Seven"); 15 | bst.insert(2, "Two"); 16 | bst.insert(4, "Four"); 17 | 18 | // Perform in-order traversal and get result 19 | List result = bst.getInorder(); 20 | 21 | // Expected in-order traversal result 22 | List expected = 23 | Arrays.asList("Key: 2, Value: Two", "Key: 3, Value: Three", "Key: 4, Value: Four", "Key: 5, Value: Five", 24 | "Key: 7, Value: Seven" 25 | ); 26 | 27 | assert result.equals(expected); 28 | } 29 | 30 | @Test 31 | public void delete_shouldCorrectlyHandleLeafNode() { 32 | BinarySearchTree bst = new BinarySearchTree<>(); 33 | bst.insert(5, "Five"); 34 | bst.insert(3, "Three"); 35 | bst.insert(7, "Seven"); 36 | 37 | bst.delete(7); 38 | 39 | // Perform in-order traversal and get result 40 | List result = bst.getInorder(); 41 | 42 | // Expected in-order traversal result after deletion 43 | List expected = Arrays.asList("Key: 3, Value: Three", "Key: 5, Value: Five"); 44 | 45 | assert result.equals(expected); 46 | } 47 | 48 | @Test 49 | public void delete_shouldCorrectlyHandleNodeWithOneChild() { 50 | BinarySearchTree bst = new BinarySearchTree<>(); 51 | bst.insert(5, "Five"); 52 | bst.insert(3, "Three"); 53 | bst.insert(6, "Six"); 54 | bst.insert(7, "Seven"); 55 | 56 | bst.delete(6); 57 | 58 | // Perform in-order traversal and get result 59 | List result = bst.getInorder(); 60 | 61 | // Expected in-order traversal result after deletion 62 | List expected = Arrays.asList("Key: 3, Value: Three", "Key: 5, Value: Five", "Key: 7, Value: Seven"); 63 | 64 | assert result.equals(expected); 65 | } 66 | 67 | @Test 68 | public void delete_shouldCorrectlyHandleNodeWithTwoChildren() { 69 | BinarySearchTree bst = new BinarySearchTree<>(); 70 | bst.insert(5, "Five"); 71 | bst.insert(3, "Three"); 72 | bst.insert(7, "Seven"); 73 | bst.insert(2, "Two"); 74 | bst.insert(4, "Four"); 75 | 76 | bst.delete(3); 77 | 78 | // Perform in-order traversal and get result 79 | List result = bst.getInorder(); 80 | 81 | // Expected in-order traversal result after deletion 82 | List expected = 83 | Arrays.asList("Key: 2, Value: Two", "Key: 4, Value: Four", "Key: 5, Value: Five", "Key: 7, Value: Seven"); 84 | 85 | assert result.equals(expected); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/dataStructures/disjointSet/quickFind/DisjointSetTest.java: -------------------------------------------------------------------------------- 1 | package dataStructures.disjointSet.quickFind; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | public class DisjointSetTest { 11 | @Test 12 | public void construct_shouldCorrectlyInitializeEmpty() { 13 | DisjointSet ds = new DisjointSet<>(); 14 | Assert.assertEquals(ds.size(), 0); 15 | } 16 | 17 | @Test 18 | public void construct_shouldCorrectlyInitializeNonEmpty() { 19 | List lst = Arrays.asList("andre", "chang xian", "jun neng", "kai ting", "shu heng"); 20 | 21 | DisjointSet ds = new DisjointSet<>(lst); 22 | Assert.assertEquals(ds.size(), 5); 23 | 24 | Assert.assertFalse(ds.find("andre", "kai ting")); 25 | } 26 | 27 | @Test 28 | public void construct_shouldCorrectlyInitializeNonEmptyArray() { 29 | String[] lst = new String[] { "andre", "chang xian", "jun neng", "kai ting", "shu heng" }; 30 | 31 | DisjointSet ds = new DisjointSet<>(lst); 32 | Assert.assertEquals(ds.size(), 5); 33 | 34 | Assert.assertFalse(ds.find("andre", "kai ting")); 35 | } 36 | 37 | @Test 38 | public void find_shouldCorrectlyFindItself() { 39 | List lst = Arrays.asList("andre", "chang xian", "jun neng"); 40 | 41 | DisjointSet ds = new DisjointSet<>(lst); 42 | Assert.assertTrue(ds.find("chang xian", "chang xian")); 43 | } 44 | 45 | @Test 46 | public void union_shouldCorrectlyUpdate() { 47 | List lst = Arrays.asList("andre", "chang xian", "jun neng", "kai ting", "shu heng"); 48 | 49 | DisjointSet ds = new DisjointSet<>(lst); 50 | 51 | Assert.assertFalse(ds.find("andre", "kai ting")); 52 | 53 | ds.union("andre", "kai ting"); 54 | Assert.assertTrue(ds.find("andre", "kai ting")); 55 | Assert.assertFalse(ds.find("andre", "chang xian")); 56 | Assert.assertFalse(ds.find("andre", "shu heng")); 57 | Assert.assertFalse(ds.find("jun neng", "kai ting")); 58 | } 59 | 60 | @Test 61 | public void retrieve_shouldCorrectlyRetrieveComponents() { 62 | List lst = Arrays.asList("andre", "chang xian", "jun neng", "kai ting", "shu heng", "seth", "gilbert"); 63 | 64 | DisjointSet ds = new DisjointSet<>(lst); 65 | ds.union("andre", "kai ting"); 66 | ds.union("chang xian", "jun neng"); 67 | ds.union("jun neng", "gilbert"); 68 | ds.union("chang xian", "seth"); 69 | 70 | List resultA = ds.retrieveFromSameComponent("kai ting"); 71 | Collections.sort(resultA); 72 | List expectedA = Arrays.asList("andre", "kai ting"); 73 | Collections.sort(expectedA); 74 | Assert.assertEquals(expectedA, resultA); 75 | 76 | List resultB = ds.retrieveFromSameComponent("gilbert"); 77 | Collections.sort(resultB); 78 | List expectedB = Arrays.asList("chang xian", "jun neng", "seth", "gilbert"); 79 | Collections.sort(expectedB); 80 | Assert.assertEquals(expectedB, resultB); 81 | 82 | List resultC = ds.retrieveFromSameComponent("shu heng"); 83 | Collections.sort(resultC); 84 | List expectedC = Arrays.asList("shu heng"); 85 | Collections.sort(expectedC); 86 | Assert.assertEquals(expectedC, resultC); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/test/java/dataStructures/disjointSet/weightedUnion/DisjointSetTest.java: -------------------------------------------------------------------------------- 1 | package dataStructures.disjointSet.weightedUnion; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | public class DisjointSetTest { 11 | @Test 12 | public void construct_shouldCorrectlyInitializeEmpty() { 13 | DisjointSet ds = new DisjointSet<>(); 14 | Assert.assertEquals(ds.size(), 0); 15 | } 16 | 17 | @Test 18 | public void construct_shouldCorrectlyInitializeNonEmpty() { 19 | List lst = Arrays.asList("andre", "chang xian", "jun neng", "kai ting", "shu heng"); 20 | 21 | DisjointSet ds = new DisjointSet<>(lst); 22 | Assert.assertEquals(ds.size(), 5); 23 | 24 | Assert.assertFalse(ds.find("andre", "kai ting")); 25 | } 26 | 27 | @Test 28 | public void construct_shouldCorrectlyInitializeNonEmptyArray() { 29 | String[] lst = new String[] { "andre", "chang xian", "jun neng", "kai ting", "shu heng" }; 30 | 31 | DisjointSet ds = new DisjointSet<>(lst); 32 | Assert.assertEquals(ds.size(), 5); 33 | 34 | Assert.assertFalse(ds.find("andre", "kai ting")); 35 | } 36 | 37 | @Test 38 | public void find_shouldCorrectlyFindItself() { 39 | List lst = Arrays.asList("andre", "chang xian", "jun neng"); 40 | 41 | DisjointSet ds = new DisjointSet<>(lst); 42 | Assert.assertTrue(ds.find("chang xian", "chang xian")); 43 | } 44 | 45 | @Test 46 | public void union_shouldCorrectlyUpdate() { 47 | List lst = Arrays.asList("andre", "chang xian", "jun neng", "kai ting", "shu heng"); 48 | 49 | DisjointSet ds = new DisjointSet<>(lst); 50 | 51 | Assert.assertFalse(ds.find("andre", "kai ting")); 52 | 53 | ds.union("andre", "kai ting"); 54 | Assert.assertTrue(ds.find("andre", "kai ting")); 55 | Assert.assertFalse(ds.find("andre", "chang xian")); 56 | Assert.assertFalse(ds.find("andre", "shu heng")); 57 | Assert.assertFalse(ds.find("jun neng", "kai ting")); 58 | } 59 | 60 | @Test 61 | public void retrieve_shouldCorrectlyRetrieveComponents() { 62 | List lst = Arrays.asList("andre", "chang xian", "jun neng", "kai ting", "shu heng", "seth", "gilbert"); 63 | 64 | DisjointSet ds = new DisjointSet<>(lst); 65 | ds.union("andre", "kai ting"); 66 | ds.union("chang xian", "jun neng"); 67 | ds.union("jun neng", "gilbert"); 68 | ds.union("chang xian", "seth"); 69 | 70 | List resultA = ds.retrieveFromSameComponent("kai ting"); 71 | Collections.sort(resultA); 72 | List expectedA = Arrays.asList("andre", "kai ting"); 73 | Collections.sort(expectedA); 74 | Assert.assertEquals(expectedA, resultA); 75 | 76 | List resultB = ds.retrieveFromSameComponent("gilbert"); 77 | Collections.sort(resultB); 78 | List expectedB = Arrays.asList("chang xian", "jun neng", "seth", "gilbert"); 79 | Collections.sort(expectedB); 80 | Assert.assertEquals(expectedB, resultB); 81 | 82 | List resultC = ds.retrieveFromSameComponent("shu heng"); 83 | Collections.sort(resultC); 84 | List expectedC = Arrays.asList("shu heng"); 85 | Collections.sort(expectedC); 86 | Assert.assertEquals(expectedC, resultC); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/test/java/dataStructures/hashSet/chaining/HashSetTest.java: -------------------------------------------------------------------------------- 1 | package dataStructures.hashSet.chaining; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | import java.util.stream.Stream; 10 | 11 | import org.junit.Test; 12 | 13 | /** 14 | * Test cases for {@link HashSet} implemented using Chaining to resolve collisions. 15 | */ 16 | public class HashSetTest { 17 | @Test 18 | public void testAdd_noDuplicates_shouldReturnTrue() { 19 | HashSet hashSet = new HashSet<>(); 20 | assertTrue(hashSet.add("Hello")); 21 | assertTrue(hashSet.add("World")); 22 | } 23 | 24 | @Test 25 | public void testAdd_withDuplicates_shouldReturnFalse() { 26 | HashSet hashSet = new HashSet<>(); 27 | assertTrue(hashSet.add("Hello")); 28 | assertTrue(hashSet.add("World")); 29 | assertFalse(hashSet.add("Hello")); // Adding duplicate element should return false 30 | } 31 | 32 | @Test 33 | public void testContains() { 34 | HashSet hashSet = new HashSet<>(); 35 | hashSet.add("Hello"); 36 | hashSet.add("World"); 37 | assertTrue(hashSet.contains("Hello")); 38 | assertTrue(hashSet.contains("World")); 39 | assertFalse(hashSet.contains("Universe")); // Element not in set 40 | } 41 | 42 | @Test 43 | public void testRemove() { 44 | HashSet hashSet = new HashSet<>(); 45 | hashSet.add("Hello"); 46 | hashSet.add("World"); 47 | assertTrue(hashSet.remove("Hello")); 48 | assertFalse(hashSet.contains("Hello")); // Element should be removed 49 | assertFalse(hashSet.remove("Universe")); // Removing non-existent element should return false 50 | } 51 | 52 | @Test 53 | public void testSize() { 54 | HashSet hashSet = new HashSet<>(); 55 | assertEquals(0, hashSet.size()); // Initial size should be 0 56 | hashSet.add("Hello"); 57 | assertEquals(1, hashSet.size()); // Size after adding one element 58 | hashSet.add("World"); 59 | assertEquals(2, hashSet.size()); // Size after adding two elements 60 | hashSet.remove("Hello"); 61 | assertEquals(1, hashSet.size()); // Size after removing one element 62 | } 63 | 64 | @Test 65 | public void testIsEmpty() { 66 | HashSet hashSet = new HashSet<>(); 67 | assertTrue(hashSet.isEmpty()); // Initial set should be empty 68 | hashSet.add("Hello"); 69 | assertFalse(hashSet.isEmpty()); // Set should not be empty after adding an element 70 | hashSet.remove("Hello"); 71 | assertTrue(hashSet.isEmpty()); // Set should be empty after removing the only element 72 | } 73 | 74 | @Test 75 | public void test_hashSet_shouldNotHaveDuplicates() { 76 | HashSet hashSet = new HashSet<>(); 77 | hashSet.add("Hello"); 78 | hashSet.add("World"); 79 | hashSet.add("Hello"); 80 | hashSet.add("World"); 81 | List firstList = Stream.of("Hello", "World").sorted().collect(Collectors.toList()); 82 | List firstResult = hashSet.toList().stream().sorted().collect(Collectors.toList()); 83 | 84 | assertEquals(firstList, firstResult); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/dataStructures/linkedList/LinkedListTest.java: -------------------------------------------------------------------------------- 1 | package dataStructures.linkedList; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import dataStructures.linkedList.LinkedList.Node; 7 | 8 | /** 9 | * Test cases for {@link LinkedList}. 10 | */ 11 | public class LinkedListTest { 12 | @Test 13 | public void testInsert() { 14 | LinkedList ll = new LinkedList<>(); 15 | Assert.assertNull(ll.toString()); 16 | 17 | ll.insertFront(2); 18 | ll.insertEnd(3); 19 | ll.insertEnd(5); 20 | ll.insertFront(1); 21 | Assert.assertEquals("1 2 3 5 ", ll.toString()); 22 | 23 | ll.insert(0, 0); 24 | ll.insert(4, 4); 25 | ll.insert(7, 6); 26 | ll.insert(6, 6); 27 | Assert.assertEquals("0 1 2 3 4 5 6 7 ", ll.toString()); 28 | } 29 | 30 | @Test 31 | public void testSearchAndGet() { 32 | LinkedList ll = new LinkedList<>(); 33 | ll.insertFront(2); 34 | ll.insertEnd(3); 35 | ll.insertEnd(5); 36 | ll.insertFront(1); 37 | ll.insert(0, 0); 38 | ll.insert(4, 4); 39 | ll.insert(7, 6); 40 | ll.insert(6, 6); 41 | 42 | Node test1 = ll.get(4); 43 | Assert.assertEquals("4", test1.toString()); 44 | 45 | Node test2 = ll.get(3); 46 | Assert.assertEquals("3", test2.toString()); 47 | 48 | Integer test3 = ll.search(4); 49 | Assert.assertEquals("4", test3.toString()); 50 | 51 | Integer test4 = ll.search(3); 52 | Assert.assertEquals("3", test4.toString()); 53 | 54 | Assert.assertEquals("0 1 2 3 4 5 6 7 ", ll.toString()); 55 | } 56 | 57 | @Test 58 | public void testRemove() { 59 | 60 | LinkedList ll = new LinkedList<>(); 61 | ll.insertFront(2); 62 | ll.insertEnd(3); 63 | ll.insertEnd(5); 64 | ll.insertFront(1); 65 | ll.insert(0, 0); 66 | ll.insert(4, 4); 67 | ll.insert(7, 6); 68 | ll.insert(6, 6); 69 | 70 | ll.remove(5); 71 | ll.delete(6); 72 | ll.pop(); 73 | ll.poll(); 74 | 75 | String expected = "1 2 3 4 "; 76 | Assert.assertEquals(expected, ll.toString()); 77 | } 78 | 79 | @Test 80 | public void testReverse() { 81 | LinkedList ll = new LinkedList<>(); 82 | ll.reverse(); 83 | Assert.assertNull(ll.toString()); 84 | 85 | ll.insertFront(3); 86 | ll.reverse(); 87 | Assert.assertEquals("3 ", ll.toString()); 88 | 89 | ll.insertFront(2); 90 | ll.insertFront(1); 91 | ll.insertEnd(4); 92 | ll.reverse(); 93 | Assert.assertEquals("4 3 2 1 ", ll.toString()); 94 | } 95 | 96 | @Test 97 | public void testSort() { 98 | LinkedList ll = new LinkedList<>(); 99 | ll.insertFront(5); 100 | ll.insertEnd(2); 101 | ll.insertFront(7); 102 | ll.insertEnd(3); 103 | ll.insertEnd(4); 104 | ll.insertEnd(3); 105 | ll.sort(); 106 | 107 | Assert.assertEquals("2 3 3 4 5 7 ", ll.toString()); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/dataStructures/queue/MonotonicQueueTest.java: -------------------------------------------------------------------------------- 1 | package dataStructures.queue; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * This class implements tests for the monotonic queue. 8 | */ 9 | public class MonotonicQueueTest { 10 | @Test 11 | public void testEmpty() { 12 | MonotonicQueue q = new MonotonicQueue<>(); 13 | Assert.assertTrue(q.isEmpty()); 14 | Assert.assertNull(q.max()); 15 | Assert.assertNull(q.pop()); 16 | } 17 | 18 | @Test 19 | public void testMax() { 20 | MonotonicQueue q = new MonotonicQueue<>(); 21 | q.push(2); 22 | Assert.assertEquals("2", q.max().toString()); 23 | q.push(7); 24 | Assert.assertEquals("7", q.max().toString()); 25 | q.push(1); 26 | Assert.assertEquals("7", q.max().toString()); 27 | q.push(7); 28 | Assert.assertEquals("7", q.max().toString()); 29 | q.push(5); 30 | Assert.assertEquals("7", q.max().toString()); 31 | q.push(4); 32 | Assert.assertEquals("7", q.max().toString()); 33 | q.push(3); 34 | q.push(2); 35 | q.push(5); 36 | Assert.assertEquals("7", q.max().toString()); 37 | } 38 | 39 | @Test 40 | public void testPop() { 41 | MonotonicQueue q = new MonotonicQueue<>(); 42 | q.push(2); 43 | q.push(7); 44 | q.push(1); 45 | q.push(7); 46 | q.push(5); 47 | q.push(4); 48 | q.push(3); 49 | q.push(2); 50 | q.push(5); 51 | q.push(2); 52 | 53 | Assert.assertEquals("7", q.pop().toString()); 54 | Assert.assertEquals("7", q.pop().toString()); 55 | Assert.assertEquals("5", q.pop().toString()); 56 | q.pop(); 57 | Assert.assertEquals("2", q.pop().toString()); 58 | Assert.assertNull(q.pop()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/dataStructures/segmentTree/SegmentTreeTest.java: -------------------------------------------------------------------------------- 1 | package dataStructures.segmentTree; 2 | import static org.junit.Assert.assertEquals; 3 | 4 | import org.junit.Test; 5 | 6 | public class SegmentTreeTest { 7 | @Test 8 | public void construct_shouldConstructSegmentTree() { 9 | int[] arr1 = new int[] {7, 77, 37, 67, 33, 73, 13, 2, 7, 17, 87, 53}; 10 | SegmentTree tree1 = new SegmentTree(arr1); 11 | assertEquals(arr1[1] + arr1[2] + arr1[3], tree1.query(1, 3)); 12 | assertEquals(arr1[4] + arr1[5] + arr1[6] + arr1[7], tree1.query(4, 7)); 13 | int sum1 = 0; 14 | for (int i = 0; i < arr1.length; i++) { 15 | sum1 += arr1[i]; 16 | } 17 | assertEquals(sum1, tree1.query(0, arr1.length - 1)); 18 | 19 | 20 | int[] arr2 = new int[] {7, -77, 37, 67, -33, 0, 73, -13, 2, -7, 17, 0, -87, 53, 0}; // some negatives and 0s 21 | SegmentTree tree2 = new SegmentTree(arr1); 22 | assertEquals(arr1[1] + arr1[2] + arr1[3], tree2.query(1, 3)); 23 | assertEquals(arr1[4] + arr1[5] + arr1[6] + arr1[7], tree2.query(4, 7)); 24 | int sum2 = 0; 25 | for (int i = 0; i < arr1.length; i++) { 26 | sum2 += arr1[i]; 27 | } 28 | assertEquals(sum2, tree2.query(0, arr1.length - 1)); 29 | } 30 | 31 | @Test 32 | public void update_shouldUpdateSegmentTree() { 33 | int[] arr = new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 34 | SegmentTree tree = new SegmentTree(arr); 35 | assertEquals(55, tree.query(0, 10)); 36 | tree.update(5, 55); 37 | assertEquals(105, tree.query(0, 10)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/dataStructures/segmentTree/arrayRepresentation/SegmentTreeTest.java: -------------------------------------------------------------------------------- 1 | package dataStructures.segmentTree.arrayRepresentation; 2 | import static org.junit.Assert.assertEquals; 3 | 4 | import org.junit.Test; 5 | 6 | /** 7 | * This file is essentially duplicated from the parent. 8 | */ 9 | public class SegmentTreeTest { 10 | @Test 11 | public void construct_shouldConstructSegmentTree() { 12 | int[] arr1 = new int[] {7, 77, 37, 67, 33, 73, 13, 2, 7, 17, 87, 53}; 13 | SegmentTree tree1 = new SegmentTree(arr1); 14 | assertEquals(arr1[1] + arr1[2] + arr1[3], tree1.query(1, 3)); 15 | assertEquals(arr1[4] + arr1[5] + arr1[6] + arr1[7], tree1.query(4, 7)); 16 | int sum1 = 0; 17 | for (int i = 0; i < arr1.length; i++) { 18 | sum1 += arr1[i]; 19 | } 20 | assertEquals(sum1, tree1.query(0, arr1.length - 1)); 21 | 22 | 23 | int[] arr2 = new int[] {7, -77, 37, 67, -33, 0, 73, -13, 2, -7, 17, 0, -87, 53, 0}; // some negatives and 0s 24 | SegmentTree tree2 = new SegmentTree(arr1); 25 | assertEquals(arr1[1] + arr1[2] + arr1[3], tree2.query(1, 3)); 26 | assertEquals(arr1[4] + arr1[5] + arr1[6] + arr1[7], tree2.query(4, 7)); 27 | int sum2 = 0; 28 | for (int i = 0; i < arr1.length; i++) { 29 | sum2 += arr1[i]; 30 | } 31 | assertEquals(sum2, tree2.query(0, arr1.length - 1)); 32 | } 33 | 34 | @Test 35 | public void update_shouldUpdateSegmentTree() { 36 | int[] arr = new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 37 | SegmentTree tree = new SegmentTree(arr); 38 | assertEquals(55, tree.query(0, 10)); 39 | tree.update(5, 55); 40 | assertEquals(105, tree.query(0, 10)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/dataStructures/stack/StackTest.java: -------------------------------------------------------------------------------- 1 | package dataStructures.stack; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * This class implements the test for the stack. 8 | */ 9 | public class StackTest { 10 | @Test 11 | public void testEmpty() { 12 | Stack stk = new Stack<>(); 13 | Assert.assertNull(stk.pop()); 14 | Assert.assertNull(stk.peek()); 15 | } 16 | 17 | @Test 18 | public void testPopAndPeek() { 19 | Stack stk = new Stack<>(1, 2, 3); 20 | Assert.assertEquals("3", stk.peek().toString()); 21 | Assert.assertEquals("3", stk.pop().toString()); 22 | Assert.assertEquals("2", stk.peek().toString()); 23 | } 24 | 25 | @Test 26 | public void testPush() { 27 | Stack stk = new Stack<>(); 28 | stk.push(1); 29 | Assert.assertEquals("1", stk.peek().toString()); 30 | stk.push(2); 31 | stk.push(3); 32 | Assert.assertEquals("3", stk.peek().toString()); 33 | } 34 | } 35 | --------------------------------------------------------------------------------