I am trying to solve a DSA problem for Minimum coin change,For that trying to print all the combinations of coins needed to make a certain amount. Below are two programs one uses String as a param and other one uses ArrayList. String one seems to pring the correct output but ArrayList doesn't.
Input to question -
wt[] = {1,2,3}
cap : 5
n : wt.length
coin = ""
Below are the code samples -
For String as param :
private static int findMaxProfit(int[] wt, int cap, int n, String coin) {
if (cap == 0) {
System.out.println(coin);
return 1;
}
if (n == 0 || cap < 0) {
return 0;
}
if (wt[n - 1] <= cap) {
//Don't reduce n, when we are including the item in knapsack.
return findMaxProfit(wt, cap - wt[n - 1], n, coin + wt[n - 1] + ",") + findMaxProfit(wt, cap, n - 1, coin);
} else {
return findMaxProfit(wt, cap, n - 1, coin);
}
}
For Arraylist as param :
private static int findMinCoins(int[] wt, int cap, int n, List<Integer> list, boolean val) {
if (cap == 0) {
System.out.println(list);
return 1;
}
if (n == 0 || cap < 0) {
return 0;
}
if (wt[n - 1] <= cap) {
//Don't reduce n, when we are including the item in knapsack.
return findMinCoins(wt, cap - wt[n - 1], n, list, list.add(wt[n - 1])) + findMinCoins(wt, cap, n - 1, list, true);
} else {
return findMinCoins(wt, cap, n - 1, list, true);
}
}
Above code samples can be executed by passing the input for reference, Adding output of both the code samples below -
String output : 3,2, ArrayList output : [3, 2]
3,1,1, [3, 2, 1, 1]
2,2,1, [3, 2, 1, 1, 2, 2, 1]
2,1,1,1, [3, 2, 1, 1, 2, 2, 1, 1, 1, 1]
1,1,1,1,1, [3, 2, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1]
Related
Given an int array that may contain duplicates. I need to find out whether the array can produce a sum with k elements?
I'm trying to use recursion for now but I'm not getting anywhere at the moment. I have a problem with getting the base-case right.
static boolean groupSum(int[] nums, int k, int sum) {
if (sum == 0)
return true;
if (k > nums.length || sum != 0)
return false;
if (groupSum(nums, k, sum - nums[k - k] - nums[k - k + 1] - nums[k - k + 2]))
return true;
if (groupSum(nums, k, sum - nums[k - k + 1] - nums[k - k + 2] - nums[k - k + 3]))
return true;
else
return false;
}
The base case of your recursive method is incorrect: sum == 0 isn't a sufficient condition you also have to check whether k == 0. If the sum is 0 but you still have to use a few more elements then the result true is incorrect.
The recursive part of the method has some mistakes in it as well. The number of elements summed up k never gets decremented.
Also, you have to track a position in the array. I mean you need to iterate over the array. And while iterating for each element there are only two options: apply it (decrement the k and subtract current value from the sum) or discard it. In both cases, you have to increment the position that you are passing as an argument.
That is how it could be fixed:
public static boolean groupSum(int[] nums, int k, int pos, int sum) {
if (sum == 0 && k == 0) {
return true;
}
if (sum < 0) {
return false;
}
boolean result = false;
for (int i = pos; i < nums.length; i++) {
boolean takeElement = groupSum(nums, k - 1, pos + 1, sum - nums[i]);
boolean discardElement = groupSum(nums, k, pos + 1, sum);
result = takeElement || discardElement;
if (result) { // a combination that yield the target sum was found
break;
}
}
return result;
}
There's another option how to implement this method. But caution: it will be slower and significantly increases memory consumption.
This approach requires creating a copy of the given array with a length decremented by 1 at each recursive method call and pass it as an argument. In this version, an element at position 0 is always used when the method is called, therefore it doesn't require to track the position in the array. The main logic remains the same: a new element can be used either to contribute the sum or can be discarded.
public static boolean groupSum(int[] nums, int k, int sum) {
if (sum == 0 && k == 0) {
return true;
}
if (sum < 0 || nums.length == 0) {
return false;
}
boolean takeElement = groupSum(Arrays.copyOfRange(nums, 1, nums.length), k - 1, sum - nums[0]);
boolean discardElement = groupSum(Arrays.copyOfRange(nums, 1, nums.length), k, sum);
return takeElement || discardElement;
}
public static void main(String[] args) {
// sum of 1, 2, 3, 4, 5 yields 15 - expected true
System.out.println(groupSum(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}, 5, 0, 15));
// it's impossible with only one element - expected false
System.out.println(groupSum(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}, 1, 0, 10));
// sum of 4, 5, 6 yields 15 - expected true
System.out.println(groupSum(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}, 3, 0, 15));
}
output
true
false
true
public static boolean isSplitable(int[] arr, int diff, int i)
no loops or helper methods i need to check if arr[i...arr.length - 1] has a two section sum that is equal to diff
for example i have: diff = 1, i = 2, arr = {3, 4, 1, 1, 2, 0, 1, 1, 3} it returns true because sum of {1, 1, 2, 0}(4) minus the sum of {1, 1, 3}(5) equals diff(1).
i have tried to think of ways to even sum arr without a loop only thing i came up with is adding it to diff but then i lose my original diff plus i dont know the amount of elements so i can add "or" in my return statement so now im out of ideas.
I'm assuming your restrictions are no loops and no library usage. You probably need some kind of additional functions. Moreover, I assume the difference check shall use absolute values, i.e. it does not matter if the first or second sum is smaller, as long as the difference matches (-1 or 1 in your example). That being said, a first approach could look like this:
import org.junit.jupiter.api.Test;
import static java.util.Arrays.copyOfRange;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class RecursiveSplitTest {
#Test
public void testIsSplitable() {
{
int[] array = {3, 4, 1, 1, 2, 0, 1, 1, 3};
assertTrue(isSplitable(array, 1, 2));
}
{
int[] array = {3, 4, 1, 1, 1, 0, 4, 1, 3};
assertTrue(isSplitable(array, 1, 4));
}
{
int[] array = {3, 4, 1, 1, 1, 0, 4, 1, 3};
assertFalse(isSplitable(array, 1, 7));
}
{
int[] array = {3};
assertFalse(isSplitable(array, 1, 1));
}
}
public static boolean isSplitable(int[] array, int diff, int start) {
if (start < 0 || start > array.length - 2) {
System.out.println("Not possible due to initial parameters");
return false;
}
return doSplitableRecursive(copyOfRange(array, start, array.length), diff, 0);
}
private static boolean doSplitableRecursive(int[] array, int diff, int start) {
if (start + 1 >= array.length) {
System.out.println("Could not find it");
return false;
}
int[] left = copyOfRange(array, 0, start + 1);
int[] right = copyOfRange(array, start + 1, array.length);
int leftSum = sum(left, 0);
int rightSum = sum(right, 0);
System.out.println("leftSum: " + leftSum);
System.out.println("rightSum: " + rightSum);
if (leftSum + diff == rightSum || leftSum - diff == rightSum) {
System.out.println("Found match for cut at start + " + (start + 1));
return true;
}
return doSplitableRecursive(array, diff, start + 1);
}
private static int sum(int[] array, int current) {
if (array.length == 0) {
return current;
}
return sum(copyOfRange(array, 1, array.length), current + array[0]);
}
}
The key is to systematically divide the array in two pieces, beginning at start and checking the sum of each piece against the difference. Then cutting one index further and repeating the process if it did not match. Finally, you have to exit with false if there are no more elements left.
It is a little rough around the edges. Feel free to improve it and fix possibly remaining bugs. The print statements are for debugging purposes. Of course, one could argue JUnit and java.util.Arrays are library functions. These can be replaced with a little bit of extra effort if needed.
I want to count the number of unique values in an array but I'm having trouble counting in the correct way.
int uniqueNumbers = 1;
Arrays.sort(n);
if (n.length == 0) {
uniqueNumbers = 0;
}
for ( int i = 1; i < n.length; i++) {
if (n[i] != n[i - 1]) {
uniqueNumbers++;
}
}
The problem is if an integer appears several times it still counts it as one unique number, when I want it not to be counted as a unique number.
you can utilize a set to add all the numbers, as won't allow duplicates.
int uniqueNumbers = 1;
Set<Integer> set = new HashSet<>();
Arrays.sort(n);
if (n.length == 0) {
uniqueNumbers = 0;
}
for ( int i = 0; i < n.length; i++) {
set.add(n[i];
}
System.out.println(set.size());
A nested loop to track a unique number and discard it can help resolve this task:
public static int countUnique(int ... n) {
Arrays.sort(n);
System.out.println(Arrays.toString(n));
int uniqueNumbers = 0;
for (int i = 0; i < n.length; i++) {
boolean unique = true;
for (int j = i + 1; j < n.length && n[i] == n[j]; j++, i++) {
unique = false;
}
if (unique) {
uniqueNumbers++;
}
}
return uniqueNumbers;
}
Tests:
System.out.println(countUnique(2, 1, 2, 3, 4, 6, 4));
System.out.println(countUnique(2, 1, 2, 3, 4, 1, 4));
System.out.println(countUnique(2, 1, 2, 4, 4, 1, 4));
Output:
[1, 2, 2, 3, 4, 4, 6]
3
[1, 1, 2, 2, 3, 4, 4]
1
[1, 1, 2, 2, 4, 4, 4]
0
However, because of sorting the input array, the complexity of this algorithm is O(N log N).
If it is allowed to use Set to track duplicates using the fact that Set::add returns false when an element already exists in the set, this may be implemented as follows (also, the input array does not need to be sorted, so this algorithm has O(N) complexity):
public static int countUniqueSets(int ... n) {
System.out.println(Arrays.toString(n));
Set<Integer> ones = new HashSet<>();
Set<Integer> dups = new HashSet<>();
for (int x : n) {
if (!ones.add(x)) {
dups.add(x);
}
}
System.out.println("distinct: " + ones);
System.out.println("duplicates: " + dups);
return ones.size() - dups.size();
}
Output for the same tests:
[2, 1, 2, 3, 4, 6, 4]
distinct: [1, 2, 3, 4, 6]
duplicates: [2, 4]
3
[2, 1, 2, 3, 4, 1, 4]
distinct: [1, 2, 3, 4]
duplicates: [1, 2, 4]
1
[2, 1, 2, 4, 4, 1, 4]
distinct: [1, 2, 4]
duplicates: [1, 2, 4]
0
Another approach using Stream API is to build a frequency map using Collectors.groupingBy + Collectors.counting or Collectors.summingInt and then count the entries in the map with frequency = 1:
public static int countUniqueStream(int ... n) {
System.out.println(Arrays.toString(n));
return (int) Arrays.stream(n)
.boxed()
.collect(Collectors.groupingBy(
x -> x,
Collectors.counting()
)) // Map<Integer, Long>
.entrySet()
.stream()
.filter(e -> 1 == e.getValue())
.count();
}
public static int countUniqueStreamInt(int ... n) {
System.out.println(Arrays.toString(n));
return Arrays.stream(n)
.boxed()
.collect(Collectors.groupingBy(
x -> x,
Collectors.summingInt(x -> 1)
)) // Map<Integer, Integer>
.entrySet().stream()
.filter(e -> 1 == e.getValue())
.collect(Collectors.summingInt(e -> 1));
}
There are so many ways to do so e.g. using Stream API.
Demo:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int [] arr= {1,2,3,4,2,3,4,1,4,3};
long uniqueCount = Arrays.stream(arr)
.distinct()
.count();
System.out.println(uniqueCount);
}
}
You can put the array content to set and then get the set size.
Set<Integer> targetSet = new HashSet<Integer>(Arrays.asList(n));
targetSet.size()
n is your array name as appearing in the question
You don't need a whole inner loop, you just need to check the two adjacent numbers while respecting the array limits.
private int countUnique (int... n)
{
int uniqueNumbers = 0;
if (n.length < 2)
{
uniqueNumbers = n.length;
}
else
{
Arrays.sort (n);
logger.info ("Data %s", Arrays.toString (n));
// Check first element
if (n[0] != n[1])
{
uniqueNumbers++;
}
// Check last element
if (n[n.length - 2] != n[n.length - 1])
{
uniqueNumbers++;
}
// Check middle elements
for (int i = 1; i < n.length - 1; i++)
{
if ((n[i - 1] != n[i]) && (n[i + 1] != n[i]))
{
uniqueNumbers++;
}
}
}
return uniqueNumbers;
}
Here is a full program description with test cases and below it is my solution:
Given an array of integers int A[], find the length and location of the longest contiguous sequence of equal values for which the values of the elements just before and just after this sequence are smaller.
You should just print these two numbers (first is the length and second is the starting index of the plateau).
To complete the definition, we can consider there are imaginary index positions at A[-1] and A[A.length] where A[-1] < A[0] and A[A.length] < A[A.length-1]. Therefore, the plateau can start/end at both ends of array A. This condition guarantees the existence of a plateau. A plateau can be of length 1.
Example 1:
java LongestPlateau 1 2 2 2 2 1
With these command-line arguments, the program should print:
4
1
Example 2:
java LongestPlateau 1 2 2 2 2 3
With these command-line arguments, the program should print:
1
5
Example 3:
java LongestPlateau 3 2 2 2 1 2 1 1 1 2 2 0 1 1 1 1 0
With these command-line arguments, the program should print:
4
12
Here is my Solution:
public class LongestPlateau {
private static int[] parseInputArray(String[] args) {
int[] value = new int[args.length+1];
for(int i = 0 ; i < args.length; i++){
if (i == args.length-1) value[i] = 0; // this imaginary last value of the array ensures that if the plateau is the last value of the array, then it outputs the correct answer
value[i] = Integer.parseInt(args[i]);
}
return value;
}
public static void printLargestPlateau(int[] values) {
int biggestStartIndex = -1;
int biggestLength = 0;
int currentIndex = 1;
int currentPlateauStartIndex = 1;
int currentLength = 1;
boolean plateauStarted = false;
while (currentIndex < values.length) {
if(isStartOfPlateau(currentIndex, values)){
currentLength = 1;
plateauStarted = true;
currentPlateauStartIndex = currentIndex;
} else if (isEndOfPlateau(currentIndex, values)) {
if(plateauStarted && currentLength > biggestLength){
biggestLength = currentLength;
biggestStartIndex = currentPlateauStartIndex;
}
plateauStarted = false;
currentLength = 1;
} else {
currentLength++;
}
currentIndex++;
}
System.out.println(biggestLength +"\n"+biggestStartIndex);
}
private static boolean isStartOfPlateau(int index, int[] values){
if(index <= 0){
return false;
}
return values[index-1] < values[index];
}
private static boolean isEndOfPlateau(int index, int[] values){
if(index <= 0){
return false;
}
return values[index - 1] > values[index];
}
public static void main(String[] args) {
int[] values = parseInputArray(args);
printLargestPlateau(values);
}
}
As I mentioned in the comments, existing code fails to detect plateaus at the start and the end of the input data, and the following implementation fixes this issue.
static void printLargestPlateau(int ... arr) {
int start = -1, maxStart = -1;
int length = 0, maxLength = -1;
boolean onPlateau = false;
if (arr.length > 0) {
start = 0;
length = 1;
onPlateau = true;
for (int i = 1; i < arr.length; i++) {
if (arr[i] == arr[i - 1]) {
if (onPlateau) {
length++;
}
} else if (arr[i] < arr[i - 1]) {
if (length > maxLength) {
maxLength = length;
maxStart = start;
}
onPlateau = false;
} else { // possible start of new plateau
onPlateau = true;
start = i;
length = 1;
}
}
// check possible plateau at the end
if (length > maxLength) {
maxLength = length;
maxStart = start;
}
}
System.out.println(maxLength);
System.out.println(maxStart);
}
Tests:
int[][] tests = {
{},
{1},
{1, 1},
{1, 2},
{1, 1, 2},
{1, 2, 2},
{1, 2, 1},
{1, 2, 3},
{1, 2, 2, 2, 3, 3, 1, 1, 1, 1},
{1, 2, 2, 2, 2, 1},
{1, 2, 2, 2, 2, 3},
{3, 2, 2, 2, 1, 2, 1, 1, 1, 2, 2, 0, 1, 1, 1, 1, 0},
{3, 3, 3, 3, 1, 1, 0, 2, 2, 2, 2, 2, 2}
};
for (int[] arr : tests) {
System.out.println(Arrays.toString(arr));
printLargestPlateau(arr);
System.out.println("-".repeat(arr.length * 3));
}
Output:
[]
-1
-1
[1]
1
0
---
[1, 1]
2
0
------
[1, 2]
1
1
------
[1, 1, 2]
1
2
---------
[1, 2, 2]
2
1
---------
[1, 2, 1]
1
1
---------
[1, 2, 3]
1
2
---------
[1, 2, 2, 2, 3, 3, 1, 1, 1, 1]
2
4
------------------------------
[1, 2, 2, 2, 2, 1]
4
1
------------------
[1, 2, 2, 2, 2, 3]
1
5
------------------
[3, 2, 2, 2, 1, 2, 1, 1, 1, 2, 2, 0, 1, 1, 1, 1, 0]
4
12
---------------------------------------------------
[3, 3, 3, 3, 1, 1, 0, 2, 2, 2, 2, 2, 2]
6
7
---------------------------------------
I have the following problem:
Given an input in base (the input is given as an array of its digits in that base), write the negation of the number in "base's"-complement notatation in outDigits.
The "base's complement" notation of a number is a generalization of "two's complement": if we treat (-x) as an unsigned number in base and add it to x, we should get 0 (modulo base^digit-size). I cannot call other function (even Math.pow)
I keep getting an error with my tests. My code:
public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
outDigits[0] = 1;
for (int i = outDigits.length - 1; i >= 0; i--) {
outDigits[i] += base - (1 + inDigits[i]);
if (i < outDigits.length - 1) {
outDigits[i + 1] = outDigits[i] / base;
}
outDigits[i] %= base;
}
}
I cannot find the error in my calculations, please help.
my test:
------------------------------------ Negate number 365 in base 10 ------------------------------------
Test case have FAILED.
Base: 10
Input number: [5, 6, 3]
Expected: [5, 3, 6]
Output: [5, 0, 0]
-------------------------------- Negate number b1010110011 in base 2 --------------------------------
Test case have FAILED.
Base: 2
Input number: [1, 1, 0, 0, 1, 1, 0, 1, 0, 1]
Expected: [1, 0, 1, 1, 0, 0, 1, 0, 1, 0]
Output: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-------------------------------------- Negate 0x7AF0 in base 16 --------------------------------------
Test case have FAILED.
Base: 16
Input number: [0, 15, 10, 7]
Expected: [0, 1, 5, 8]
Output: [0, 1, 0, 0]
Your problem is that you may seem to be trying to do the negation of the complement while calculating the complement and it is complicating your solution.
You could try to simplify your solution by splitting it into two phases:
First compute the complement.
Second add the +1 to the computed complement.
The following method is a working version of this:
public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
// Compute the complement of the digits
for (int i = outDigits.length - 1; i >= 0; i--)
outDigits[i] = base - (1 + inDigits[i]);
// Negate the complement by adding +1 to the computed number (collection of digits)
for (int i = 0; i < outDigits.length; i++) {
if (outDigits[i] == base - 1) {
// Max out digit. Set it to zero and try with the higher order next.
outDigits[i] = 0;
} else {
// Digit that has room for +1. Finally add the 1 and DONE!
outDigits[i]++;
break;
}
}
}
This approach is clearer, better performing and the code is self explanatory; but I added comments in the code to follow the logic used.
Complete code on GitHub
Hope this helps.
Since the "expected" values show that index 0 is the lowest order digit, it means that for number 123₁₀ the array would be [3, 2, 1], i.e. the digits are in reverse order of what you'd expect as a human. To a computer, it makes sense that value at index i is the value that must be multiplied by baseⁱ.
That means you need the i loop to iterate up, not down, so you can track the carry-over. Otherwise you code works fine:
public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
outDigits[0] = 1;
for (int i = 0; i < outDigits.length; i++) { // <== reversed iteration
outDigits[i] += base - (1 + inDigits[i]);
if (i < outDigits.length - 1) {
outDigits[i + 1] = outDigits[i] / base;
}
outDigits[i] %= base;
}
}
Personally, writing it like this makes more sense, especially since it doesn't rely on outDigits array to be pre-initialized to all 0's:
public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
int carry = 0;
for (int i = 0; i < outDigits.length; i++) {
outDigits[i] = (base - inDigits[i] - carry) % base;
carry = (inDigits[i] + outDigits[i] + carry) / base;
}
}
For better performance, you don't want to use % and /, so something like this might be better:
public static void baseNegate(int base, int[] inDigits, int[] outDigits) {
boolean carry = false;
for (int i = 0; i < outDigits.length; i++) {
if (carry) {
outDigits[i] = base - inDigits[i] - 1;
} else if (inDigits[i] != 0) {
outDigits[i] = base - inDigits[i];
carry = true;
}
}
}
Test
All 3 will give the same result:
public static void main(String[] args) {
test(10, 5,6,3);
test(2, 1,1,0,0,1,1,0,1,0,1);
test(16, 0,15,10,7);
test(8, 0,0,0); // 0 -> 0 (000)
test(8, 1,0,0); // 1 -> -1 (777)
test(8, 7,7,3); // 255 -> -255 (104)
test(8, 0,0,4); // -256 -> -256 (004)
}
static void test(int base, int... inDigits) {
int[] outDigits = new int[inDigits.length];
baseNegate(base, inDigits, outDigits);
System.out.printf("%d: %s -> %s%n", base, Arrays.toString(inDigits),
Arrays.toString(outDigits));
}
Output
10: [5, 6, 3] -> [5, 3, 6]
2: [1, 1, 0, 0, 1, 1, 0, 1, 0, 1] -> [1, 0, 1, 1, 0, 0, 1, 0, 1, 0]
16: [0, 15, 10, 7] -> [0, 1, 5, 8]
8: [0, 0, 0] -> [0, 0, 0]
8: [1, 0, 0] -> [7, 7, 7]
8: [7, 7, 3] -> [1, 0, 4]
8: [0, 0, 4] -> [0, 0, 4]
I think there's a problem around here:
if (i < outDigits.length - 1) {
outDigits[i + 1] = outDigits[i] / base;
}
Let's say you're using base 10. Since a digit can only be 0 through 9, dividing by 10 would mean the result of this calculation is always 0. I don't think you intended this.