So I have spent a considerable amount of time struggling to comprehend what is wrong with my code. I have an example program that I compared mine to, which works. My code is structured differently (it's all in one method, as requested by my professor) than the example (which uses two methods). I'm supposed to create a a recursive, divide-and-conquer solution to count inversions in an int array.
I am lost on why the example program maintains the manipulations to the input array throughout the recursion, while mine does not. I know Java is pass-by-value, so I am confused why the example works. Any help with me understanding the differences in these solutions would be greatly appreciated! Thanks!
Example code with two methods - merge and invCounter:
public static long merge(int[] arr, int[] left, int[] right) {
int i = 0, j = 0, count = 0;
while (i < left.length || j < right.length) {
if (i == left.length) {
arr[i+j] = right[j];
j++;
} else if (j == right.length) {
arr[i+j] = left[i];
i++;
} else if (left[i] <= right[j]) {
arr[i+j] = left[i];
i++;
} else {
arr[i+j] = right[j];
count += left.length-i;
j++;
}
}
return count;
}
//the recursive function
public static long invCounter(int[] arr) {
int sum = 0;
if (arr.length < 2)
return 0;
int m = (arr.length + 1) / 2;
int left[] = Arrays.copyOfRange(arr, 0, m);
int right[] = Arrays.copyOfRange(arr, m, arr.length);
sum += invCounter(left);
sum += invCounter(right);
sum += merge(arr, left, right);
return sum;
}
My single-method implementation (attempt):
public static int invCounter(int ranking[]) {
int sum = 0;
int result[] = new int[ranking.length];
int resIndx = 0;
if (ranking.length < 2) {
return 0; //base case
}
//divide
int left[] = Arrays.copyOfRange(ranking, 0, ranking.length/2);
int right[] = Arrays.copyOfRange(ranking, ranking.length/2,
ranking.length);
sum += invCounter(left);
sum += invCounter(right);
int i = 0, j = 0;
while (i < left.length || j < right.length) {
if (i == left.length) {
//i empty, just add j
result[resIndx++] = right[j++];
}
else if (j == right.length) {
//j empty, just add i
result[resIndx++] = left[i++];
}
else if (right[j] < left[i]) {
//inversion
result[resIndx++] = right[j++];
sum += left.length - i;
}
else {
//no inversion
result[resIndx++] = left[i++];
}
}
ranking = Arrays.copyOf(result, result.length);
return sum;
}
Why is the example program able to maintain an updated array through the recursion while mine is not?
UPDATE (10/22/15):
So I discovered that I am able to get the correct results if I replace result with ranking and just modify this array directly. My question now though is why can't I use the result array to temporarily store the results and then copy them into the ranking (argument) array at the end? This seems to me like it would be doing the same exact thing as putting the values in earlier, however the changes to ranking aren't reflected if I change it at the end.
Your method doesn't modify the rankings parameter, instead it creates a new int array (result), and you work on it. Try directly set value on the rankings array, not on result array, or simply set the result variable to the rankings.
public static int invCounter(int ranking[]) {
int sum = 0;
int result[] = ranking;
//other code...
Edit: Or you can copy it's content, but not with Arrays.copyOf, because it first CREATES a new array and then copy into it. Use instead System.arrayCopy which copies into an EXISTING array:
System.arrayCopy(result, 0, rankings, 0, result.length();
Related
I was learning to merge sort an integer array, when I noticed that while copying the sorted array elements to the original array, we need two separate loop variables to run simultaneously, while the values at those indices are copied to the original array. Here is the code for reference:
class MergeSort {
public static void sort(int arr[], int si, int ei, int mid) {
int merged[] = new int[ei - si + 1];
int index1 = si; // tracks the first array
int index2 = mid + 1; // tracks the second array
int i = 0;
while (index1 <= mid && index2 <= ei) {
if (arr[index1] <= arr[index2]) {
merged[i++] = arr[index1++];
} else {
merged[i++] = arr[index2++];
}
} // end of while
while (index1 <= mid) {
merged[i++] = arr[index1++];
}
while (index2 <= ei) {
merged[i++] = arr[index2++];
}
// to copy merged[] to arr[]
int j = si;
for (i = 0; i < merged.length; i++, j++) {
arr[j] = merged[i];
}
} // end sort()
public static void divide(int arr[], int si, int ei) {
// base case
if (si >= ei) {
return;
} // end of base case
int mid = si + (ei - si) / 2; // same as (ei-si)/2 but with less space complexity
divide(arr, si, mid);
divide(arr, mid + 1, ei);
sort(arr, si, ei, mid);
} // end of divide
public static void main(String args[]) {
int arr[] = { 1, 8, 0, 7, -4 };
int n = arr.length;
divide(arr, 0, n - 1);
for (int i = 0; i < n; i++) {
System.out.print(arr[i] + " ");
} // end of for
} // end of main
} // end of class
Notice that while copying the values of the array merged[] to the array arr[], we are using two separate variables i and j. I did try using only one loop variable, which went like:
for (int i = 0; i < arr.length; i++) {
arr[i] = merged[i];
}
but received an incorrect output. If anyone knows why we need two separate variables for the operation, please let me know. Thank you :)
You could use a single variable in this final loop, but you must add the offset of the start of the slice in the destination array:
for (int i = 0; i < arr.length; i++) {
arr[si + i] = merged[i];
}
public static void main(String[] args) {
int[] numbers = {20,4,7,6,1,3,9,5};
mergeSort(numbers);
}
private static void mergeSort(int[] inputArray) {
int inputLength = inputArray.length;
System.out.println(inputLength);
if (inputLength < 2)
return;
int midIndex = inputLength / 2;
int[] leftHalf = new int[midIndex];
int[] rightHalf = new int[inputLength - midIndex];
for (int i = 0; i < midIndex; i++) {
leftHalf[i] = inputArray[i];
}
for (int i = midIndex; i < inputLength; i++) {
rightHalf[i - midIndex] = inputArray[i];
}
mergeSort(leftHalf);
mergeSort(rightHalf);
merge(inputArray, leftHalf, rightHalf);
}
private static void merge(int[] inputArray, int[] leftHalf, int[] rightHalf) {
int leftSize = leftHalf.length;
int rightSize = rightHalf.length;
int i = 0, j = 0, k = 0;
while (i < leftSize && j < rightSize) {
if (leftHalf[i] <= rightHalf[i]) {
inputArray[k] = leftHalf[i];
i++;
} else {
inputArray[k] = rightHalf[j];
j++;
}
k++;
}
while (i < leftSize) { //eğer karşılaştırılmayan bir tane kalırsa diye yapılıyor yani
inputArray[k] = leftHalf[i];
i++;
k++;
}
while (j < rightSize) {
inputArray[k] = rightHalf[j];
j++;
k++;
}
}
In the mergeSort part if inputLength < 2 part of the code, we return when the length is less than 2. And last time the inputLength was 1, it becomes 2 and returns to the array [20,4].
This did not make sense to me logically. How does it get back to [20,4] when last we had [20] left?
first of all the code you have shared is flawed in the merge function part, you can find the proper code for Merge sort online, you can refer to
https://www.geeksforgeeks.org/java-program-for-merge-sort/
Now for understanding merge sort you have to understand the concept of stack (Last in First out) & recursion. In recursion the lines after the recursive call wait till the recursive call to the function has not executed completely. So in case of the
1st call Merge sort the length of the array is n and waiting for the complete execution of mergeSort(leftHalf) and mergeSort(rightHalf) both of size (n/2).
now for both the mergeSort(leftHalf) and mergeSort(rightHalf)
there will be sub left part and sub right part and this will continue till the size of the array becomes <2 and the remaining part will wait.
and after the successful execution of the smallest part it will return to the previous part from where this part was called. By this eventually this will return to the place where the function was called first.
And in case of your code both the smaller arrays are merged into the larger array so the data of the left and right sub array aren't lost.
I have to write a function "alternatingSum(a)" that takes an array of numbers and returns the alternating sum (where the sign alternates from positive to negative or vice versa).
For example:
int[] a = { 5, 3, 8, 4 };
Assert(alternatingSum(a) == 6); // because 5-3+8-4 == 6
So far I have tried to take the array and check to see if the index is odd (not including i=0) then subtract it from the i+1, and if it is even do the same thing except add the two. Am I on the right track with this?
Here is my code:
class foo {
public static int alternatingSum(int[] a){
int sum=0;
for (int i=0;i<a.length;i++){
if (i==0){
sum+=a[0]-a[1];
}
if (i%2 == 0){
sum+=(a[i]+a[i+1]);
}
if (i%2 == 1){
sum+=(a[i]-a[i+1]);
}
}
return sum;
}
public static void testAlternatingSum(){
System.out.println("testing code");
int[] a = { 5, 3, 8, 4 };
assert (alternatingSum(a) == 6); // because 5-3+8-4 == 6
}
public static void main(String[] args){
testAlternatingSum();
}
}
a for loop
I would just keep a boolean flag for even (and toggle it with every loop iteration). If it's an even number, than we're performing addition. But if it's not an even number (it's odd) then we can perform an unary negative operation. That might look something like,
int sum = 0;
boolean even = true;
for (int i = 0; i < a.length; i++) {
sum += even ? a[i] : -a[i];
even = !even
}
return sum;
for-each loop
and you could also use a for-each loop and write it like
boolean even = true;
int sum = 0;
for (int value : a) {
sum += even ? value : -value;
even = !even;
}
return sum;
Easy to read..
int[] alternatingSums(int[] a) {
int sum1 = 0;
int sum2 = 0;
for(int i =0; i < a.length; i++){
if((i % 2) != 1){
sum1 += a[i];
}else{
sum2 += a[i];
}
}
int[] result = {sum1 , sum2};
return result;
}
Trying to solve codility lessons for practice and working on this.
Written my code in Java and tested the code on a wide range of inputs, however the code fails for extreme_min_max, single and double in the codility test results.
Assumption given:
N is an integer within the range [1..100,000].
Each element of array A is an integer within the range [1..1,000,000,000].
Explanation of my code:
1. Sort the given array.
2. Iterate over each element in the array to find the difference between every consecutive pair. If the difference is not 1, Then its not a perm hence return 0. In case there is only one element in the array, return 1.
Can anyone please help me find out the bug(s) in my code?
My code:
public int solution(int[] A)
{
if(A.length == 1)
return 1;
Arrays.sort(A);
for (int i = 0; i < A.length-1; i++)
{
long diff = Math.abs(A[i] - A[i+1]);
if(diff!=1)
return 0;
}
return 1;
}
Here is simple and better implementation which runs in O(N) time complexity and takes O(N) space complexity.
public int solution(int[] A)
{
int size = A.length;
int hashArray[] = new int[size+1];
for (int i = 0; i < size; i++)
{
if(A[i]>size)
return 0;
else
hashArray[A[i]]+=1;
}
for(int i=1;i<=size;i++)
if(hashArray[i]!=1)
return 0;
return 1;
}
Try this in C# (Score 100%) :
using System;
using System.Linq;
class Solution {
public int solution(int[] A) {
if (A.Any(x => x == 0)) { return 0; }
var orderSelect = A.OrderBy(x => x).GroupBy(x => x);
if (orderSelect.Any(x => x.Count() > 1)) { return 0; }
var res = Enumerable.Range(1, A.Length).Except(A);
return res.Any() ? 0 : 1;
}
}
Pretty simple:
Your code doesn't check this condition:
A permutation is a sequence containing each element from 1 to N once, and only once.
Ensure that the first element after sorting is 1, and everything should work.
I'm not big on Java syntax, but what you want to do here is:
Create an array temp the length of A - initialized to 0.
Go over A and do temp[A[i]]++.
Go over temp, and if any place in the array is not 1, return false.
If duplicate exists - return 0 I have implemented with 100% pass
https://codility.com/demo/results/trainingWX2E92-ASF/
public static int permCheck(int A[]){
Set<Integer> bucket = new HashSet<Integer>();
int max = 0;
int sum=0;
for(int counter=0; counter<A.length; counter++){
if(max<A[counter]) max=A[counter];
if(bucket.add(A[counter])){
sum=sum+A[counter];
}
else{
return 0;
}
}
System.out.println(max+"->"+sum);
int expectedSum = (max*(max+1))/2;
if(expectedSum==sum)return 1;
return 0;
}
Here's my first 100% code.
I can't say if it's the fastest but it seems all correct -- watch the double OR ( || ) condition.
import java.util.Arrays;
class Solution
{
public int solution(int[] A)
{
int i = 0;
int size = A.length;
if ( size > 0 && size < 100001)
{
// Sort the array ascending:
Arrays.sort(A);
// Check each element:
for(i = 0; i < size; i++)
if ( A[i] > size || A[i] != (i + 1) )
return 0;
return 1;
}
return 0;
}
}
EDIT
Actually, we need not worry about valid first element data (i.e. A[i] > 0) because, after sorting, a valid perm array must have A[0] = 1 and this is already covered by the condition A[i] = i + 1.
The upper limit for array entries (> 1,000,000,000) is restricted further by the limit on the array size itself (100,000) and we must check for conformity here as there will be a Codility test for this. So I have removed the lower limit condition on array entries.
Below code runs and gave me a 100%, the time complexity is O(n):
private static int solution(int[] A) {
int isPermutation = 1; // all permutations start at 1
int n = A.length;
Arrays.sort(A);
if (n == 0) return 0; // takes care of edge case where an empty array is passed
for (int i = 0; i < n; i++) {
if (A[i] != isPermutation) { //if current array item is not equals to permutation, return 0;
return 0;
}
isPermutation++;
}
return 1;
}
100% score with complexity O(N)
public int solution(int[] A) {
int res = 1;
if (A.length == 1 && A[0]!=1)
return 0;
int[] B = new int[A.length];
for (int j : A) {
int p = j - 1;
if (A.length > p)
B[p] = j;
}
for (int i = 0; i < B.length - 1; i++) {
if (B[i] + 1 != B[i + 1]) {
res = 0;
break;
}
}
return res;
}
I'm having trouble combining these two algorithms together. I've been asked to modify Binary Search to return the index that an element should be inserted into an array. I've been then asked to implement a Binary Insertion Sort that uses my Binary Search to sort an array of randomly generated ints.
My Binary Search works the way it's supposed to, returning the correct index whenever I test it alone. I wrote out Binary Insertion Sort to get a feel for how it works, and got that to work as well. As soon as I combine the two together, it breaks. I know I'm implementing them incorrectly together, but I'm not sure where my problem lays.
Here's what I've got:
public class Assignment3
{
public static void main(String[] args)
{
int[] binary = { 1, 7, 4, 9, 10, 2, 6, 12, 3, 8, 5 };
ModifiedBinaryInsertionSort(binary);
}
static int ModifiedBinarySearch(int[] theArray, int theElement)
{
int leftIndex = 0;
int rightIndex = theArray.length - 1;
int middleIndex = 0;
while(leftIndex <= rightIndex)
{
middleIndex = (leftIndex + rightIndex) / 2;
if (theElement == theArray[middleIndex])
return middleIndex;
else if (theElement < theArray[middleIndex])
rightIndex = middleIndex - 1;
else
leftIndex = middleIndex + 1;
}
return middleIndex - 1;
}
static void ModifiedBinaryInsertionSort(int[] theArray)
{
int i = 0;
int[] returnArray = new int[theArray.length + 1];
for(i = 0; i < theArray.length; i++)
{
returnArray[ModifiedBinarySearch(theArray, theArray[i])] = theArray[i];
}
for(i = 0; i < theArray.length; i++)
{
System.out.print(returnArray[i] + " ");
}
}
}
The return value I get for this when I run it is 1 0 0 0 0 2 0 0 3 5 12. Any suggestions?
UPDATE: updated ModifiedBinaryInsertionSort
static void ModifiedBinaryInsertionSort(int[] theArray)
{
int index = 0;
int element = 0;
int[] returnArray = new int[theArray.length];
for (int i = 1; i < theArray.lenght - 1; i++)
{
element = theArray[i];
index = ModifiedBinarySearch(theArray, 0, i, element);
returnArray[i] = element;
while (index >= 0 && theArray[index] > element)
{
theArray[index + 1] = theArray[index];
index = index - 1;
}
returnArray[index + 1] = element;
}
}
Here is my method to sort an array of integers using binary search.
It modifies the array that is passed as argument.
public static void binaryInsertionSort(int[] a) {
if (a.length < 2)
return;
for (int i = 1; i < a.length; i++) {
int lowIndex = 0;
int highIndex = i;
int b = a[i];
//while loop for binary search
while(lowIndex < highIndex) {
int middle = lowIndex + (highIndex - lowIndex)/2; //avoid int overflow
if (b >= a[middle]) {
lowIndex = middle+1;
}
else {
highIndex = middle;
}
}
//replace elements of array
System.arraycopy(a, lowIndex, a, lowIndex+1, i-lowIndex);
a[lowIndex] = b;
}
}
How an insertion sort works is, it creates a new empty array B and, for each element in the unsorted array A, it binary searches into the section of B that has been built so far (From left to right), shifts all elements to the right of the location in B it choose one right and inserts the element in. So you are building up an at-all-times sorted array in B until it is the full size of B and contains everything in A.
Two things:
One, the binary search should be able to take an int startOfArray and an int endOfArray, and it will only binary search between those two points. This allows you to make it consider only the part of array B that is actually the sorted array.
Two, before inserting, you must move all elements one to the right before inserting into the gap you've made.
I realize this is old, but the answer to the question is that, perhaps a little unintuitively, "Middleindex - 1" will not be your insertion index in all cases.
If you run through a few cases on paper the problem should become apparent.
I have an extension method that solves this problem. To apply it to your situation, you would iterate through the existing list, inserting into an empty starting list.
public static void BinaryInsert<TItem, TKey>(this IList<TItem> list, TItem item, Func<TItem, TKey> sortfFunc)
where TKey : IComparable
{
if (list == null)
throw new ArgumentNullException("list");
int min = 0;
int max = list.Count - 1;
int index = 0;
TKey insertKey = sortfFunc(item);
while (min <= max)
{
index = (max + min) >> 1;
TItem value = list[index];
TKey compKey = sortfFunc(value);
int result = compKey.CompareTo(insertKey);
if (result == 0)
break;
if (result > 0)
max = index - 1;
else
min = index + 1;
}
if (index <= 0)
index = 0;
else if (index >= list.Count)
index = list.Count;
else
if (sortfFunc(list[index]).CompareTo(insertKey) < 0)
++index;
list.Insert(index, item);
}
Dude, I think you have some serious problem with your code. Unfortunately, you are missing the fruit (logic) of this algorithm. Your divine goal here is to get the index first, insertion is a cake walk, but index needs some sweat. Please don't see this algorithm unless you gave your best and desperate for it. Never give up, you already know the logic, your goal is to find it in you. Please let me know for any mistakes, discrepancies etc. Happy coding!!
public class Insertion {
private int[] a;
int n;
int c;
public Insertion()
{
a = new int[10];
n=0;
}
int find(int key)
{
int lowerbound = 0;
int upperbound = n-1;
while(true)
{
c = (lowerbound + upperbound)/2;
if(n==0)
return 0;
if(lowerbound>=upperbound)
{
if(a[c]<key)
return c++;
else
return c;
}
if(a[c]>key && a[c-1]<key)
return c;
else if (a[c]<key && a[c+1]>key)
return c++;
else
{
if(a[c]>key)
upperbound = c-1;
else
lowerbound = c+1;
}
}
}
void insert(int key)
{
find(key);
for(int k=n;k>c;k--)
{
a[k]=a[k-1];
}
a[c]=key;
n++;
}
void display()
{
for(int i=0;i<10;i++)
{
System.out.println(a[i]);
}
}
public static void main(String[] args)
{
Insertion i=new Insertion();
i.insert(56);
i.insert(1);
i.insert(78);
i.insert(3);
i.insert(4);
i.insert(200);
i.insert(6);
i.insert(7);
i.insert(1000);
i.insert(9);
i.display();
}
}