Related
I'm trying to figure out the running time complexity of the findingDup algorithm because I'm unsure if it's O(n) or O(log n). My goal is to implement a sublinear algorithm that finds how many times an int value is duplicated. You can assume the given array int[] A is always sorted. If you have any additional questions please leave them below.
public class Controller {
public static void main(String[] args){
int[] A = {-1, 2, 3, 5, 6, 6, 6, 9, 10};
int value = 6;
System.out.println(findingDup(A, value));
}// end main
public static int findingDup(int[] a, int x){
int counter = 0;
int index = binarySearch(a, x); // index = 4
int leftIndex = index - 1; // leftIndex = 3
int rightIndex = index + 1; // rightIndex = 5
if(index == -1){
return 0;
}
else if(a[index] == x){
counter++;
}
// checking if all numbers are dups
if(a[0] == a[a.length - 1]){
return a.length;
}
if(leftIndex >= 0){
while(a[leftIndex] == x){
counter++;
leftIndex--;
if(leftIndex < 0){
break;
}
}
}
if(rightIndex <= a.length - 1){
while(a[rightIndex] == x){
counter++;
rightIndex++;
if(rightIndex > a.length - 1){
break;
}
}
}
return counter;
}// end method
public static int binarySearch(int[] a, int x){
int low = 0, high = a.length - 1;
while(low <= high){
int mid = (low + high) / 2;
if(a[mid] < x){
low = mid + 1;
}
else if(a[mid] > x){
high = mid - 1;
}
else{
return mid;
}
}
return -1;
}// End Method
}// end class
Your code is O(k + log n), where "k" is number of times the value is present on the list.
If the k = O(n) it degrades to O(n).
As an example, in the extreme case of the list being [6, 6, 6, 6, 6, ...] you will end-up processing all the elements.
You can still fix this problem by running more than one binary search.
First you run it to find first occurrence of "value", and then you run it again to find a first number larger than value (search for value+1).
Your binary search algorithm needs to be modified to return the first occurrence of the value, or larger value if the value cannot be found.
As of now it finds any occurrence, not guaranteed to be the first one nor the last one.
Your binary search has the following condition:
if (smaller) {...}
else if (larger) {...}
else {we have found it!}
So it can return any occurrence.
You should be looking for an index that:
a[mid - 1] < value && a[mid] >= value
mid-1 can be smaller than 0, so you need to check for that first.
If this is not the case, we haven't found the first occurrence, and need to move either left or right index.
Below, I have designed a function tournamentTreeKSelection which simulates a tree like structure using arrays and returns the largest element in the array. For example, given an input array [10,9,8,7,6,5,4,3,2,1] the following steps are performed to return 10.
[10, 8, 6, 4, 2, -1]
[10, 6, 2, -1]
[10, 2]
[10] //Max element of array found
My goal is to now add a second parameter int k requesting that the function return the k-th largest element such that tournamentTreeKSelection(data, 2) returns 9.
I'm having a lot of difficulty in modifying my algorithm to perform this task because my assumption is that i'm going to have to keep track of all elements that the max element beats ? Any help is appreciated.
import java.util.ArrayList;
import java.util.Arrays;
public class TournamentTree {
public static int tournamentTreeKSelection(int[] data, int k) {
ArrayList<Integer> list = new ArrayList<>();
ArrayList<Integer> list2 = new ArrayList<>();
for(int i = 0; i < data.length - 1; i += 2) {
list.add(max(data[i] , data[i + 1]));
}
for(int i = 0; i < data.length - 1; i++) {
list2.add(min(data[i], data[i + 1]));
}
if(list.size() == 1) return list.get(0);
if(list.size() % 2 != 0) list.add(-1);
if(k == 1) return tournamentTreeKSelection(listToArray(list),k);
else return tournamentTreeKSelection(listToArray(list2), --k);
}
public static int max(int a, int b) {
return a > b ? a : b;
}
public static int min(int a, int b) {
return a > b ? b : a;
}
public static int[] listToArray(ArrayList<Integer> arr) {
int[] arr2 = new int[arr.size()];
for(int i = 0; i < arr.size(); i++)
arr2[i] = arr.get(i);
return arr2;
}
}
I have now modified the code but it only works for k = 1 - 8, why does it break down ? tournamentTreeKSelection(data, 9) and tournamentTreeKSelection(data, 10) return 3 when they should be returning 2 and 1 respectively.
First of all, why your code is wrong:
When the size of the list is 2 or 3, your statement list.size() == 1 will be true even if K > 1.
Why do you do min(data[i], data[i + 1]), I have a feeling you just want to remove the maximum element but what with the case
[10,1,9,2,8,3,7,4,6,5], gives after 1 iteration [1,1,2,2,3,3,4,4,5] removing possible outcomes 9, 8, 7 and 6.
Some tips
Don't do useless computations. You are calculating the two lists, while you know in front you are only going to use one of them.
Use builtin methods whenever possible, see Math.max, Math.min
Note that you know the size of the resulting array in front. There is no need to create an ArrayList which causes a lot of overhead for you. Just create an array of the resulting size. For k==1, ((data.length+1)/2) else data.length-1
Still wondering
You say your tournament tree structure is a requirement, but you are looping over it in your code as it is an array. Why? You could determine the max value from the moment K==1 in 1 loop, instead of taking half of the maxes and doing it over and over again.
Alternative approach
As already suggested the sorting approach, or the quick find methods can be used. I was thinking how you could still use your tournament tree approach. And the best I came up with is how merge sort works. I slightly edited because you only need max K elements to return.
public static int find(int[] a, int k) {
int[] max = find(a, 0, a.length - 1, k);
return max[k-1];
}
private static int[] find(int[] a, int lo, int hi, int k) {
if (hi < lo){
return new int[]{};
}
if(lo == hi){
return new int[]{a[lo]};
}
int mid = lo + (hi - lo) / 2;
int[] left = find(a, lo, mid, k);
int[] right = find(a, mid + 1, hi, k);
return merge(left, right, k);
}
private static int[] merge(int[] left, int[] right, int k) {
int[] res = new int[Math.min(k, left.length+right.length)];
int l = 0, r = 0;
for (int i = 0; i<res.length;i++) {
if (l == left.length)
res[i] = right[r++];
else if (r == right.length)
res[i] = left[l++];
else if (left[l] > right[r])
res[i] = left[l++];
else
res[i] = right[r++];
}
return res;
}
I just had this question on an final exam for an introductory CS course. I thought it was pretty interesting but I couldn't fully figure it out. I tried coding it out on Eclipse for a while and wasn't getting anywhere. I have a Biochem exam soon so I really should be studying for that but this is bugging me... Thought I would ask for help.
Question -- Write an algorithm for the following:
Given an input array, A, with 2 indices -- start and stop -- calculate the sum between the two. The array is sorted and all elements belong to the set {1, 2, 3}. This algorithm must run in O(log(n)). So given an array {1, 1, 2, 2, 3, 3, 3, 3} with indices 1 and 5 it would return 11.
This is what I have so far:
public static int findSum(int[] a, int start, int stop) {
int sum = 0;
int temp[] = new int[4];
int mid = (start+stop)/2;
while (mid > start || mid < stop) {
if (a[mid+1]-a[mid] != 0) {
temp[mid+1] = mid+1;
temp[mid] = mid;
mid++;
}
else {
findSum(a, start, mid);
findSum(a, mid+1, stop);
}
}
for (int i = 4; i > 0; i--) {
if (temp[i] == 0) {
temp[i] = temp[i-1];
}
}
for (int i = 1; i < 4; i++) {
sum += i*(temp[i]-temp[i-1]);
}
return sum;
}
Don't know if my logic is correct at all but my idea was to look for indices in the array where the numbers shift and to do so in a recursive manner where I keep splitting the array into two parts. With these indices, I can determine the frequency in which the numbers appear and thus multiply the numbers by these frequencies and add those up to calculate the sum.
Pretty sure my algorithm has numerous flaws in it...
Now that you've updated your question to include that the data will only have 1, 2, or 3 in it: You can simply just recursively check which number you are at in your range:
public static int findSum(int[] a, int start, int stop) {
if (start < 0 || start >= a.length || stop < 0 || stop >= a.length) { //bounds check
return 0;
}
if (start == stop) { //return the number!
return a[start];
} else if (a[start] == a[stop]) { //catch for some quick math, return amount of values * the value
return a[start] * (stop - start + 1);
} else {
int mid = start + ((stop - start) >>> 1); //This is the middle point between start and stop
return findSum(a, start, mid) + findSum(a, mid + 1, stop);
}
}
How can i go about adding the elements of a sorted array which contain a specific string prefix using binary search and those elements as the order they appear in the array to a arraylist..
It is not hard to code but i am having difficulty with binary search. To use the string prefix the String class provides startswith. I just need help to start the binary search
public static <T extends Comparable<T>> ArrayList prefixMatch(T[] list,
String prefix) {
}
Use the binarySearch method from the API.
String[] objString = {"a","b","c"};
System.out.println(Arrays.binarySearch(objString,"c"));
Or if you want to create your own Binary search implementation. Here it is.
/* BinarySearch.java */
public class BinarySearch {
public static final int NOT_FOUND = -1;
public static int search(int[] arr, int searchValue) {
int left = 0;
int right = arr.length - 1;
return binarySearch(arr, searchValue, left, right);
}
private static int binarySearch(int[] arr, int searchValue, int left, int right) {
if (right < left) {
return NOT_FOUND;
}
/*
int mid = mid = (left + right) / 2;
There is a bug in the above line;
Joshua Bloch suggests the following replacement:
*/
int mid = (left + right) >>> 1;
if (searchValue > arr[mid]) {
return binarySearch(arr, searchValue, mid + 1, right);
} else if (searchValue < arr[mid]) {
return binarySearch(arr, searchValue, left, mid - 1);
} else {
return mid;
}
}
}
public class BinarySearchTest {
public static void main(String[] args) {
int[] arr = {1, 5, 2, 7, 9, 5};
Arrays.sort(arr);
System.out.println(BinarySearch.search(arr, 2));
}
}
I had a similar requirement - given the array of Strings find indexes of each string which starts with a new letter, i.e. for Africa Angela Beach Bamboo Zorro, I needed algorithm which would return [ 0, 2, 4 ]
I found that there's a modification of well known Binary Search algorithm which uses 'deferred equality test' and this has the side effect of finding exactly needed indexes - ones which start the range.
You can read about it in this Wikipedia article (also there's a very simple example based on which I implemented my own).
I have written this code but it will print these stack traces in the console please help me thanks! (Aslo "p" and "q" are the first and last index of our array ,respectively)
public class JavaQuickSort {
public static void QuickSort(int A[], int p, int q) {
int i, last = 0;
Random rand = new Random();
if (q < 1) {
return;
}
**swap(A, p, rand.nextInt() % (q+1));**
for (i = p + 1; i <= q; i++) {
if (A[i] < A[p]) {
swap(A, ++last, i);
}
}
swap(A, p, last);
QuickSort(A, p, last - 1);
QuickSort(A, last + 1, q);
}
private static void swap(int[] A, int i, int j) {
int temp;
temp = A[i];
**A[i] = A[j];**
A[j] = temp;
}
public static void main(String[] args){
int[] A = {2,5,7,3,9,0,1,6,8};
**QuickSort(A, 0,8 );**
System.out.println(Arrays.toString(A));
}
}
the Stack traces :
run:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -3
at JavaQuickSort.swap(JavaQuickSort.java:38)
at JavaQuickSort.QuickSort(JavaQuickSort.java:22)
at JavaQuickSort.main(JavaQuickSort.java:45)
Java Result: 1
BUILD SUCCESSFUL (total time: 2 seconds)
I also bold those statements that cause these stack traces. like ==> ** ...**
EDITED:
public class JavaQuickSort {
public static void QuickSort(int arr[],int lo,int hi) {
int n = arr.length;
if(n<=1)
return;
**int r = partition(arr);**
**QuickSort(arr,lo , r-1);**
QuickSort(arr, r+1, hi);
}
private static void swap(int[] A, int i, int j) {
int temp;
temp = A[i];
**A[i] = A[j];**
A[j] = temp;
}
public static int partition(int arr[]){
int i, last = 0;
Random rand = new Random();
int n = arr.length;
if (n <= 1)
return arr[0];
**swap(arr, 0, rand.nextInt(n));**
for (i =1; i <n; i++) {
if (arr[i] < arr[0]) {
swap(arr, ++last, i);
}
}
swap(arr, 0, last);
return last;
}
public static void main(String[] args){
int[] A = {2,5,7,3,9,0,1,6,8};
**QuickSort(A, 0,8 );**
System.out.println(Arrays.toString(A));
}
}
I have edited my post for being more understandable also it will print these stack traces and I bold the lines that cause these stack traces!!!
the stack traces :
run:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
at JavaQuickSort.swap(JavaQuickSort.java:27)
at JavaQuickSort.partition(JavaQuickSort.java:39)
at JavaQuickSort.QuickSort(JavaQuickSort.java:19)
at JavaQuickSort.QuickSort(JavaQuickSort.java:20)
at JavaQuickSort.QuickSort(JavaQuickSort.java:20)
at JavaQuickSort.QuickSort(JavaQuickSort.java:20)
at JavaQuickSort.QuickSort(JavaQuickSort.java:20)
at JavaQuickSort.main(JavaQuickSort.java:52)
Java Result: 1
BUILD SUCCESSFUL (total time: 2 seconds)
please help me thanks
EDITED :
I have written this code with paying attention to the forst answer of this post but it wont sort my array!!!
public class JavaQuickSort {
public static void QuickSort(int arr[], int lo, int hi) {
if (hi > lo) {
Random rand = new Random();
int pivotIndex = lo + rand.nextInt(hi-lo+1);
int pivotnewIndex = partition(arr, lo, hi,pivotIndex);
QuickSort(arr, lo, pivotnewIndex - 1);
QuickSort(arr, pivotnewIndex + 1, hi);
}
}
private static void swap(int[] A, int i, int j) {
int temp;
temp = A[i];
A[i] = A[j];
A[j] = temp;
}
public static int partition(int arr[],int lo,int hi,int pivotIndex)
{
int pivotValue = arr[pivotIndex];
swap(arr, hi, pivotIndex);
int storeIndex = lo;
for(int i = lo;i<hi;i++){
if (arr[i]<=pivotValue)
swap(arr, storeIndex, i);
storeIndex = storeIndex ++;
}
swap(arr, storeIndex, hi);
return storeIndex;
}
public static void main(String[] args) {
int[] A = {2, 5, 7, 3, 9, 0, 1, 6, 8};
QuickSort(A, 0, 8);
System.out.println(Arrays.toString(A));
}
}
the output:
run:
[2, 9, 3, 8, 0, 6, 7, 1, 5]
BUILD SUCCESSFUL (total time: 2 seconds)
I need your help really I confused !!!
Some problems with your code are:
Don't create a new Random instance for one time uses. Just create it once, store it in say a static field, and then use it to generate many random numbers for your application.
When creating a random index for the pivot, it must lie between p and q inclusive.
Use Random.nextInt(int n) overload to generate a random number in a given range
Perhaps use lo and hi instead of p and q, and int[] arr instead of int A[]
You can get the length of an array with its .length member
As for the quicksort algoritm itself, it doesn't look like anything I've seen before. I recommend studying the standard imperative implementation and adapting that.
See also
Wikipedia/Quicksort
Update
Unfortunately you have mixed up the pseudocodes from Wikipedia somewhat. You want to adapt this algorithm:
function partition(array, left, right, pivotIndex)
pivotValue := array[pivotIndex]
swap array[pivotIndex] and array[right] // Move pivot to end
storeIndex := left
for i from left to right - 1 // left ≤ i < right
if array[i] ≤ pivotValue
swap array[i] and array[storeIndex]
storeIndex := storeIndex + 1
swap array[storeIndex] and array[right] // Move pivot to its final place
return storeIndex
procedure quicksort(array, left, right)
if right > left
select a pivot index //(e.g. pivotIndex := left+(right-left)/2)
pivotNewIndex := partition(array, left, right, pivotIndex)
quicksort(array, left, pivotNewIndex - 1)
quicksort(array, pivotNewIndex + 1, right)
Note that the above algorithm selects the middle element of the subarray between left and right for the pivot index. Since you want a randomized quicksort, you want to choose a random index between left and right inclusive, so the formula needs to be changed as follows:
pivotIndex := left + (random number between 0 and right-left inclusive)
So, for example, if left = 5 and right = 7, then you want pivotIndex to be 5 + x where x is a random number between 0 and 7-5=2 inclusive.
Since Random.nextInt(n) has an exclusive upper bound, translating this to Java would be something like:
int pivotIndex = lo + rand.nextInt(hi - lo + 1);
Final correction
You've made a common mistake for beginners here:
// HORRIBLE FORMATTING! Don't do this!
if (arr[i]<=pivotValue)
swap(arr, storeIndex, i);
storeIndex = storeIndex ++;
If you noticed the pseudocode above, both statements are supposed to be part of the if body, but the above code, when properly indented and with braces added, is really this:
// PROPER FORMATTING! Reveals bug instantly!
if (arr[i]<=pivotValue) {
swap(arr, storeIndex, i);
}
storeIndex = storeIndex ++;
The fix, therefore is to move the storeIndex increment to the if body like this:
// Corrected according to pseudocode!
if (arr[i]<=pivotValue) {
swap(arr, storeIndex, i);
storeIndex = storeIndex ++;
}
Or you can also just do:
// Nice and clear!
if (arr[i]<=pivotValue) {
swap(arr, storeIndex++, i);
}
The lessons from this latest update is:
Always indent your code properly
A good IDE can make this a breeze, you really have no excuse
Make a habit of putting braces on if statements, even when they're not strictly necessary
Many subtle bugs are due to omission of braces
Use post/pre-increments sensibly
Like many things, they can be abused beyond comprehension, but used appropriately and idiomatically they lead to concise and readable code
One last thing
When I mentioned .length of arrays, I meant that instead of this:
int[] A = {2, 5, 7, 3, 9, 0, 1, 6, 8};
QuickSort(A, 0, 8); // BAD! Don't hardcode array length!
You should do this:
int[] arr = {2, 5, 7, 3, 9, 0, 1, 6, 8};
QuickSort(arr, 0, arr.length - 1); // GOOD!
The random generator produces positive and negative values. That's why eventually you call swap with a negative q value.
swap(A, p, rand.nextInt() % (q+1));
The random integer generated may be below p, clearly you would want it to be something between p and q.
As this is not homework, and I'd take the homework category to encompass self-learning, you should just use the standard Arrays.sort(int[]).