How to convert linear search to binary search? - java

- This is my find() method using Binary Search algorithm:
It works just as you would expect it to. No problems at all.
public int find(long searchKey) {
int lowerBound = 0;
int upperBound = nElems - 1;
int currentIndex;
while(true) {
currentIndex = (lowerBound + upperBound) / 2;
if(a[currentIndex] == searchKey)
return currentIndex; // found it!
else if(lowerBound > upperBound)
return nElems; // can't find it
else { // so then divide range
if(a[currentIndex] < searchKey)
lowerBound = currentIndex + 1; // it's in upper half
else
upperBound = currentIndex - 1; // it's in lower half
} // end else divide range
} // end while loop
} // end find() method
Here's the original insert() method using linear search. Pretty straightforward, right?
public void insert(long value) { // put element into array
int j;
for(j=0; j<nElems; j++) // find where it goes
if(a[j] > value) // (linear search)
break;
for(int k=nElems; k>j; k--) // move bigger ones up
a[k] = a[k-1];
a[j] = value; // insert it
nElems++; // increment size
} // end insert()
I need to modify the insert() method to use the binary search algorithm of the find() method. Here's what I came up with so far. Obviously there's something wrong with it, but I can't seem to find the problem. It doesn't work at all, i.e. no insertions are performed:
public int insertBS(long value) {
int lowerBound = 0;
int upperBound = nElems - 1;
int curIn;
while(true) {
curIn = (lowerBound + upperBound) / 2;
if(a[curIn] == value)
return curIn;
else if(lowerBound > upperBound)
return nElems;
else {
if(a[curIn] < value)
lowerBound = curIn + 1;
else
upperBound = curIn - 1;
}
for(int k=nElems; k>curIn; k--) // move bigger one up
a[k] = a[k-1];
a[curIn] = value;
nElems++;
}
}
Language: Java
Using ordered array.

well, it's obvious why the value isn't inserted, it's because you never inserted the value. Once you found the index of the position to insert you simply return from the function without doing anything.

Um, why not just CALL your find function?
public int insertBS(long value) {
int curIn = find(value); // find where it goes (binary search)
for(int k=nElems; k>curIn; k--) // move bigger one up
a[k] = a[k-1];
a[j] = value; // insert it
nElems++; // increment size
}
This way, when you optimize/change your find function, your insert function will go faster, too!
As a side note, I think your find function will not give you expected behavior, as written. If you have a list of [0,1,4,5,9] and I search for 7, I will get an index of nElems (5), which could be misinterpreted as the values at indexes 0 to 4 are all less than 7. Seems a little wonky.

You need to perform the binary search to find the insertion index before moving elements. In your last code snippet, you are attempting to use the variable curIn to move elements inside your while loop before your binary search has finished. Try moving the for loop outside of the while loop.

int lowerBound = 0;
int upperBound = nElems-1;
int pos = 0;
if(value < a[0])
pos=0;
else if(nElems>0 && value>a[upperBound])
pos=nElems;
else
{
while(nElems>1)
{
int j = (lowerBound + upperBound ) / 2;
if(upperBound - lowerBound ==0)
{
pos = lowerBound+1;
break; // found it
}
else if(upperBound - lowerBound ==1)
{
pos=upperBound; //lo encontre
break;
}
else // divide range
{
if(a[j] < value)
lowerBound = j + 1; // it's in upper half
else
upperBound = j - 1; // it's in lower half
} // end else divide range
}
}
for(int k=nElems; k>pos; k--) // move higher ones up
a[k] = a[k-1];
a[pos] = value; // insert it
nElems++; // increment size

Related

Why does this algorithm not sort the last index correctly?

I am supposed to write a version of the selection sort algorithm that moves the smallest number to the front of the array while simultaneously moving the largest number to the end of the array. I understand that it is basically working from both ends in so there are two sorted sub arrays, however my code seems to misplace the last number in the array. My code:
public static void dualSelectionSort(int[]a) {
for(int i=0; i<a.length-1;i++) {
int min=i;
int max=(a.length-1)-i;
for (int j=i+1;j<a.length-i;j++) {
if(a[j]<a[min]) {
min=j;
}
if(a[j]>a[max]) {
max=j;
}
}
int temp1=a[min];
int temp2=a[max];
a[min]=a[i];
a[max]=a[(a.length-1)-i];
a[i]=temp1;
a[(a.length-1)-i]=temp2;
}
}
If given the input [5,4,3,2,1], it outputs [1,2,5,3,4] instead of [1,2,3,4,5].
What am I missing?
The problem is in the inner loop.
It is starting from i+1.
So the code for comparing max is actually comparing a[1] to a[4].
you can change it as
for (int j=i;j<a.length-i;j++)
Also as you can read in comment there can be a case where with the above change the code will still not work.
This will happen because i == max and so the existing swap approach will not work.
For example:
assume array =[6,4,5],inner loop max =0,min=1.Existing swap will make it [4,6,6].
Hence,swap should be handled differently.
if(i==max) {
swap(a,max,a.length-1-i);
swap(a,min,i);
}else {
swap(a,min,i);
swap(a,max,(a.length-1)-i);
}
The complete code is as below:
public static void dualSelectionSort(int[]a) {
for(int i=0; i<a.length-1;i++) {
int min=i;
int max=(a.length-1)-i;
for (int j=i;j<a.length-i;j++) {
if(a[j]<a[min]) {
min=j;
}
if(a[j]>a[max]) {
max=j;
}
}
if(i==max) {
swap(a,max,a.length-1-i);
swap(a,min,i);
}else {
swap(a,min,i);
swap(a,max,(a.length-1)-i);
}
}
}
public static void swap(int[] a,int i,int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
In each iteration of the outer loop, you start with the first index (i) as the initial minimum index and the last index (a.length-1-i) as the initial maximum index. Then you compare both of these indices to all the indices in the range i+1 to a.length-i-1. But if the first index (i) actually contain the max value, you never compare it to a[max], so a[max] will never contain the correct value in the end of the inner loop.
Here's a suggested fix:
public static void dualSelectionSort(int[]a) {
for(int i = 0; i < a.length - i - 1; i++) {
if (a[i] > a[(a.length - 1) - i]) {
int temp = a[i];
a[i] = a[(a.length - 1) - i];
a[(a.length - 1) - i] = temp;
}
int min = i;
int max = (a.length - 1) - i;
for (int j = i + 1; j < (a.length -1 ) - i; j++) {
if(a[j] < a[min]) {
min = j;
}
if(a[j] > a[max]) {
max = j;
}
}
int temp1 = a[min];
int temp2 = a[max];
a[min] = a[i];
a[max] = a[(a.length - 1) - i];
a[i] = temp1;
a[(a.length - 1) - i] = temp2;
}
}
The three things I changed:
The outer loop can end much sooner than you end it - once i >= a.length-i-1 - since in each iteration you find the minimum and maximum value, so you reduce your remaining unsorted array by 2.
When initializing the min and max indices, make sure min index actually contains a value smaller the max index. If a[i] > a[(a.length-1)-i], swap them before setting min to i and max to (a.length-1)-i.
The inner loop should iterate j from i+1 to (a.length-1)-i-1.

Code for finding binomial coefficient in iterative form

I've written the code for finding the binomial coefficient in recursive form:
public int binom(int n, int k)
{
if (k==n || k==0)
return 1;
else return binom(n-1,k-1) + binom(n-1, k);
}
How can I rewrite this code in iterative form instead of recursive form?
Instead of building the entire Pascal triangle up to the n-th row (memory usage grows quadratically with n), we can simply focus on the row itself, and use constant memory.
Let's find a relationship between consecutive terms on the same row on Pascal's triangle:
Thus we can iteratively generate the terms from nC0 = 1:
public static int binom(int n, int k)
{
int value = 1;
// need to be careful here - can't just use *= due to integer division
for (int i = 0; i < k; i++)
value = (value * (n - i)) / (i + 1);
return value;
}
public int binom(int n, int k)
{
int C[][] = new int[n+1][k+1];
int i, j;
int min;
// Caculate value of Binomial Coefficient in bottom up manner
for (i = 0; i <= n; i++)
{
min = (i<k)? i: k;
for (j = 0; j <= min; j++)
{
// Base Cases
if (j == 0 || j == i)
C[i][j] = 1;
// Calculate value using previosly stored values
else
C[i][j] = C[i-1][j-1] + C[i-1][j];
}
}
return C[n][k];
}

Java Array Manipulation and Recursion

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();

Using quicksort on a string array

I'm a programming student and rather than post the whole assignment I'll just ask for help solving what I've tried for hours now to understand. I'm tasked with sorting an array of strings using the quicksort method. Everything else I've been tasked with as part of this problem is fine but when I tested the sorting method by printing out the String Array, it's completely jumbled up without any seeming rhyme or reason. Please help me pinpoint the error in my code, or the several glaring errors I've overlooked. The array of strings provided is this list of 65 names: http://pastebin.com/jRrgeV1E and the method's code is below:
private static void quickSort(String[] a, int start, int end)
{
// index for the "left-to-right scan"
int i = start;
// index for the "right-to-left scan"
int j = end;
// only examine arrays of 2 or more elements.
if (j - i >= 1)
{
// The pivot point of the sort method is arbitrarily set to the first element int the array.
String pivot = a[i];
// only scan between the two indexes, until they meet.
while (j > i)
{
// from the left, if the current element is lexicographically less than the (original)
// first element in the String array, move on. Stop advancing the counter when we reach
// the right or an element that is lexicographically greater than the pivot String.
while (a[i].compareTo(pivot) < 0 && i <= end && j > i){
i++;
}
// from the right, if the current element is lexicographically greater than the (original)
// first element in the String array, move on. Stop advancing the counter when we reach
// the left or an element that is lexicographically less than the pivot String.
while (a[j].compareTo(pivot) > 0 && j >= start && j >= i){
j--;
}
// check the two elements in the center, the last comparison before the scans cross.
if (j > i)
swap(a, i, j);
}
// At this point, the two scans have crossed each other in the center of the array and stop.
// The left partition and right partition contain the right groups of numbers but are not
// sorted themselves. The following recursive code sorts the left and right partitions.
// Swap the pivot point with the last element of the left partition.
swap(a, start, j);
// sort left partition
quickSort(a, start, j - 1);
// sort right partition
quickSort(a, j + 1, end);
}
}
/**
* This method facilitates the quickSort method's need to swap two elements, Towers of Hanoi style.
*/
private static void swap(String[] a, int i, int j)
{
String temp = a[i];
a[i] = a[j];
a[j] = temp;
}
Ok, i was mistaken that it would work and found your tiny mistake.
Take a look at wikipedias pseudo code
You will notice that your conditions in the while loop are causing the error
if you change (a[i].compareTo(pivot) < 0 && i <= end && j > i) and (a[j].compareTo(pivot) > 0 && j >= start && j >= i) to
(a[i].compareTo(pivot) <= 0 && i < end && j > i) and (a[j].compareTo(pivot) >= 0 && j > start && j >= i).
Thought this would help for those who seek for a string sorting algorithm based on quick sorting method.
public class StringQuickSort {
String names[];
int length;
public static void main(String[] args) {
StringQuickSort sorter = new StringQuickSort();
String words[] = {"zz", "aa", "cc", "hh", "bb", "ee", "ll"}; // the strings need to be sorted are put inside this array
sorter.sort(words);
for (String i : words) {
System.out.print(i);
System.out.print(" ");
}
}
void sort(String array[]) {
if (array == null || array.length == 0) {
return;
}
this.names = array;
this.length = array.length;
quickSort(0, length - 1);
}
void quickSort(int lowerIndex, int higherIndex) {
int i = lowerIndex;
int j = higherIndex;
String pivot = this.names[lowerIndex + (higherIndex - lowerIndex) / 2];
while (i <= j) {
while (this.names[i].compareToIgnoreCase(pivot) < 0) {
i++;
}
while (this.names[j].compareToIgnoreCase(pivot) > 0) {
j--;
}
if (i <= j) {
exchangeNames(i, j);
i++;
j--;
}
}
//call quickSort recursively
if (lowerIndex < j) {
quickSort(lowerIndex, j);
}
if (i < higherIndex) {
quickSort(i, higherIndex);
}
}
void exchangeNames(int i, int j) {
String temp = this.names[i];
this.names[i] = this.names[j];
this.names[j] = temp;
}
}

How to find a place where to insert value into ordered array using binary search?

I am doing programming project from book about data structures and algorithms and I need to implement insertion into ordered array using binary search.
My initial implementation for this using linear approach is:
public void insert(long value) { // put element into array
int j;
for (j = 0; j < nElems; j++) // find where it goes
if (a[j] > value) // (linear search)
break;
for (int k = nElems; k > j; k--) // move bigger ones up
a[k] = a[k-1];
a[j] = value; // insert it
nElems++; // increment size
} // end insert()
But, I am stuck when I tried to create something similar using binary search approach.
Here is what I did:
public void insert(long value) {
int lowerBound = 0;
int upperBound = nElems-1;
int curIn;
while(lowerBound < upperBound) {
curIn = (lowerBound + upperBound) / 2;
if(arr[curIn]>value && arr[curIn-1] < value) {
for(int k=nElems; k>curIn; k--)
arr[k] = arr[k-1];
arr[curIn] = value;
} else {
if(arr[curIn] > value)
upperBound = curIn-1;
else
lowerBound = curIn+1;
}
}
} // end insert()
I think my main mistake is the following :
I don't have any logic which handles empty array case.
Give me some advice please. I just started to learn this stuff about a week ago, so some explanation would be great.
Thank you in advance, Nick.
During the loop you can keep a loop invariant(insert position always in interval [lowerBound upperBound]).
So when arr[curIn] > value, halve the interval to [lowerBound curIn]
when arr[curIn] <= value, halve the interval to [curIn+1 upperBound]
After the loop, lowerBound is the position to insert.
//Assume arr, nElems are declared somewhere else and enough space to insert...
public void insert(long value) {
int lowerBound = 0;
int upperBound = nElems;
int curIn;
while(lowerBound < upperBound) {
curIn = (lowerBound+upperBpund)/2;
if(arr[curIn] > value) {
upperBound = curIn;
} else {
lowerBound = curIn+1;
}
}
//note lowerBound may equal nElems, it works anyway
for(int k = nElems; k > lowerBound; k--) {
arr[k] = arr[k-1];
}
arr[lowerBound] = value;
nElems++;
}

Categories

Resources