Related
I'm trying to understand the logic behind the following code however I'm unclear about 2 parts of the code partially because the math supporting the logic is not totally clear to me at this moment.
CONFUSION 1: I don't understand why would we put 0 with count = 1 in the map before we start finding the sum of the array? How does it help?
CONFUSION 2: If I move the map.put(sum, map.getOrDefault(sum)+1) after the if() condition, I get the correct solution. However if I put it at the place as shown in the code below, it gives me wrong result. The question is why does the position of this matters, when we're searching for the value of sum-k in the map for finding the count
public int subarraySum(int[] nums, int k) {
HashMap<Integer,Integer> prefixSumMap = new HashMap<>();
prefixSumMap.put(0, 1); // CONFUSION 1
int sum = 0;
int count = 0;
for(int i=0; i<nums.length; i++) {
sum += nums[i];
prefixSumMap.put(sum, prefixSumMap.getOrDefault(sum, 0)+1); //CONFUSION 2
if(prefixSumMap.containsKey(sum - k)) {
count += prefixSumMap.get(sum - k);
}
}
return count;
}
You may find this interesting. I modified the method to use longs to prevent integer overflow resulting in negative numbers.
Both of these methods work just fine for positive numbers. Even though the first one is much simpler, they both return the same count for the test array.
public static void main(String[] args) {
Random r = new Random();
long[] vals = r.longs(10_000_000, 1, 1000).toArray();
long k = 29329;
System.out.println(positiveValues(vals, k));
System.out.println(anyValues(vals, k));
public static int positiveValues(long[] array, long k) {
Map<Long,Long> map = new HashMap<>(Map.of(0L,1L));
int count = 0;
long sum = 0;
for (long v : array) {
sum += v;
map.put(sum,1L);
if (map.containsKey(sum-k)) {
count++;
}
}
return count;
}
public static int anyValues(long[] nums, long k) {
HashMap<Long,Long> prefixSumMap = new HashMap<>();
prefixSumMap.put(0L, 1L);
long sum = 0;
int count = 0;
for(int i=0; i<nums.length; i++) {
sum += nums[i];
prefixSumMap.put(sum, prefixSumMap.getOrDefault(sum, 0L)+1L);
if(prefixSumMap.containsKey(sum - k)) {
count += prefixSumMap.get(sum - k);
}
}
return count;
}
Additionally, the statement
long v = prefixSumMap.getOrDefault(sum, 0L) + 1L;
Always returns 1 for positive arrays. This is because previous sums can never be re-encountered for positive only values.
That statement, and the one which computes count by taking a value from the map is to allow the array to contain both positive and negative numbers. And ths same is true a -k and all positive values.
For the following input:
long[] vals = {1,2,3,-3,0,3};
The subarrays that sum to 3 are
(1+2), (3), (1+2+3-3), (1+2+3-3+0), (3-3+0+3), (0+3), (3)
Since adding negative numbers can result in previous sums, those need to be
accounted for. The solution for positive values does not do this.
This will also work for all negative values. If k is positive, no subarray will be found since all sums will be negative. If k is negative one or more subarrays may possibly be found.
#1: put(0, 1) is a convenience so you don't have to have an extra if statement checking if sum == k.
Say k = 6 and you have input [1,2,3,4], then after you've processed the 3 you have sum = 6, which of course means that subarray [1, 2, 3] needs to be counted. Since sum - k is 0, get(sum - k) returns a 1 to add to count, which means we don't need a separate if (sum == k) { count++; }
#2: prefixSumMap.put(sum, prefixSumMap.getOrDefault(sum, 0)+1) means that the first time a sum is seen, it does a put(sum, 1). The second time, it becomes put(sum, 2), third time put(sum, 3), and so on.
Basically the map is a map of sum to the number of times that sum has been seen.
E.g. if k = 3 and input is [0, 0, 1, 2, 4], by the time the 2 has been processed, sum = 3 and the map contains { 0=3, 1=1, 3=1 }, so get(sum - k), aka get(0), returns 3, because there are 3 subarrays summing to 3: [0, 0, 1, 2], [0, 1, 2], and [1, 2]
Similar if k = 4 and input is [1, 2, 0, 0, 4], by the time the 4 has been processed, sum = 7 and the map contains { 0=1, 1=1, 3=3, 7=1 }, so get(sum - k), aka get(3), returns 3, because there are 3 subarrays summing to 3: [0, 0, 4], [0, 4], and [4].
Note: This all assumes that values cannot be negative.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
In the last hours I have tried to write the selection sort algorithm in Java without looking at finished code. I just read how the algorithm works (by words).
I won't copy paste the explanation but rather depict it (to check if I understood it):
-We have one unsorted array A and an empty array B (which has the same size as A)
-Now take the unsorted array A and find its smallest element. Smallest element found, now switch this element with the first element of the unsorted array A
-Put the first element (=smallest element of array A) into the array B
-Repeat till we are done with every element of A
I tried to code that in Java:
public class Selectionsort{
public static void main(String[] args) {
int[] myArray = {9,6,1,3,0,4,2};
int[] B = new int[myArray.length];
for(int i=0; i<myArray.length; i++) {
B[i]=findMIN(myArray, i);
}
}
static int findMIN(int[] A, int c) {
int x = A[c];
while(c<A.length) {
if(x>A[c]) {
x=A[c];
}
c++;
}
return x;
}
}
But I get a weird output:
0 0 0 0 0 2 2
First, fix your findMIN, you should return the index of the minimum element (and compare the element at the current value to the minimum). Like,
static int findMIN(int[] A, int c) {
int x = c;
for (; c < A.length; c++) {
if (A[c] < A[x]) {
x = c;
}
}
return x;
}
Then I would use a swap method, like
static void swap(int[] A, int a, int b) {
if (a != b) {
int t = A[a];
A[a] = A[b];
A[b] = t;
}
}
Finally, tie it together like
public static void main(String[] args) {
int[] myArray = { 9, 6, 1, 3, 0, 4, 2 };
for (int i = 0; i < myArray.length; i++) {
swap(myArray, i, findMIN(myArray, i));
}
System.out.println(Arrays.toString(myArray));
}
And, I get
[0, 1, 2, 3, 4, 6, 9]
Why doesn't my selection sort algorithm do what it's supposed to
(java)?
Lets see whats happening:
c A[c] Returned X
-----------------------------------
0 9 0(Since minElement is 0 with index >= 0)
1 6 0(Since minElement is 0 with index >= 1)
2 1 0(Since minElement is 0 with index >= 2)
3 3 0(Since minElement is 0 with index >= 3)
4 0 0(Since minElement is 0 with index >= 4)
5 4 2(Since minElement is 2 with index >= 5)
6 2 2(Since minElement is 2 with index >= 6)
"Smallest element found, now switch this element with the first element of the unsorted array A". So, you must switch the first element with the smallest element. You can use the code like this:
static int findMIN(int[] A, int c) {
int first = c; // The first element's id
int id = c; // An id of the smallest element
int x = A[c];
while(c<A.length) {
if(x>A[c]) {
x=A[c];
id = c;
}
c++;
}
// Switching the first element with the smallest element
int tmp = A[first];
A[first] = A[id];
A[id] = tmp;
return x;
}
It works perfectly. Also check #VidorVistrom's answer, if you don't understand why does your code work as it works.
Your findMIN function always returns min value of the myArray starting from ith index. You are getting first 5 values 0 because, myArray[4] contains 0. Then for the last two digits you are getting 2 because from myArray[5] to myArray[6] min value is 2.
Just after finding min value in your findMIN function you should swap this value with the initial index 'c' you passed into the function. In that case next time when you call findMIN function it will exclude that min value you got earlier.
As you did not want to see any implementation I try to say the solution in words. Hope it helps you.
You don't swap elements when you find the minimum.
Try this fix
static int findMIN(int[] A, int c) {
int x = A[c];
int min_index = c;
for(int i=c; i<A.length; i++) {
if(x>A[i]) {
x=A[i];
min_index = i;
}
}
A[min_index] = A[c];
A[c] = A[min_index];
return x;
}
Also know that Selection Sort is an in place algorithm, so you don't need a separate empty array to sort.
public class Selectionsort {
public static void main(String[] args) {
int[] myArray = {9, 6, 1, 3, 0, 4, 2};
for (int i = 0; i < myArray.length; i++) {
int pos = findMIN(myArray, i);
int element = myArray[pos];
//swap
int temp = myArray[i];
myArray[i] = element;
myArray[pos] = temp;
}
System.out.println(Arrays.toString(myArray));
}
//get the postion
static int findMIN(int[] A, int c) {
int x = A[c];
int position = c;
while (c < A.length) {
if (x > A[c]) {
x = A[c];
position = c;
}
c++;
}
return position;
}
}
I just had an online coding interview and one of the questions asked there is for a given array of integers, find out the number of pairs whose summation is equal to a certain number (passed as parameter inside the method ). For example an array is given as,
int[] a = {3, 2, 1, 45, 27, 6, 78, 9, 0};
int k = 9; // given number
So, there will be 2 pairs (3, 6) and (9, 0) whose sum is equal to 9. It's good to mention that how the pairs are formed doesn't matter. The means (3,6) and (6,3) will be considered as same pair. I provided the following solution (in Java) and curious to know if I missed any edge cases?
public static int numberOfPairs(int[] a, int k ){
int len = a.length;
if (len == 0){
return -1;
}
Arrays.sort(a);
int count = 0, left = 0, right = len -1;
while( left < right ){
if ( a[left] + a[right] == k ){
count++;
if (a[left] == a[left+1] && left < len-1 ){
left++;
}
if ( a[right] == a[right-1] && right >1 ){
right-- ;
}
right--; // right-- or left++, otherwise, will get struck in the while loop
}
else if ( a[left] + a[right] < k ){
left++;
}
else {
right--;
}
}
return count;
}
Besides, can anyone propose any alternative solution of the problem ? Thanks.
Following solution will return the number of unique pairs
public static int numberOfPairs(Integer[] array, int sum) {
Set<Integer> set = new HashSet<>(Arrays.asList(array));
// this set will keep track of the unique pairs.
Set<String> uniquePairs = new HashSet<String>();
for (int i : array) {
int x = sum - i;
if (set.contains(x)) {
int[] y = new int[] { x, i };
Arrays.sort(y);
uniquePairs.add(Arrays.toString(y));
}
}
//System.out.println(uniquePairs.size());
return uniquePairs.size();
}
The time complexity will be O(n).
Hope this helps.
You can use the HashMap<K,V> where K: a[i] and V: k-a[i]
This may result in an incorrect answer if there are duplicates in an array.
Say for instances:
int a[] = {4, 4, 4, 4, 4, 4, 4, 4, 4}
where k = 8 or:
int a[] = {1, 3, 3, 3, 3, 1, 2, 1, 2}
where k = 4.
So in order to avoid that, we can have a List<List<Integer>> , which can check each pair and see if it is already in the list.
static int numberOfPairs(int[] a, int k)
{
List<List<Integer>> res = new ArrayList<>();
Map<Integer, Integer> map = new HashMap<>();
for(int element:a)
{
List<Integer> list = new ArrayList<>();
if(map.containsKey(element))
{
list.add(element);
list.add(map.get(element));
if(!res.contains(list))
res.add(list);
}
else
map.put(k - element, element);
}
return res.size();
}
Your solution is overly complex, you can do this exercise in a much easier manner:
public static int numberOfPairs(int[] a, int k ){
int count=0;
List<Integer> dedup = new ArrayList<>(new HashSet<>(Arrays.asList(a)));
for (int x=0 ; x < dedup.size() ; x++ ){
for (int y=x+1 ; y < dedup.size() ; y++ ){
if (dedup.get(x)+dedup.get(y) == k)
count++;
}
}
return count;
}
The trick here is to have a loop starting after the first loop's index to not count the same values twice, and not compare it with your own index. Also, you can deduplicate the array to avoid duplicate pairs, since they don't matter.
You can also sort the list, then break the loop as soon as your sum goes above k, but that's optimization.
This code will give you count of the pairs that equals to given sum and as well as the pair of elements that equals to sum
private void pairofArrayElementsEqualstoGivenSum(int sum,Integer[] arr){
int count=0;
List numList = Arrays.asList(arr);
for (int i = 0; i < arr.length; i++) {
int num = sum - arr[i];
if (numList.contains(num)) {
count++;
System.out.println("" + arr[i] + " " + num + " = "+sum);
}
}
System.out.println("Total count of pairs "+count);
}
Given an array of integers and a target value, determine the number of pairs of array elements with a difference equal to a target value.
The function has the following parameters:
k: an integer, the target difference
arr: an array of integers
Using LINQ this is nice solution:
public static int CountNumberOfPairsWithDiff(int k, int[] arr)
{
var numbers = arr.Select((value) => new { value });
var pairs = from num1 in numbers
join num2 in numbers
on num1.value - k equals num2.value
select new[]
{
num1.value, // first number in the pair
num2.value, // second number in the pair
};
foreach (var pair in pairs)
{
Console.WriteLine("Pair found: " + pair[0] + ", " + pair[1]);
}
return pairs.Count();
}
Given this array
int [] myArray = {5,-11,2,3,14,5,-14,2};
You are to find the maximum sum of the values in any downsequence in an unsorted array of integers. If the array is of length zero then maxSeqValue must return Integer.MIN_VALUE.
You should print the number, 19 because the downsequence with the maximum sum is 14,5.
Downsequence number is a series of non-increasing number.
These are the codes that i used but i guess that there are some cases which is still not accounted for.
Any ideas, thanks in advance.
public class MaxDownSequence{
public int maxSeqValue(int[] a){
int sum=Integer.MIN_VALUE;
int maxsum=Integer.MIN_VALUE;
for (int i=1;i<a.length;i++){
if(a[i]<a[i-1]){
sum = a[i] + a[i-1];
if (sum>maxsum){
maxsum=sum;
}
}
else {
sum=a[i];
if (sum>maxsum){
maxsum=sum;
}
}
}
if (a.length==0){
return Integer.MIN_VALUE;
}
else{
return maxsum;
}
}
public static void main(String args[]){
MaxDownSequence mySeq = new MaxDownSequence();
int [] myArray = {5,-11,2,3,14,5,-14,2};
System.out.println(mySeq.maxSeqValue(myArray));
}
}
Take the input {3,2,1} the answer should be 6 your program gives 5.
Your approach is correct, every time you test a number in the array you check if its less than (actually this should be <=) previous array element.
If it is you update sum as: sum = a[i] + a[i-1]; this is incorrect. sum in your program represents the running rum of the current subsequence. You should not be overwriting it.
Dynamic programming is the way to go.
http://en.wikipedia.org/wiki/Dynamic_programming
I know, maybe that doesn't help at all, but since I don't want to post a solution for your problem the best thing I can do is to give you this hint :)
you haven't considered sequences of more than two numbers. If you had [3,2,1] the result should be 6. But your code would give 5, because it only looks at the sum of the current number and the previous, whereas you should keep track of a current downsequence and add the current number to the running total of that downsequence. Once you hit a number that breaks the downsequence update maxsum if needed then reset the running total to 0.
not sure why you have the else in the loop?? If a[i] is not less than a[i-1] then it is not a downsequence, therefore surely maxsum should not be updated. If you take just the first 3 numbers in your sample array, it would return the number 2. Because the first downsequence [5,-11] would give a sum of -6 and on the next iteration it would just look at 2, which is greater than -6 and therefore maxsum is updated.
No need for:
if (a.length==0){
return Integer.MIN_VALUE;
}
if the array length is 0 then you never enter the loop and therefore never change maxsum, so it will still be equal to Integer.MIN_VALUE, so you can just return maxsum at the end regardless.
You are suppose to have a running sum i think. Meaning Sum = Sum + A[i]. Just make sure to initialize the sum to the first member of the array and you are in business.
package sree;
import java.util.ArrayList;
import java.util.List;
import javax.lang.model.element.NestingKind;
public class MaximumSumSequence {
private final int[] theArray;
private MaximumSumSequence(int[] theArray) {
this.theArray = theArray;
}
private void maximumSequence() {
int currentMax = 0,currentSum = 0, start = 0, end = 0, nextStart = 0;
for (int i=0; i< theArray.length; i++) {
currentSum += theArray[ i ];
if (currentMax < currentSum) {
currentMax = currentSum;
start = nextStart;
nextStart = end;
end = i;
} else if (currentSum < 0) {
currentSum = 0;
}
}
System.out.println("Max Sum :" + currentMax);
System.out.println("Start :" + start);
System.out.println("End :" + end);
}
public static void main(String[] args) {
//int[] anArray = {4, -1, 2, -2, -1, -3};
int[] anArray ={-2, 1, -3, 4, -1, 2, 1, -5, 4};
new MaximumSumSequence(anArray).maximumSequence();
}
}
I'm trying to turn an array like this:
0, 1, 2, 2, 1, 0, 1, 0, 0, 1, 2
into this:
0 0 0 0 1 1 1 1 2 2 2
Here is my code:
public static int[] sortDNF(int[] tape) {
int smaller = 0; // everything with index < smaller is 0
int bigger = tape.length - 1; // everything with index > bigger is 2
int current = 0; // where we are looking now
int tmp;
while (current <= bigger) {
if (tape[current] == 0) {
tmp = tape[smaller];
tape[smaller] = tape[current];
tape[current] = tmp;
smaller++;
}
if (tape[current] == 2) {
tmp = tape[bigger];
tape[bigger] = tape[current];
tape[current] = tmp;
bigger--;
}
current++;
}
return tape;
}
This is what it produces:
0 0 0 1 1 1 1 0 2 2 2
What is my problem?
A guess:
You should not be increasing current every time through the loop since that is supposed to represent the partition between the 1's and the unknowns. For example, when you hit the first 2 you end up swapping it with another 2 and then moving on. The fact that the 2 later gets swapped for a 0 is accidental. The elements between smaller and current should always be 1's and that is broken.
current++ should only be done in the tape[current] != 2 case. It's ok to do it when tape[current] = 0 because you haven't changed the [smaller -> current] = 1-only condition. And it's ok to move it when tape[current] = 1 because that satisfies the 1-only condition.
...but I haven't tried it.
For those who haven't studied the problem, sorting is sufficient to provide a solution, but it is (or can be) more than necessary. Solving the DNF problem only requires that all like items be moved together, but you don't have to place unequal items in any particular order.
It's pretty easy to solve DNF with expected O(N) complexity, where (most normal forms of) sorting have O(N lg N) complexity. Instead of rearranging the input elements, it's much easier to simply count the elements with any given value, then print out the right number of each. Since the order of unequal elements doesn't matter, you normally store your counts in a hash table.
The problem is that you reply on (possible nonexistent) values later in the chain to swap incorrectly skipped 1 values, try your algo on the trivial case of:
1 2 0
the 2 gets swapped in the right spot, but the current index has been advanced over the index 0 ends up in resulting in:
1 0 2
So don't increment current before you inspect the new value at that index.
You need to increment current only during the check for 1 and check for 2. When you are checking for 3 you just need to decrement bigger. Here the working solution. BTW this works for the array of values between 1-3. You can change it to 0-2 if you so wish.
The program produces the following output given a random number array as follows:
Original Array : [1, 3, 3, 3, 2, 2, 2, 1, 2, 2, 1, 3, 3, 2, 1, 1, 3]
Sorted Array : [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3]
private static int[] sortUsingDutchNationalFlagProblem(int[] array)
{
System.out.println("Original Array : " + Arrays.toString(array));
int smaller = 0; // everything with index < smaller is 1
int bigger = array.length - 1; // everything with index > bigger is 3
int current = 0; // where we are looking now
int tmp;
while (current <= bigger) {
if (array[current] == 1) {
tmp = array[smaller];
array[smaller] = array[current];
array[current] = tmp;
smaller++;current++;
}
if(array[current] == 2)
current++;
if (array[current] == 3) {
tmp = array[bigger];
array[bigger] = array[current];
array[current] = tmp;
bigger--;
}
}
System.out.println("Sorted Array : " + Arrays.toString(array));
return array;
}
Here is my approach using Java
public static void dutchNationalFlagProblem(int[] input) {
int low=0, mid=0, high = input.length -1;
while(mid <=high) {
switch (input[mid]) {
case 0:
swap(input, low++, mid++);
break;
case 1:
mid++;
break;
default :
swap(input, mid, high--);
break;
}
}
}
private static void swap(int[] input, int firstIndex, int secondIndex) {
int temp = input[firstIndex];
input[firstIndex] = input[secondIndex];
input[secondIndex] = temp;
}
Here is the corresponding test cases
#Test
public void dutchNationalFlagProblemTest() {
int[] input = new int[]{0,1,0,2,2,1};
ArrayUtils.dutchNationalFlagProblem(input);
assertThat(input, equalTo(new int[]{0,0,1,1,2,2}));
}
I'm going to propose an easier solution :-)
public static int[] sortDNF(int[] tape) {
Arrays.sort(tape);
return tape;
}
As far as what's wrong with your solution, when the current element is 2 and it's swapped with the element near the end of the list, the element it's swapped with may not be 2, but you're incrementing current and not looking at this position again.
public static int[] sortDNF(int[] tape) {
int smaller = 0; // everything with index < smaller is 0
int bigger = tape.length - 1; // everything with index > bigger is 2
int current = 0; // where we are looking now
int tmp;
while (current <= bigger) {
if (tape[current] == 0) {
tmp = tape[smaller];
tape[smaller] = tape[current];
tape[current] = tmp;
smaller++;
}
else if (tape[current] == 2) {
tmp = tape[bigger];
tape[bigger] = tape[current];
tape[current] = tmp;
bigger--;
}
current++;
}
return tape;
}
Below program is giving right result, do you guys see any issue in this simple innocent programming approach?
public class Test {
public static void main(String[] args) {
int arr[] = {1, 2, 0, 2, 1, 1, 1, 0, 0, 2, 2, 1, 0, 1, 2, 0, 2};
int x0 = 0, x1 = 0, x2 = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] == 0)
x0++;
if (arr[i] == 1)
x1++;
if (arr[i] == 2)
x2++;
}
int i = 0;
for (int j = i; j < x0; j++)
arr[i++] = 0;
for (int j = i; j < x0+x1; j++)
arr[i++] = 1;
for (int j = i; j < x0+x1+x2; j++)
arr[i++] = 2;
printArray(arr);
}
private static void printArray(int[] arr) {
for (int a : arr) {
System.out.print(a + " ");
}
}
}