Skip to content

Commit b462210

Browse files
authored
Merge branch 'master' into refactor/LineSweep
2 parents d93c1e8 + 6edc009 commit b462210

File tree

4 files changed

+193
-157
lines changed

4 files changed

+193
-157
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,73 @@
11
package com.thealgorithms.dynamicprogramming;
22

3-
/*
4-
5-
* Problem Statement: -
6-
* Find Longest Alternating Subsequence
7-
8-
* A sequence {x1, x2, .. xn} is alternating sequence if its elements satisfy one of the following
9-
relations :
10-
11-
x1 < x2 > x3 < x4 > x5 < …. xn or
12-
x1 > x2 < x3 > x4 < x5 > …. xn
3+
/**
4+
* Class for finding the length of the longest alternating subsequence in an array.
5+
*
6+
* <p>An alternating sequence is a sequence of numbers where the elements alternate
7+
* between increasing and decreasing. Specifically, a sequence is alternating if its elements
8+
* satisfy one of the following relations:
9+
*
10+
* <ul>
11+
* <li>{@code x1 < x2 > x3 < x4 > x5 < ... < xn}</li>
12+
* <li>{@code x1 > x2 < x3 > x4 < x5 > ... > xn}</li>
13+
* </ul>
14+
*
15+
* <p>This class provides a method to compute the length of the longest such subsequence
16+
* from a given array of integers.
1317
*/
1418
public final class LongestAlternatingSubsequence {
1519
private LongestAlternatingSubsequence() {
1620
}
1721

18-
/* Function to return longest alternating subsequence length*/
22+
/**
23+
* Finds the length of the longest alternating subsequence in the given array.
24+
*
25+
* @param arr an array of integers where the longest alternating subsequence is to be found
26+
* @param n the length of the array {@code arr}
27+
* @return the length of the longest alternating subsequence
28+
*
29+
* <p>The method uses dynamic programming to solve the problem. It maintains a 2D array
30+
* {@code las} where:
31+
* <ul>
32+
* <li>{@code las[i][0]} represents the length of the longest alternating subsequence
33+
* ending at index {@code i} with the last element being greater than the previous element.</li>
34+
* <li>{@code las[i][1]} represents the length of the longest alternating subsequence
35+
* ending at index {@code i} with the last element being smaller than the previous element.</li>
36+
* </ul>
37+
*
38+
* <p>The method iterates through the array and updates the {@code las} array based on
39+
* whether the current element is greater or smaller than the previous elements.
40+
* The result is the maximum value found in the {@code las} array.
41+
*/
1942
static int alternatingLength(int[] arr, int n) {
20-
/*
21-
22-
las[i][0] = Length of the longest
23-
alternating subsequence ending at
24-
index i and last element is
25-
greater than its previous element
26-
27-
las[i][1] = Length of the longest
28-
alternating subsequence ending at
29-
index i and last element is
30-
smaller than its previous
31-
element
32-
33-
*/
3443
int[][] las = new int[n][2]; // las = LongestAlternatingSubsequence
3544

45+
// Initialize the dp array
3646
for (int i = 0; i < n; i++) {
3747
las[i][0] = 1;
3848
las[i][1] = 1;
3949
}
4050

4151
int result = 1; // Initialize result
4252

43-
/* Compute values in bottom up manner */
53+
// Compute values in a bottom-up manner
4454
for (int i = 1; i < n; i++) {
45-
/* Consider all elements as previous of arr[i]*/
4655
for (int j = 0; j < i; j++) {
47-
/* If arr[i] is greater, then check with las[j][1] */
56+
// If arr[i] is greater than arr[j], update las[i][0]
4857
if (arr[j] < arr[i] && las[i][0] < las[j][1] + 1) {
4958
las[i][0] = las[j][1] + 1;
5059
}
5160

52-
/* If arr[i] is smaller, then check with las[j][0]*/
61+
// If arr[i] is smaller than arr[j], update las[i][1]
5362
if (arr[j] > arr[i] && las[i][1] < las[j][0] + 1) {
5463
las[i][1] = las[j][0] + 1;
5564
}
5665
}
5766

58-
/* Pick maximum of both values at index i */
59-
if (result < Math.max(las[i][0], las[i][1])) {
60-
result = Math.max(las[i][0], las[i][1]);
61-
}
67+
// Pick the maximum of both values at index i
68+
result = Math.max(result, Math.max(las[i][0], las[i][1]));
6269
}
6370

6471
return result;
6572
}
66-
67-
public static void main(String[] args) {
68-
int[] arr = {10, 22, 9, 33, 49, 50, 31, 60};
69-
int n = arr.length;
70-
System.out.println("Length of Longest "
71-
+ "alternating subsequence is " + alternatingLength(arr, n));
72-
}
7373
}

src/main/java/com/thealgorithms/strings/Anagrams.java

+91-102
Original file line numberDiff line numberDiff line change
@@ -10,141 +10,130 @@
1010
* also the word binary into brainy and the word adobe into abode.
1111
* Reference from https://en.wikipedia.org/wiki/Anagram
1212
*/
13-
public class Anagrams {
13+
public final class Anagrams {
14+
private Anagrams() {
15+
}
1416

1517
/**
16-
* 4 approaches are provided for anagram checking. approach 2 and approach 3 are similar but
17-
* differ in running time.
18-
* OUTPUT :
19-
* first string ="deal" second string ="lead"
20-
* Output: Anagram
21-
* Input and output is constant for all four approaches
22-
* 1st approach Time Complexity : O(n logn)
23-
* Auxiliary Space Complexity : O(1)
24-
* 2nd approach Time Complexity : O(n)
25-
* Auxiliary Space Complexity : O(1)
26-
* 3rd approach Time Complexity : O(n)
27-
* Auxiliary Space Complexity : O(1)
28-
* 4th approach Time Complexity : O(n)
29-
* Auxiliary Space Complexity : O(n)
30-
* 5th approach Time Complexity: O(n)
31-
* Auxiliary Space Complexity: O(1)
18+
* Checks if two strings are anagrams by sorting the characters and comparing them.
19+
* Time Complexity: O(n log n)
20+
* Space Complexity: O(n)
21+
*
22+
* @param s the first string
23+
* @param t the second string
24+
* @return true if the strings are anagrams, false otherwise
3225
*/
33-
public static void main(String[] args) {
34-
String first = "deal";
35-
String second = "lead";
36-
// All the below methods takes input but doesn't return any output to the main method.
37-
Anagrams nm = new Anagrams();
38-
System.out.println(nm.approach2(first, second)); /* To activate methods for different approaches*/
39-
System.out.println(nm.approach1(first, second)); /* To activate methods for different approaches*/
40-
System.out.println(nm.approach3(first, second)); /* To activate methods for different approaches*/
41-
System.out.println(nm.approach4(first, second)); /* To activate methods for different approaches*/
42-
}
43-
44-
boolean approach1(String s, String t) {
26+
public static boolean approach1(String s, String t) {
4527
if (s.length() != t.length()) {
4628
return false;
47-
} else {
48-
char[] c = s.toCharArray();
49-
char[] d = t.toCharArray();
50-
Arrays.sort(c);
51-
Arrays.sort(d); /* In this approach the strings are stored in the character arrays and
52-
both the arrays are sorted. After that both the arrays are compared
53-
for checking anangram */
54-
55-
return Arrays.equals(c, d);
5629
}
30+
char[] c = s.toCharArray();
31+
char[] d = t.toCharArray();
32+
Arrays.sort(c);
33+
Arrays.sort(d);
34+
return Arrays.equals(c, d);
5735
}
5836

59-
boolean approach2(String a, String b) {
60-
if (a.length() != b.length()) {
37+
/**
38+
* Checks if two strings are anagrams by counting the frequency of each character.
39+
* Time Complexity: O(n)
40+
* Space Complexity: O(1)
41+
*
42+
* @param s the first string
43+
* @param t the second string
44+
* @return true if the strings are anagrams, false otherwise
45+
*/
46+
public static boolean approach2(String s, String t) {
47+
if (s.length() != t.length()) {
6148
return false;
62-
} else {
63-
int[] m = new int[26];
64-
int[] n = new int[26];
65-
for (char c : a.toCharArray()) {
66-
m[c - 'a']++;
67-
}
68-
// In this approach the frequency of both the strings are stored and after that the
69-
// frequencies are iterated from 0 to 26(from 'a' to 'z' ). If the frequencies match
70-
// then anagram message is displayed in the form of boolean format Running time and
71-
// space complexity of this algo is less as compared to others
72-
for (char c : b.toCharArray()) {
73-
n[c - 'a']++;
74-
}
75-
for (int i = 0; i < 26; i++) {
76-
if (m[i] != n[i]) {
77-
return false;
78-
}
49+
}
50+
int[] charCount = new int[26];
51+
for (int i = 0; i < s.length(); i++) {
52+
charCount[s.charAt(i) - 'a']++;
53+
charCount[t.charAt(i) - 'a']--;
54+
}
55+
for (int count : charCount) {
56+
if (count != 0) {
57+
return false;
7958
}
80-
return true;
8159
}
60+
return true;
8261
}
8362

84-
boolean approach3(String s, String t) {
63+
/**
64+
* Checks if two strings are anagrams by counting the frequency of each character
65+
* using a single array.
66+
* Time Complexity: O(n)
67+
* Space Complexity: O(1)
68+
*
69+
* @param s the first string
70+
* @param t the second string
71+
* @return true if the strings are anagrams, false otherwise
72+
*/
73+
public static boolean approach3(String s, String t) {
8574
if (s.length() != t.length()) {
8675
return false;
8776
}
88-
// this is similar to approach number 2 but here the string is not converted to character
89-
// array
90-
else {
91-
int[] a = new int[26];
92-
int[] b = new int[26];
93-
int k = s.length();
94-
for (int i = 0; i < k; i++) {
95-
a[s.charAt(i) - 'a']++;
96-
b[t.charAt(i) - 'a']++;
97-
}
98-
for (int i = 0; i < 26; i++) {
99-
if (a[i] != b[i]) {
100-
return false;
101-
}
77+
int[] charCount = new int[26];
78+
for (int i = 0; i < s.length(); i++) {
79+
charCount[s.charAt(i) - 'a']++;
80+
charCount[t.charAt(i) - 'a']--;
81+
}
82+
for (int count : charCount) {
83+
if (count != 0) {
84+
return false;
10285
}
103-
return true;
10486
}
87+
return true;
10588
}
10689

107-
boolean approach4(String s, String t) {
90+
/**
91+
* Checks if two strings are anagrams using a HashMap to store character frequencies.
92+
* Time Complexity: O(n)
93+
* Space Complexity: O(n)
94+
*
95+
* @param s the first string
96+
* @param t the second string
97+
* @return true if the strings are anagrams, false otherwise
98+
*/
99+
public static boolean approach4(String s, String t) {
108100
if (s.length() != t.length()) {
109101
return false;
110102
}
111-
// This approach is done using hashmap where frequencies are stored and checked iteratively
112-
// and if all the frequencies of first string match with the second string then anagram
113-
// message is displayed in boolean format
114-
else {
115-
HashMap<Character, Integer> nm = new HashMap<>();
116-
HashMap<Character, Integer> kk = new HashMap<>();
117-
for (char c : s.toCharArray()) {
118-
nm.put(c, nm.getOrDefault(c, 0) + 1);
119-
}
120-
for (char c : t.toCharArray()) {
121-
kk.put(c, kk.getOrDefault(c, 0) + 1);
103+
HashMap<Character, Integer> charCountMap = new HashMap<>();
104+
for (char c : s.toCharArray()) {
105+
charCountMap.put(c, charCountMap.getOrDefault(c, 0) + 1);
106+
}
107+
for (char c : t.toCharArray()) {
108+
if (!charCountMap.containsKey(c) || charCountMap.get(c) == 0) {
109+
return false;
122110
}
123-
// It checks for equal frequencies by comparing key-value pairs of two hashmaps
124-
return nm.equals(kk);
111+
charCountMap.put(c, charCountMap.get(c) - 1);
125112
}
113+
return charCountMap.values().stream().allMatch(count -> count == 0);
126114
}
127115

128-
boolean approach5(String s, String t) {
116+
/**
117+
* Checks if two strings are anagrams using an array to track character frequencies.
118+
* This approach optimizes space complexity by using only one array.
119+
* Time Complexity: O(n)
120+
* Space Complexity: O(1)
121+
*
122+
* @param s the first string
123+
* @param t the second string
124+
* @return true if the strings are anagrams, false otherwise
125+
*/
126+
public static boolean approach5(String s, String t) {
129127
if (s.length() != t.length()) {
130128
return false;
131129
}
132-
// Approach is different from above 4 aproaches.
133-
// Here we initialize an array of size 26 where each element corresponds to the frequency of
134-
// a character.
135130
int[] freq = new int[26];
136-
// iterate through both strings, incrementing the frequency of each character in the first
137-
// string and decrementing the frequency of each character in the second string.
138131
for (int i = 0; i < s.length(); i++) {
139-
int pos1 = s.charAt(i) - 'a';
140-
int pos2 = s.charAt(i) - 'a';
141-
freq[pos1]++;
142-
freq[pos2]--;
132+
freq[s.charAt(i) - 'a']++;
133+
freq[t.charAt(i) - 'a']--;
143134
}
144-
// iterate through the frequency array and check if all the elements are zero, if so return
145-
// true else false
146-
for (int i = 0; i < 26; i++) {
147-
if (freq[i] != 0) {
135+
for (int count : freq) {
136+
if (count != 0) {
148137
return false;
149138
}
150139
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import java.util.stream.Stream;
6+
import org.junit.jupiter.params.ParameterizedTest;
7+
import org.junit.jupiter.params.provider.Arguments;
8+
import org.junit.jupiter.params.provider.MethodSource;
9+
10+
public class LongestAlternatingSubsequenceTest {
11+
12+
@ParameterizedTest
13+
@MethodSource("provideTestCases")
14+
void testAlternatingLength(int[] arr, int expected) {
15+
assertEquals(expected, LongestAlternatingSubsequence.alternatingLength(arr, arr.length));
16+
}
17+
18+
private static Stream<Arguments> provideTestCases() {
19+
return Stream.of(Arguments.of(new int[] {1}, 1), Arguments.of(new int[] {1, 2}, 2), Arguments.of(new int[] {2, 1}, 2), Arguments.of(new int[] {1, 3, 2, 4, 3, 5}, 6), Arguments.of(new int[] {1, 2, 3, 4, 5}, 2), Arguments.of(new int[] {5, 4, 3, 2, 1}, 2),
20+
Arguments.of(new int[] {10, 22, 9, 33, 49, 50, 31, 60}, 6), Arguments.of(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 2));
21+
}
22+
}

0 commit comments

Comments
 (0)