Skip to content

Commit c2304f0

Browse files
Merge branch 'master' into fix/rabinkarp-overflow
2 parents 6709998 + e6b2ecb commit c2304f0

18 files changed

Lines changed: 166 additions & 50 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
...

‎.github/workflows/project_structure.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
runs-on: ubuntu-latest
1717
steps:
1818
- uses: actions/checkout@v7
19-
- uses: actions/setup-python@v7.0.0
19+
- uses: actions/setup-python@v7.1.0
2020
with:
2121
python-version: '3.13'
2222

‎pom.xml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
<plugins>
6262
<plugin>
6363
<artifactId>maven-surefire-plugin</artifactId>
64-
<version>3.6.0</version>
64+
<version>3.6.1</version>
6565
<configuration>
6666
<forkNode implementation="org.apache.maven.plugin.surefire.extensions.SurefireForkNodeFactory"/>
6767
</configuration>
@@ -82,7 +82,7 @@
8282
<plugin>
8383
<groupId>org.jacoco</groupId>
8484
<artifactId>jacoco-maven-plugin</artifactId>
85-
<version>0.8.15</version>
85+
<version>0.8.16</version>
8686
<executions>
8787
<execution>
8888
<goals>

‎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/searches/BoyerMoore.java‎

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
11
package com.thealgorithms.searches;
22

3+
import java.util.HashMap;
4+
import java.util.Map;
5+
36
/**
47
* Boyer-Moore string search algorithm.
58
* Efficient algorithm for substring search.
69
* https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm
710
*/
811
public class BoyerMoore {
912

10-
private final int radix; // Radix (number of possible characters)
11-
private final int[] right; // Bad character rule table
13+
private final Map<Character, Integer> right; // Bad character rule table: last index of each pattern character
1214
private final String pattern;
1315

1416
public BoyerMoore(String pat) {
1517
this.pattern = pat;
16-
this.radix = 256;
17-
this.right = new int[radix];
18-
19-
for (int c = 0; c < radix; c++) {
20-
right[c] = -1;
21-
}
18+
// A map instead of a 256-entry array, so characters outside extended ASCII work too
19+
this.right = new HashMap<>();
2220

2321
for (int j = 0; j < pat.length(); j++) {
24-
right[pat.charAt(j)] = j;
22+
right.put(pat.charAt(j), j);
2523
}
2624
}
2725

@@ -40,7 +38,7 @@ public int search(String text) {
4038
char txtChar = text.charAt(i + j);
4139
char patChar = pattern.charAt(j);
4240
if (patChar != txtChar) {
43-
skip = Math.max(1, j - right[txtChar]);
41+
skip = Math.max(1, j - right.getOrDefault(txtChar, -1));
4442
break;
4543
}
4644
}

‎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
}

0 commit comments

Comments
 (0)