Skip to content

Commit 73d8543

Browse files
Merge branch 'master' into fix/boyermoore-non-ascii
2 parents 741fcc1 + 410f087 commit 73d8543

13 files changed

Lines changed: 114 additions & 37 deletions

File tree

‎.github/workflows/codeql.yml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ jobs:
3030
distribution: 'temurin'
3131

3232
- name: Initialize CodeQL
33-
uses: github/codeql-action/init@v4.39.0
33+
uses: github/codeql-action/init@v4.39.1
3434
with:
3535
languages: 'java-kotlin'
3636

3737
- name: Build
3838
run: mvn --batch-mode --update-snapshots verify
3939

4040
- name: Perform CodeQL Analysis
41-
uses: github/codeql-action/analyze@v4.39.0
41+
uses: github/codeql-action/analyze@v4.39.1
4242
with:
4343
category: "/language:java-kotlin"
4444

@@ -55,12 +55,12 @@ jobs:
5555
uses: actions/checkout@v7
5656

5757
- name: Initialize CodeQL
58-
uses: github/codeql-action/init@v4.39.0
58+
uses: github/codeql-action/init@v4.39.1
5959
with:
6060
languages: 'actions'
6161

6262
- name: Perform CodeQL Analysis
63-
uses: github/codeql-action/analyze@v4.39.0
63+
uses: github/codeql-action/analyze@v4.39.1
6464
with:
6565
category: "/language:actions"
6666
...

‎src/main/java/com/thealgorithms/datastructures/lists/SkipList.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ public void remove(E e) {
121121
current = next;
122122
}
123123
}
124+
if (layer < 0) {
125+
return; // not found, nothing to unlink
126+
}
124127
for (int i = 0; i <= layer; i++) {
125128
current.previous(i).setNext(i, current.next(i));
126129
if (current.next(i) != null) {

‎src/main/java/com/thealgorithms/sorts/DutchNationalFlagSort.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public class DutchNationalFlagSort implements SortAlgorithm {
1313

1414
@Override
1515
public <T extends Comparable<T>> T[] sort(T[] array) {
16+
if (array.length == 0) {
17+
return array;
18+
}
1619
return dutchNationalFlagSort(array, array[(int) Math.ceil((array.length) / 2.0) - 1]);
1720
}
1821

‎src/main/java/com/thealgorithms/sorts/FlashSort.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ private <T extends Comparable<? super T>> void flashSort(T[] arr) {
7676
return; // All elements are the same
7777
}
7878

79-
final int m = (int) (classificationRatio * arr.length);
79+
// At least one class, otherwise small arrays index into an empty classification table
80+
final int m = Math.max(1, (int) (classificationRatio * arr.length));
8081

8182
final int[] classificationArray = new int[m];
8283

‎src/main/java/com/thealgorithms/sorts/SwapSort.java‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ public <T extends Comparable<T>> T[] sort(T[] array) {
1717
final int amountSmallerElements = this.getSmallerElementCount(array, index);
1818

1919
if (amountSmallerElements > 0) {
20-
SortUtils.swap(array, index, index + amountSmallerElements);
20+
int target = index + amountSmallerElements;
21+
// Skip slots that already hold an equal element, otherwise two equal values swap forever
22+
while (array[target].compareTo(array[index]) == 0) {
23+
target++;
24+
}
25+
SortUtils.swap(array, index, target);
2126
} else {
2227
index++;
2328
}

‎src/main/java/com/thealgorithms/sorts/TreeSort.java‎

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import static com.thealgorithms.sorts.SortUtils.print;
44

55
import com.thealgorithms.datastructures.trees.BSTRecursiveGeneric;
6+
import java.util.ArrayList;
7+
import java.util.Arrays;
68
import java.util.List;
79

810
/**
@@ -30,38 +32,39 @@ public <T extends Comparable<T>> List<T> sort(List<T> unsortedList) {
3032
}
3133

3234
private <T extends Comparable<T>> T[] doTreeSortArray(T[] unsortedArray) {
33-
// create a generic BST tree
34-
BSTRecursiveGeneric<T> tree = new BSTRecursiveGeneric<T>();
35-
36-
// add all elements to the tree
37-
for (T element : unsortedArray) {
38-
tree.add(element);
39-
}
40-
41-
// get the sorted list by inorder traversal of the tree
42-
List<T> sortedList = tree.inorderSort();
43-
44-
// add the elements back to the initial array
45-
int i = 0;
46-
for (T element : sortedList) {
47-
unsortedArray[i++] = element;
48-
}
49-
50-
// return the array
51-
return unsortedArray;
35+
// sort through the list path and copy the result back into the initial array
36+
List<T> sortedList = doTreeSortList(Arrays.asList(unsortedArray));
37+
return sortedList.toArray(unsortedArray);
5238
}
5339

5440
private <T extends Comparable<T>> List<T> doTreeSortList(Iterable<T> unsortedList) {
55-
// create a generic BST tree
56-
BSTRecursiveGeneric<T> tree = new BSTRecursiveGeneric<T>();
41+
// the BST ignores duplicate keys, so pair every element with its position to keep equal values apart
42+
BSTRecursiveGeneric<Keyed<T>> tree = new BSTRecursiveGeneric<>();
5743

5844
// add all elements to the tree
45+
int position = 0;
5946
for (T element : unsortedList) {
60-
tree.add(element);
47+
tree.add(new Keyed<>(element, position++));
6148
}
6249

6350
// get the sorted list by inorder traversal of the tree and return it
64-
return tree.inorderSort();
51+
List<T> sortedList = new ArrayList<>();
52+
for (Keyed<T> keyed : tree.inorderSort()) {
53+
sortedList.add(keyed.value());
54+
}
55+
return sortedList;
56+
}
57+
58+
/**
59+
* An element plus its original position. Equal elements are ordered by position,
60+
* which also makes the sort stable.
61+
*/
62+
private record Keyed<T extends Comparable<T>>(T value, int position) implements Comparable<Keyed<T>> {
63+
@Override
64+
public int compareTo(Keyed<T> other) {
65+
int byValue = value.compareTo(other.value);
66+
return byValue != 0 ? byValue : Integer.compare(position, other.position);
67+
}
6568
}
6669

6770
public static void main(String[] args) {

‎src/main/java/com/thealgorithms/strings/HorspoolSearch.java‎

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public static Integer getLastComparisons() {
9595
* @return index of first occurrence of the pattern in the text
9696
*/
9797
private static int firstOccurrence(String pattern, String text, boolean caseSensitive) {
98-
shiftValues = calcShiftValues(pattern); // build the bad symbol table
98+
shiftValues = calcShiftValues(pattern, caseSensitive); // build the bad symbol table
9999
comparisons = 0; // reset comparisons
100100

101101
if (pattern.length() == 0) { // return failure, if pattern empty
@@ -113,7 +113,7 @@ private static int firstOccurrence(String pattern, String text, boolean caseSens
113113
char patternChar = pattern.charAt(i);
114114
char textChar = text.charAt((textIndex + i) - (pattern.length() - 1));
115115
if (!charEquals(patternChar, textChar, caseSensitive)) { // bad character, shift pattern
116-
textIndex += getShiftValue(text.charAt(textIndex));
116+
textIndex += getShiftValue(text.charAt(textIndex), caseSensitive);
117117
break;
118118
}
119119
i--;
@@ -153,14 +153,15 @@ private static boolean charEquals(char c1, char c2, boolean caseSensitive) {
153153
* in the table, then it is not a rightmost occurrence, so it continues.
154154
*
155155
* @param pattern basis for the bad symbol table
156+
* @param caseSensitive whether to build the table with case-sensitive keys
156157
* @return the bad symbol table
157158
*/
158-
private static HashMap<Character, Integer> calcShiftValues(String pattern) {
159+
private static HashMap<Character, Integer> calcShiftValues(String pattern, boolean caseSensitive) {
159160
patternLength = pattern.length();
160161
HashMap<Character, Integer> table = new HashMap<>();
161162

162163
for (int i = pattern.length() - 2; i >= 0; i--) { // length - 2 is the index of the second to last character
163-
char c = pattern.charAt(i);
164+
char c = caseSensitive ? pattern.charAt(i) : Character.toLowerCase(pattern.charAt(i));
164165
int finalI = i;
165166
table.computeIfAbsent(c, k -> pattern.length() - 1 - finalI);
166167
}
@@ -173,11 +174,14 @@ private static HashMap<Character, Integer> calcShiftValues(String pattern) {
173174
* appropriate shift value for a given character
174175
*
175176
* @param c character
177+
* @param caseSensitive whether the table was built with case-sensitive keys
176178
* @return shift value that corresponds to the character argument
177179
*/
178-
private static Integer getShiftValue(char c) {
179-
if (shiftValues.get(c) != null) {
180-
return shiftValues.get(c);
180+
private static Integer getShiftValue(char c, boolean caseSensitive) {
181+
// the table holds lower-case keys for case-insensitive searches
182+
char key = caseSensitive ? c : Character.toLowerCase(c);
183+
if (shiftValues.get(key) != null) {
184+
return shiftValues.get(key);
181185
} else {
182186
return patternLength;
183187
}

‎src/test/java/com/thealgorithms/datastructures/lists/SkipListTest.java‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,20 @@ void testClearViaRemovals() {
106106
assertEquals(0, skipList.size());
107107
}
108108

109+
@Test
110+
@DisplayName("Removing a missing element leaves the list unchanged")
111+
void testRemoveMissing() {
112+
skipList.remove("missing");
113+
assertEquals(0, skipList.size());
114+
115+
skipList.add("a");
116+
skipList.add("c");
117+
skipList.remove("b");
118+
assertEquals(2, skipList.size());
119+
assertEquals("a", skipList.get(0));
120+
assertEquals("c", skipList.get(1));
121+
}
122+
109123
private SkipList<String> createSkipList() {
110124
SkipList<String> s = new SkipList<>();
111125
String[] values = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"};

‎src/test/java/com/thealgorithms/sorts/DutchNationalFlagSortTest.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,10 @@ void testOddStringsMidGivenDnfs() {
109109
dutchNationalFlagSort.sort(strings, "e");
110110
assertArrayEquals(strings, stringsResult);
111111
}
112+
113+
@Test
114+
void testEmptyArray() {
115+
Integer[] integers = {};
116+
assertArrayEquals(new Integer[] {}, new DutchNationalFlagSort().sort(integers));
117+
}
112118
}

‎src/test/java/com/thealgorithms/sorts/FlashSortTest.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.thealgorithms.sorts;
22

3+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
34
import static org.junit.jupiter.api.Assertions.assertEquals;
45
import static org.junit.jupiter.api.Assertions.assertThrows;
56

@@ -59,6 +60,12 @@ public List<DynamicTest> dynamicTestsForSorting() {
5960
return dynamicTests;
6061
}
6162

63+
@Test
64+
public void testArraysTooSmallForMoreThanOneClass() {
65+
assertArrayEquals(new Integer[] {1, 2}, new FlashSort().sort(new Integer[] {2, 1}));
66+
assertArrayEquals(new Integer[] {1, 2, 3, 4, 5}, new FlashSort(0.1).sort(new Integer[] {5, 3, 1, 4, 2}));
67+
}
68+
6269
private List<DynamicTest> createDynamicTestsForRatio(double ratio) {
6370
List<DynamicTest> dynamicTests = new ArrayList<>();
6471
for (TestMethod testMethod : getTestMethodsFromSuperClass()) {

0 commit comments

Comments
 (0)