Okay okay. I have been working on a recursive selection sort in Java. I have done my reading, googling, stack overflowing, and am still unable to figure it out. I think the code is getting worse the more time I spend on it due to overcomplicating it. All the examples I have seen use multiple parameters, and the single parameter is throwing me off.
Below is the recursive method and the driver. The first 3 if statements are given so I am assuming required.
public static void selectionSort_Rec(int[] arr)
{
if(arr == null) throw new NullPointerException();
if(arr.length == 0) throw new IllegalArgumentException();
if(arr.length == 1) return;
int startIndex = 0;
if ( startIndex >= arr.length - 1 )
return;
int minIndex = startIndex;
for ( int index = startIndex + 1; index < arr.length; index++ )
{
if (arr[index] < arr[minIndex] )
minIndex = index;
}
int temp = arr[startIndex];
arr[startIndex] = arr[minIndex];
arr[minIndex] = temp;
startIndex++;
selectionSort_Rec(arr);
}
// Driver method
public static void main(String args[])
{
int arr[] = {3, 1, 5, 2, 7, 0};
// Calling function
selectionSort_Rec(arr);
for(int i :arr){
System.out.print(i);
}
}
There is some problem in your code.
at the first you use startIndex and find good number from your array for that, then increment it at the end of code which is redundant because when function called again, it use 0 for it again. each function call has it's own variable and so the next call, function create new startIndex and use that which is zero again.
You must pass it to function and increment at each next function call. because of this, you base check is not true anymore and it is changed to check until we get at the end of our array.
also this line of code is redundant because when we get to this line, we know that arr.lenghth() is more than one. (however our logic of code changed and there is no need to this too)
if ( startIndex >= arr.length - 1 )
return;
return when get to base condition like 1 is better and throw exception is not necessary because when it is one, we return without going lower. (conditions always false for zero or empty array. you don't remove anything from array too)
I define recursive function as a function that sort array from start index sent to it.
This is the result:
public class GFG
{
public static void selectionSort_Rec(int[] arr) {
selectionSortHelper(arr, 0);
}
static void selectionSortHelper(int[] arr, int startIndex) {
if (startIndex >= arr.length)
return;
int minIndex = startIndex;
for (int index = startIndex + 1; index < arr.length; index++)
{
if (arr[index] < arr[minIndex])
minIndex = index;
}
int temp = arr[startIndex];
arr[startIndex] = arr[minIndex];
arr[minIndex] = temp;
selectionSortHelper(arr, startIndex + 1);
}
// Driver method
public static void main(String args[]) {
int arr[] = {3, 1, 5, 2, 7, 0};
// Calling function
selectionSort_Rec(arr);
for (int i : arr)
{
System.out.print(i + " ");
}
}
}
I hope this is what you want.
Related
I'm trying to learn a bit Java with tutorials and currently I'm struggling with piece of code where I should find on which index is difference between arrays (if there is difference at all)
My code
Scanner scanner = new Scanner(System.in);
int[] arrOne = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int[] arrTwo = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int sumArrOne = 0;
int index = 0;
boolean diff = false;
for (int k : arrOne) {
if (Arrays.equals(arrOne, arrTwo)) {
sumArrOne += k;
} else {
for (int i : arrTwo) {
if (k != i) {
index = i;
diff = true;
break;
}
}
}
}
if (diff) {
System.out.println("Found difference at " + index + " index.");
} else {
System.out.println("Sum: " + sumArrOne);
}
So, if arrays are identical I'm sum array elements in arrOne. If they are not identical -> must show at which index they are not.
With this code when I input
1 2 3 4 5
1 2 4 3 5
I should get that difference is at index 2 instead I've got index 1.
I'm not quite sure why and would be glad if someone point me out where is my mistake.
I updated your code. Looks like you're misunderstanding the concept of indexes yet.
Use one common index to check with in both arrays, in my example it's simply called i:
import java.util.Arrays;
import java.util.Scanner;
public class BadArray {
static private final int INVALID_INDEX = Integer.MIN_VALUE;
public static void main(final String[] args) {
try (final Scanner scanner = new Scanner(System.in);) {
final int[] arrOne = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
final int[] arrTwo = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int sumArrOne = 0;
int diffIndex = INVALID_INDEX;
final int minLen = Math.min(arrOne.length, arrTwo.length);
for (int i = 0; i < minLen; i++) {
sumArrOne += arrOne[i];
if (arrOne[i] != arrTwo[i]) {
diffIndex = i;
break;
}
}
if (diffIndex != INVALID_INDEX) {
System.out.println("Found difference at " + diffIndex + " index.");
} else if (arrOne.length != arrTwo.length) {
System.out.println("Arrays are equal but have different length!");
} else {
System.out.println("Sum: " + sumArrOne);
}
}
}
}
I also put the scanner into a try-resource-catch to handle resource releasing properly.
Note you could also do the array lengths comparison right at the start if different array lengths play a more crucial role.
You are trying to find out which index has the first difference so you should iterate via the index rather than using a for-each loop (aka enhanced for loop). The following method should work for this.
/**
* Returns the index of the first element of the two arrays that are not the same.
* Returns -1 if both arrays have the same values in the same order.
* #param left an int[]
* #param right an int[]
* #return index of difference or -1 if none
*/
public int findIndexOfDifference(int[] left, int[] right) {
// short-circuit if we're comparing an array against itself
if (left == right) return -1;
for (int index = 0 ; index < left.length && index < right.length ; ++index) {
if (left[index] != right[index]) {
return index;
}
}
return -1;
}
In your code you compare, where the indexes are different, not the values at the indexes. Also your code has several other issues. I'll try to go through them step by step:
// compare the whole array only once, not inside a loop:
diff = !Arrays.equals(arrOne, arrTwo));
if (!diff) {
// do the summing without a loop
sumArrOne = Arrays.stream(arrOne).sum();
} else {
// find the difference
// it could be the length
index = Math.min(arrOne.length, arrTwo.length);
// or in some different values
for (int i = 0; i < index; i++) { // do a loop with counter
if (arrOne[i] != arrTwo[i]) {
index = i;
break;
}
}
}
It doesn't matter that I set index here above the loop as it's value will be overwritten anyways inside the loop, if relevant.
I must implement a recursive method merge(long[] arr, int i) which multiplies adjacent elements if they have the same value, starting at index i.
Example:
merge({1, 2, 2, 4}, 0)
should produce an array like this:
{1, 4, 4}
If there are multiple (n) occurrences of a number {1, 2, 2, 2, 2, 5}, all of these must be multiplied together: {1, 16, 5}.
A number which has already been merged can not be merged again {1, 4, 4, 16} -> {1, 16, 16}.
All this must be achieved by using only this one method merge and having exactly one recursive call per element in the original array.
This is a working implementation using recursion and loops:
public static long[] merge(long[] ns, int i) {
final long[] EMPTY_LONG_ARRAY = {};
if (i < 0) {
return merge(ns, 0, m); // if i negative, start at 0
} else if (i >= ns.length) {
return EMPTY_LONG_ARRAY; // if out of bounds, return empty array
} else if (i == ns.length - 1) {
return ns; // base case
} else { // recursion in here
if (ns[i] == ns[i + 1]) { // if next long is equal
int occurences = 1; // first occurence
for (int j = i; j < ns.length - 1; j++) {
if (ns[j] == ns[j + 1])
occurences++;
else
break;
} // add next occurences
long[] newArray = new long[ns.length - occurences + 1]; // new array is (occurences-1) shorter
for (int j = 0; j < newArray.length; j++) { // fill new array
if (j < i) {
newArray[j] = ns[j]; // left of i: values stay the same
} else if (j > i) {
newArray[j] = ns[j + occurences - 1]; // pull values right of i (occurences-1) to the left
} else {
int counter = occurences;
long mergedValue = ns[j];
while (counter > 1) {
mergedValue *= ns[j];
counter--;
}
newArray[j] = mergedValue; // at j: value is ns[j]^occurences
}
}
if (i == ns.length - 1)
return merge(newArray, i, m);
else
return merge(newArray, i + 1, m); // if bounds permit it, jump to next number
} else {
return merge(ns, i + 1, m); // nothing to merge, go one step forward
}
}
This implementation produces the correct result, however, the recursion depth is wrong (needs to be one recursive call per element in original array ns[]).
I'm sure there is a genius out here who can solve this using linear recursion.
Lets transform your loop into a recursive call. The only reason to do this is that the assignment asks for it - it is not more readable (at least to me), and it is actually slower. People usually want to go in the other direction for efficiency reasons: from recursion to loops.
First, an annotated version of your code:
public static long[] merge(long[] ns, int i) { // i not needed, but useful for recursion
long[] out = new long[ns.length]; // for result; allocate only once
for (int j = i; j < ns.length; j++) { // main loop, condition is "j == length"
int occurences = 0;
for (int k = i; k < ns.length; k++) { // inner loop - can avoid!
if (ns[j] == ns[k]) {
occurences++;
}
}
out[j] = (long) Math.pow(ns[j], occurences); // updating the result
}
// remove additional elements
return out; // this does not remove elements yet!
}
First, let me rewrite that to be more efficient. Since duplicates are only removed if they are next to each other, you do not need the inner loop, and can write this instead:
public static long[] merge(long[] ns) {
long[] out = new long[ns.length];
int oSize = 0; // index of element-after-last in array out
long prev = ns[0]; // previous element in ns; initial value chosen carefully
out[0] = 1; // this make the 1st iteration work right, not incrasing oSize
for (int i=0; i<ns.length; i++) {
long current = ns[i];
if (current == prev) {
out[oSize] *= current; // accumulate into current position
} else {
oSize ++; // generate output
out[oSize] = current; // reset current and prev
prev = current;
}
}
// generate final output, but do not include unused elements
return Arrays.copyOfRange(out, 0, oSize+1);
}
Assuming this works (and beware - I have not tested it), I will now transform it into tail recursion. There will be 2 parts: a driver code (everything not in the loop), and the recursive code (the loopy part).
public static long[] merge(long[] ns) {
long[] out = new long[ns.length];
int oSize = 0;
long prev = ns[0];
out[0] = 1;
int i=0;
recursiveMerge(ns, i, out, oSize, prev); // recursion!
return Arrays.copyOfRange(out, 0, oSize+1);
}
public static void recursiveMerge(long[] ns, int i, long[] out, int oSize, long prev) {
if (i == n) return; // check "loop" termination condition
// copy-pasted loop contents
long current = ns[i];
if (current == prev) {
out[oSize] *= current; // accumulate into current position
} else {
oSize ++; // generate output
out[oSize] = current; // reset current and prev
prev = current;
}
// next loop iteration is now a recursive call. Note the i+1
recursiveMerge(ns, i+1, out, oSize, prev);
}
The general idea is to pass all state as arguments to your recursive function, and check loop termination at the start, put the loop code in the middle, and at the very end, make a recursive call for the next iteration.
I always understood that defining a local variable within a loop does not slow it down because they are reused between iterations of the same loop.
I was surprised to find that when I move the definition of the local variable outside the loop, then it reduces memory significantly (39.4Mb vs 40 Mb).
Between iterations of the same loop, are local variables reused or reallocated?
I did also see Allocation of space for local variables in loops
Duplicate Zeroes Problem (leetcode): Given a fixed length array arr of integers, duplicate each occurrence
of zero, shifting the remaining elements to the right.
Note that elements beyond the length of the original array are not
written.
Do the above modifications to the input array in place, do not return
anything from your function.
import java.util.Arrays;
/**
* algorithm: the zeroes divide the array into sub-arrays or subsets.
* we move or shift the elements exactly once, to their final resting place R.I.P. ;)
* The last subset will be shifted n0s places, the one before it, n0s -1 places and so on...
* O(n)
* #author likejudo
*
*/
public class DuplicateZeroes {
static void arrayCopy(int[] arr, int begin, int end, int n) {
for (int i = end + 1; i >= begin ; i--) {
int destination = i + n;
if (destination < arr.length) {
arr[destination] = arr[i];
}
}
}
public static void duplicateZeros(int[] arr) {
int n0s = 0; // number of zeroes
int last0At = -1; // last zero at index
int boundary = 0; // rightmost boundary
// find total n0s, last0At
for (int i = 0; i < arr.length; i++) {
if (arr[i] == 0) {
n0s++;
last0At = i;
}
}
// System.out.format("n0s=%d last0At=%d \n", n0s, last0At);
// if no zeroes or all zeroes, we are done
if(n0s == 0 || n0s == arr.length) {
return;
}
boundary = arr.length - n0s;
while (n0s > 0) {
// System.out.format("before arrayCopy(%s, %d, %d, %d) ", Arrays.toString(arr), last0At, boundary, n0s);
// move subset of all elements from last0At till boundary-1, by n0s spaces.
arrayCopy(arr, last0At, boundary, n0s);
// set start of subset to 0
arr[last0At] = 0;
// System.out.format("after arrayCopy : %s assigned arr[last0At=%d]=0\n", Arrays.toString(arr),last0At);
// update boundary
boundary = last0At - 1;
// next subset to the left will have one less zero
n0s--;
last0At--;
// find the next last zer0 At index
while (last0At > 0 && arr[last0At] != 0)
last0At--;
// if no more...
if (last0At <0 || arr[last0At] != 0) {
return;
}
}
}
public static void main(String[] args) {
// input: [1, 0, 2, 3, 0, 4, 5, 0]
// output: [1, 0, 0, 2, 3, 0, 0, 4]
int[] arr = {0,0,0,0,0,0,0};
System.out.println("input: " + Arrays.toString(arr));
duplicateZeros(arr);
System.out.println("output: " + Arrays.toString(arr));
}
}
In the method arrayCopy, when I move the local variable destination outside the loop,
Before
static void arrayCopy(int[] arr, int begin, int end, int n) {
for (int i = end + 1; i >= begin ; i--) {
int destination = i + n; // >>>>>>>>>>>>>>>>>>>>>>>
if (destination < arr.length) {
arr[destination] = arr[i];
}
}
}
After
memory usage improved! (39.4 Mb vs 40 Mb)
static void arrayCopy(int[] arr, int begin, int end, int n) {
int destination = 0; // >>>>>>>>>>>>>>>>>
for (int i = end + 1; i >= begin ; i--) {
destination = i + n;
if (destination < arr.length) {
arr[destination] = arr[i];
}
}
}
About your question
I always understood that defining a local variable within a loop does
not slow it down because they are reused between iterations of the
same loop.
declaring local variable inside loop does not slow it down?
Yes, you are right. Declaring local vars does not increase the time complexity, or if it does change the runtime just a bit, it's too insignificant to be considered.
Runtime and memory measurements of LeetCode are highly inaccurate, especially runtime. For instance, I just resubmitted the following solution and it says 39.6 MB, some days ago said 43.3 MB for the exact same solution without a byte change:
Their test cases are usually limited because it is costly I guess, thus their benchmarking is not valuable.
public final class Solution {
public static final void duplicateZeros(int[] arr) {
int countZero = 0;
for (int index = 0; index < arr.length; index++)
if (arr[index] == 0) {
countZero++;
}
int length = arr.length + countZero;
for (int indexA = arr.length - 1, indexB = length - 1; indexA < indexB; indexA--, indexB--)
if (arr[indexA] != 0) {
if (indexB < arr.length) {
arr[indexB] = arr[indexA];
}
} else {
if (indexB < arr.length) {
arr[indexB] = arr[indexA];
}
indexB--;
if (indexB < arr.length) {
arr[indexB] = arr[indexA];
}
}
}
}
Overall it'd be best to focus on asymptotically efficient algorithms mostly, because benchmarking has lots of "how-tos" and we'd want to have really good resources (CPU, memory, etc.) with isolated test systems.
References
For additional details, please see the Discussion Board where you can find plenty of well-explained accepted solutions with a variety of languages including low-complexity algorithms and asymptotic runtime/memory analysis1, 2.
For example if I have an array of ints as a parameter to my method, my method needs to return an array of ints where each element is the sum of all the elements following it.
Example:
parameter is [5, 6, 7, 2, 3, 1] I need to return [24, 19, 13, 6, 4, 1]
I have a written a helper method that correctly adds an index to all of the ones after it here:
public static int sum(int[] array, int index) {
if (index == array.length) {
return array[array.length-1];
} else {
return array[index] + sum(array, index + 1);
}
}
This all works as it should, but I'm having trouble with the original method here:
public int[] reverseCumulative(int[] numbers) {
int[] temp = new int[numbers.length];
if (numbers.length == 0) {
return temp;
}
else {
temp[numbers.length-1] = sum(numbers, numbers.length);
numbers = Arrays.copyOf(numbers, numbers.length - 1);
reverseCumulative(numbers);
return temp;
}
}
The output here is [0, 0, 0, 0 , 0, 1]. I understand that this is most likely due to the fact I'm creating a new int[] temp every time I call the reverseCumulative method within itself, but I am completely lost and any push in the right direction would be appreciated.
Edit: Forgot to add, I am not allowed to use any loops.
Since you are creating a new array each time you call the reverseCumulative method, you should use the sum method as a helper method inside the reverseCumulative method so that you are still using recursion. For example,
public int[] reverseCumulative(int[] numbers) {
int[] temp = new int[numbers.length];
if (numbers.length == 0) {
return temp;
}
else {
for(int i = 0; i < numbers.length; i++){
temp[i] = sum(numbers,i);
}
return temp;
}
}
This way, each element in temp equals a sum of integers in the numbers array depending on which iteration it is currently on. In the first iteration, temp[0] = the sum of all the ints in numbers. In the second iteration, temp[1] = the sum of all the ints in numbers except the first int and so on. However, the way the sum method is written right now, it adds the last element twice so here's a simple fix,
public static int sum(int[] array, int index) {
if (index == array.length-1) {
return array[array.length-1];
} else {
return array[index] + sum(array, index + 1);
}
}
Maybe something like this?
public static int sum(final int[] target, final int[] source, int index) {
if (index >= source.length - 1)
return source[index];
return target[index] = source[index] + sum(target, source, index + 1);
}
public static int[] reverseCulmulative(final int[] array) {
final int[] target = array.clone();
sum(target, array, 0);
return target;
}
I am working on a school assignment and I have the following question:
I am given a number of sticks (with distinct or similar length), and am tasked to find out the minimum number of sticks required to form a longer stick of given length.
For instance,
Given 6 sticks of length 1,1,1,1,1,3 to form a longer stick of length 5, the output would be 3.
NOTE: Sticks cannot be reused.
However, if it is impossible to form the given length, output -1.
For instance,
Given 3 sticks of length 1,2,6, to form a longer stick of length 5, output would be -1.
I have the following code, which have passed all public test cases. However, I failed the private test cases which I cannot figure out my mistake.
Here's my code:
import java.util.*;
class Result {
static int min = 100000;
public static int solve(int pos, int currSum, int len, int numStk) {
// implementation
for (int i=1; i<=Stick.data.length - pos; i++){
if (currSum > len){
continue;
}
else if (currSum < len){
if (pos+i >= Stick.data.length){
break;
}
else{
solve(pos+i,currSum+Stick.data[pos+i], len, numStk+1);
}
}
else if (currSum == len){
if (numStk < min){
min = numStk;
}
}
}
return min;
}
}
class Stick {
static int[] data;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int sticks = sc.nextInt();
data = new int[sticks];
int len = sc.nextInt();
for (int i=0; i<sticks; i++){
data[i] = sc.nextInt();
}
for (int i=0; i<sticks; i++){
Result.solve(i,0,len,1);
}
if (Result.min == 100000){
System.out.println(-1);
} else {
System.out.println(Result.min-1);
}
}
}
Things I notice about your code:
Bug: In main,
Result.solve(i,0,len,1);
assumes that stick i is taken (hence numsticks = 1 in the arguments list), but currSum is given as 0. Shouldn't that be data[i]?
Better code quality: The checks for currSum > len and currSum == len can be done outside the for loop, which is more efficient.