Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,6 @@ fabric.properties
hot.md
.claude/worktrees/
triage-*.md

# jqwik failure database (replays previously failing samples locally)
.jqwik-database
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ src/main/java/io/github/diegourban/algorithms/
datastructure/ array (shuffle), binarytree, linkedlist, queue, set, stack, vector
io/ Reader: fast tokenising stdin reader for judge-style input
math/ Algebra (gcd, lcm, primes, factorisation), Combinatorics, Geometry, Numbers, Statistics
search/ BinarySearch
sorting/ Insertion, Merge, Quick and Selection sort
search/ BinarySearch.indexOf
sorting/ Sorter interface: Insertion, Merge, Quick and Selection sort
string/ StringUtils

src/test/java/io/github/diegourban/algorithms/
<same packages> unit tests for the library
<same packages> unit tests for the library (*PropertyTest: jqwik property-based tests)
problems/<topic> worked problems (see below)
testing/ SampleBuilder, SampleLoader: helpers for judge-style tests
```
Expand Down
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

<junit.version>6.1.3</junit.version>
<assertj.version>3.27.7</assertj.version>
<jqwik.version>1.10.2</jqwik.version>

<maven-clean-plugin.version>3.5.0</maven-clean-plugin.version>
<maven-compiler-plugin.version>3.16.0</maven-compiler-plugin.version>
Expand Down Expand Up @@ -53,6 +54,12 @@
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.jqwik</groupId>
<artifactId>jqwik</artifactId>
<version>${jqwik.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@
*/
public class BinarySearch {

/**
* Finds an element in an ordered list
*
* @param sorted an ordered list of items to find
* @param key the element to find
* @return the index of the element, -1 otherwise
*/
public static int indexOf(final int[] sorted, final int key) {
return find(sorted, 0, sorted.length - 1, key);
}

/**
* Finds an element in a list
* <p>
Expand All @@ -16,7 +27,7 @@ public class BinarySearch {
* @param element the element to find
* @return the index of the element, -1 otherwise
*/
public int find(final int[] items, final int from, final int to, final int element) {
private static int find(final int[] items, final int from, final int to, final int element) {
if(from > to) {
return -1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@
/**
* Insertion Sort Algorithm
*/
public class InsertionSort {
public class InsertionSort implements Sorter {

@Override
public void sort(final int[] items) {
sort(items, items.length);
}

/**
* Sort the items using insertion sort algorithm
*
* @param items items to sort
* @param totalElements total elements
*/
public void sort(final int[] items, final int totalElements) {
private void sort(final int[] items, final int totalElements) {
for(int current = 1; current < totalElements; current++) {
int analysis = current;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
/**
* Merge sort algorithm
*/
public class MergeSort {
public class MergeSort implements Sorter {

@Override
public void sort(final int[] items) {
sort(items, 0, items.length);
}

/**
* Sorts the items from begin to end
Expand All @@ -12,7 +17,7 @@ public class MergeSort {
* @param begin begin
* @param end end
*/
public void sort(final int[] items, final int begin, final int end) {
private void sort(final int[] items, final int begin, final int end) {
final int total = end - begin;
if(total > 1) {
final int middle = (begin + end) / 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
/**
* Quick sort algorithm
*/
public class QuickSort {
public class QuickSort implements Sorter {

@Override
public void sort(final int[] items) {
sort(items, 0, items.length);
}

/**
* Sort the items from start to end
Expand All @@ -12,20 +17,20 @@ public class QuickSort {
* @param start from
* @param end end
*/
public void sort(final int[] items, final int start, final int end) {
private void sort(final int[] items, final int start, final int end) {
final int totalElements = end - start;
if(totalElements > 1) {
final int pivotPosition = partition(items, end);
final int pivotPosition = partition(items, start, end);
sort(items, start, pivotPosition);
sort(items, pivotPosition + 1, end);
}
}

private int partition(final int[] items, final int end) {
int minorsFound = 0;
private int partition(final int[] items, final int start, final int end) {
int minorsFound = start;

final int pivot = items[end - 1];
for(int current = 0; current < end - 1; current++) {
for(int current = start; current < end - 1; current++) {
final int currentItem = items[current];
if(currentItem <= pivot) {
swap(items, current, minorsFound);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@
/**
* Selection Sort algorithm
*/
public class SelectionSort {
public class SelectionSort implements Sorter {

@Override
public void sort(final int[] items) {
sort(items, items.length);
}

/**
* Sort the items using selection sort algorithm
*
* @param items items to sort
* @param totalElements total elements
*/
public void sort(final int[] items, final int totalElements) {
private void sort(final int[] items, final int totalElements) {
for(int current = 0; current < totalElements - 1; current++) {
final int smallerIndex = findSmallerIndex(items, current, totalElements);
Integer currentItem = items[current];
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/io/github/diegourban/algorithms/sorting/Sorter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.github.diegourban.algorithms.sorting;

/**
* An in-place, ascending sort of an {@code int[]}.
* <p>
* Every sorting algorithm in this package implements it, so they can be swapped, compared and tested as one family.
*/
public interface Sorter {

/**
* Sorts {@code items} in ascending order, in place.
*
* @param items the values to sort; an empty array is left as is
*/
void sort(int[] items);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class FindTheOddIntTest {

static int solve(int[] numbers) {
var sorted = numbers.clone();
new MergeSort().sort(sorted, 0, sorted.length);
new MergeSort().sort(sorted);
var runStart = 0;
for (var i = 1; i <= sorted.length; i++) {
if (i == sorted.length || sorted[i] != sorted[runStart]) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.github.diegourban.algorithms.search;

import net.jqwik.api.ForAll;
import net.jqwik.api.Property;

import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;

/**
* {@link BinarySearch#indexOf(int[], int)} must agree with {@link Arrays#binarySearch(int[], int)} on whether the key
* is present. With duplicates the two may pick different indexes, so only the found value is compared.
*/
class BinarySearchPropertyTest {

@Property
void agreesWithTheJdk(@ForAll int[] items, @ForAll int key) {
Arrays.sort(items);

var index = BinarySearch.indexOf(items, key);

if (Arrays.binarySearch(items, key) >= 0) {
assertThat(items[index]).isEqualTo(key);
} else {
assertThat(index).isEqualTo(-1);
}
}

@Property
void findsEveryElementThatIsPresent(@ForAll int[] items) {
Arrays.sort(items);

for (var item : items) {
assertThat(items[BinarySearch.indexOf(items, item)]).isEqualTo(item);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,37 @@

class BinarySearchTest {

private static final int[] ONE_TO_TEN = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

@Test
void findsOnTheLeftSide() {
assertThat(BinarySearch.indexOf(ONE_TO_TEN, 3)).isEqualTo(2);
}

@Test
void findsOnTheRightSide() {
assertThat(BinarySearch.indexOf(ONE_TO_TEN, 9)).isEqualTo(8);
}

@Test
void findsBothEnds() {
assertThat(BinarySearch.indexOf(ONE_TO_TEN, 1)).isZero();
assertThat(BinarySearch.indexOf(ONE_TO_TEN, 10)).isEqualTo(9);
}

@Test
void shouldHandleNotExistingElement() {
final int[] items = {1, 2, 3, 5, 6, 7, 8, 9, 10};
final int element = 4;
final int expectedPosition = -1;

final BinarySearch binarySearch = new BinarySearch();
final int position = binarySearch.find(items, 0, items.length, element);
assertThat(position).isEqualTo(expectedPosition);
void returnsMinusOneForAMissingKey() {
assertThat(BinarySearch.indexOf(new int[]{1, 2, 3, 5, 6, 7, 8, 9, 10}, 4)).isEqualTo(-1);
}

@Test
void shouldFindOnRightSide() {
final int[] items = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
final int element = 9;
final int expectedPosition = 8;

final BinarySearch binarySearch = new BinarySearch();
final int position = binarySearch.find(items, 0, items.length, element);
assertThat(position).isEqualTo(expectedPosition);
void returnsMinusOneOutsideTheRange() {
assertThat(BinarySearch.indexOf(ONE_TO_TEN, 0)).isEqualTo(-1);
assertThat(BinarySearch.indexOf(ONE_TO_TEN, 11)).isEqualTo(-1);
}

@Test
void shouldFindOnLeftSide() {
final int[] items = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
final int element = 3;
final int expectedPosition = 2;

final BinarySearch binarySearch = new BinarySearch();
final int position = binarySearch.find(items, 0, items.length, element);
assertThat(position).isEqualTo(expectedPosition);
void returnsMinusOneForAnEmptyArray() {
assertThat(BinarySearch.indexOf(new int[]{}, 1)).isEqualTo(-1);
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Loading
Loading