I am trying to implement Median of Medians in Java for a method like this:
Select(Comparable[] list, int pos, int colSize, int colMed)
list is a list of values of which to find a specified position
pos is the specified position
colSize is the size of the columns that I create in the first stage
colMed is the position in those columns that I use as the medX
I am not sure which sorting algorithm would be the best to use or how to implement this exactly..
I don't know if you still need this problem solved, but http://www.ics.uci.edu/~eppstein/161/960130.html has an algorithm:
select(L,k)
{
if (L has 10 or fewer elements)
{
sort L
return the element in the kth position
}
partition L into subsets S[i] of five elements each
(there will be n/5 subsets total).
for (i = 1 to n/5) do
x[i] = select(S[i],3)
M = select({x[i]}, n/10)
partition L into L1<M, L2=M, L3>M
if (k <= length(L1))
return select(L1,k)
else if (k > length(L1)+length(L2))
return select(L3,k-length(L1)-length(L2))
else return M
}
Good luck!
The question asked for Java, so here it is
import java.util.*;
public class MedianOfMedians {
private MedianOfMedians() {
}
/**
* Returns median of list in linear time.
*
* #param list list to search, which may be reordered on return
* #return median of array in linear time.
*/
public static Comparable getMedian(ArrayList<Comparable> list) {
int s = list.size();
if (s < 1)
throw new IllegalArgumentException();
int pos = select(list, 0, s, s / 2);
return list.get(pos);
}
/**
* Returns position of k'th largest element of sub-list.
*
* #param list list to search, whose sub-list may be shuffled before
* returning
* #param lo first element of sub-list in list
* #param hi just after last element of sub-list in list
* #param k
* #return position of k'th largest element of (possibly shuffled) sub-list.
*/
public static int select(ArrayList<Comparable> list, int lo, int hi, int k) {
if (lo >= hi || k < 0 || lo + k >= hi)
throw new IllegalArgumentException();
if (hi - lo < 10) {
Collections.sort(list.subList(lo, hi));
return lo + k;
}
int s = hi - lo;
int np = s / 5; // Number of partitions
for (int i = 0; i < np; i++) {
// For each partition, move its median to front of our sublist
int lo2 = lo + i * 5;
int hi2 = (i + 1 == np) ? hi : (lo2 + 5);
int pos = select(list, lo2, hi2, 2);
Collections.swap(list, pos, lo + i);
}
// Partition medians were moved to front, so we can recurse without making another list.
int pos = select(list, lo, lo + np, np / 2);
// Re-partition list to [<pivot][pivot][>pivot]
int m = triage(list, lo, hi, pos);
int cmp = lo + k - m;
if (cmp > 0)
return select(list, m + 1, hi, k - (m - lo) - 1);
else if (cmp < 0)
return select(list, lo, m, k);
return lo + k;
}
/**
* Partition sub-list into 3 parts [<pivot][pivot][>pivot].
*
* #param list
* #param lo
* #param hi
* #param pos input position of pivot value
* #return output position of pivot value
*/
private static int triage(ArrayList<Comparable> list, int lo, int hi,
int pos) {
Comparable pivot = list.get(pos);
int lo3 = lo;
int hi3 = hi;
while (lo3 < hi3) {
Comparable e = list.get(lo3);
int cmp = e.compareTo(pivot);
if (cmp < 0)
lo3++;
else if (cmp > 0)
Collections.swap(list, lo3, --hi3);
else {
while (hi3 > lo3 + 1) {
assert (list.get(lo3).compareTo(pivot) == 0);
e = list.get(--hi3);
cmp = e.compareTo(pivot);
if (cmp <= 0) {
if (lo3 + 1 == hi3) {
Collections.swap(list, lo3, lo3 + 1);
lo3++;
break;
}
Collections.swap(list, lo3, lo3 + 1);
assert (list.get(lo3 + 1).compareTo(pivot) == 0);
Collections.swap(list, lo3, hi3);
lo3++;
hi3++;
}
}
break;
}
}
assert (list.get(lo3).compareTo(pivot) == 0);
return lo3;
}
}
Here is a Unit test to check it works...
import java.util.*;
import junit.framework.TestCase;
public class MedianOfMedianTest extends TestCase {
public void testMedianOfMedianTest() {
Random r = new Random(1);
int n = 87;
for (int trial = 0; trial < 1000; trial++) {
ArrayList list = new ArrayList();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
int v = r.nextInt(256);
a[i] = v;
list.add(v);
}
int m1 = (Integer)MedianOfMedians.getMedian(list);
Arrays.sort(a);
int m2 = a[n/2];
assertEquals(m1, m2);
}
}
}
However, the above code is too slow for practical use.
Here is a simpler way to get the k'th element that does not guarantee performance, but is much faster in practice:
/**
* Returns position of k'th largest element of sub-list.
*
* #param list list to search, whose sub-list may be shuffled before
* returning
* #param lo first element of sub-list in list
* #param hi just after last element of sub-list in list
* #param k
* #return position of k'th largest element of (possibly shuffled) sub-list.
*/
static int select(double[] list, int lo, int hi, int k) {
int n = hi - lo;
if (n < 2)
return lo;
double pivot = list[lo + (k * 7919) % n]; // Pick a random pivot
// Triage list to [<pivot][=pivot][>pivot]
int nLess = 0, nSame = 0, nMore = 0;
int lo3 = lo;
int hi3 = hi;
while (lo3 < hi3) {
double e = list[lo3];
int cmp = compare(e, pivot);
if (cmp < 0) {
nLess++;
lo3++;
} else if (cmp > 0) {
swap(list, lo3, --hi3);
if (nSame > 0)
swap(list, hi3, hi3 + nSame);
nMore++;
} else {
nSame++;
swap(list, lo3, --hi3);
}
}
assert (nSame > 0);
assert (nLess + nSame + nMore == n);
assert (list[lo + nLess] == pivot);
assert (list[hi - nMore - 1] == pivot);
if (k >= n - nMore)
return select(list, hi - nMore, hi, k - nLess - nSame);
else if (k < nLess)
return select(list, lo, lo + nLess, k);
return lo + k;
}
I agree with the answer/solution from Chip Uni. I will just comment the sorting part and provide some further explanations:
You do not need any sorting algorithm. The algorithm is similar to quicksort, with the difference that only one partition is solved (left or right). We just need to find an optimal pivot so that left and right parts are as equal as possible, which would mean N/2 + N/4 + N/8 ... = 2N iterations, and thus the time complexity of O(N). The above algorithms, called median of medians, computes the median of medians of 5, which turns out to yield linear time complexity of the algorithm.
However, sorting algorithm is used when the range being searched for nth smallest/greatest element (which I suppose you are implementing with this algorithm) in order to speed up the algorithm. Insertion sort is particularly fast on small arrays up to 7 to 10 elements.
Implementation note:
M = select({x[i]}, n/10)
actually means taking the median of all those medians of 5-element groups. You can accomplish that by creating another array of size (n - 1)/5 + 1 and call the same algorithm recursively to find the n/10-th element (which is median of the newly created array).
#android developer :
for (i = 1 to n/5) do
x[i] = select(S[i],3)
is really
for (i = 1 to ceiling(n/5) do
x[i] = select(S[i],3)
with a ceiling function appropriate for your data(eg in java 2 doubles)
This affects the median as well wrt simply taking n/10, but we are finding closest to the mean that occurs in the array, not the true mean.
Another note is that S[i] may have fewer than 3 elements, so we want to find the median with respect to length; passing it into select with k=3 won't always work.( eg n =11, we have 3 subgroups 2 w 5, 1 w 1 element)
I know it's a very old post and you might not remember about it any more. But I wonder did you measure the running time of your implementation when you implemented it?
I tried this algorithm and compare it with the simple approach using java sorting method (Arrays.sort() ), then pick the kth element from sorted array. The result that I received is that this algorithm only out-beat java sorting algorithm when the size of the array is about hundred thousand elements or more. And it's only about 2 or 3 times faster, which is obviously not log(n) time faster.
Do you have any comment on that?
Related
We have an assignment to search for the minimum element of a sorted array that is shifted to the right afterwards. For example: [1, 5, 6, 19, 56, 101] becomes [19, 56, 101, 1, 5, 6]. The method should be implemented using a divide and conquer algorithm and it should have a better asymptotic time complexity than O(n).
EDIT: I forgot to add that the elements int the array are unique.
I already implemented a method and wanted to ask if this is better than O(n) and if there are ways to improve my method.
public class FindMinimum {
public void findMinimum(int[] arr) {
// the recursive method ends when the length of the array is smaller than 2
if (arr.length < 2) {
return;
}
int mid = arr.length / 2;
/*
* if the array length is greater or the same as two, check if the middle
* element is smaller as the element before that. And print the middle element
* if it's true.
*/
if (arr.length >= 2) {
if (arr[mid - 1] > arr[mid]) {
System.out.println("Minimum: " + arr[mid]);
return;
}
}
/*
* separate the array in two sub-arrays through the middle and start the method
* with those two arrays again.
*/
int[] leftArr = new int[mid];
int[] rightArr = new int[arr.length - mid];
for (int i = 0; i < mid; i++) {
leftArr[i] = arr[i];
}
for (int i = mid; i < arr.length; i++) {
rightArr[i - mid] = arr[i];
}
findMinimum(leftArr);
findMinimum(rightArr);
}
}
In Java you could use a List because than you can create a Sublist.
private Integer findMinimum(List<Integer> list) {
if (list.size() < 2)
return list.get(0);
int mid = list.size() / 2;
// create left and right list
List<Integer> leftList = list.subList(0, mid);
List<Integer> rightList = list.subList(mid, list.size());
if (leftList.get(leftList.size() - 1) <= rightList.get(rightList.size() - 1))
return findMin(leftList);
else
return findMin(rightList);
}
When you create a Sublist with Java there is no copy. So to create a new Sublist takes a complexity of O(1).
So the function has a complexity of O(logn).
This is my new solution, without copying the array anywhere.
public class FindMinimum {
public void findMinimum(int[] arr) {
findMinimumSub(arr, 0, arr.length - 1, 2);
}
private void findMinimumSub(int[] arr, int start, int end, int size) {
// the recursive method ends when the length of the array is smaller than 2
if ((end - start) < 2) {
if (arr[end] > arr[start])
System.out.println("Minimum: " + arr[start]);
else
System.out.println("Minimum: " + arr[end]);
return;
}
int mid = arr.length / size;
if (arr[start] > arr[end]) {
// right side
start += mid;
findMinimumSub(arr, start, end, size * 2);
}
else {
// left side
findMinimumSub(arr, start, mid, size * 2);
}
}
}
Mr professor has assigned us the task of writing a custom qucksort algorithm that we must implement using his outline ( I can't write my own from scratch, I must use his). He calls it smartQuickSort, and what makes this algorithm "custom" is that we have to calculate the averages on each side of the pivot point which is then used to sort the array. The algorithm uses a class called SmartQuickSortPivot which has int values left and right to hold the averages on the left/right side respectively.
I've written numerous quick sort algorithms in several languages but I cannot, for the life of me, get this one to sort correctly. I've spent 3 days rewriting and debugging this thing with no success, so i'm really hoping someone could help me out as i'm about to pull all of my hair out. Starting from the "skeleton code" he gave us (which includes commented instructions), this is my latest attempt:
/**
* split4SmartQuickSort splits the array (from first to last) into two subarrays, left and right, using the
* provided splitVal. It needs to calculate on the fly the average of all the elements of the left subarray
* and average of all elements of the right subarray, and store the two averages in the #pivot object.
* The following implementation is only copy of the code from
* the split function (from line 247) and you should enhance the function to implement what we need to calculate the averages
* as the pivot for the left and right subarray.
*
* Please be noted that splitVal may not even exist in the array since we choose the average.
* But this should not impact the correctness algorithm of splitting and sorting.
* #param first
* #param last
* #param splitVal
* #param leftRightAverages
* #return
*/
static int split4SmartQuickSort(int first, int last, int splitVal, SmartQuickSortPivot leftRightAverages)
{
int saveF = first;
int leftAvg = 0;
int leftCount = 0;
int rightAvg = 0;
int rightCount = 0;
boolean onCorrectSide;
first++;
do
{
onCorrectSide = true;
while (onCorrectSide) // move first toward last
if (values[first] > splitVal)
onCorrectSide = false;
else
{
//I think my average calculations here are wrong,
//but nothing I have tried works correctly
leftAvg += first;
leftCount++;
first++;
leftRightAverages.left = leftAvg / leftCount;
onCorrectSide = (first <= last);
}
onCorrectSide = (first <= last);
while (onCorrectSide) // move last toward first
if (values[last] <= splitVal)
onCorrectSide = false;
else
{
//I think my average calculations here are wrong,
//but nothing I have tried works correctly
rightAvg += last;
rightCount++;
last--;
leftRightAverages.right = rightAvg / rightCount;
onCorrectSide = (first <= last);
}
if (first < last)
{
swap(first, last);
first++;
last--;
}
} while (first <= last);
swap(saveF, last);
//I think this is one of my problems. Not sure
//what I should be returning here
return last;
}
/**
* Smart quick sort allows the use of a better splitting value (the pivot value) when to split the array
* into two. In this algorithm, we will use the average of the array (subarray) of all elements as the pivot.
*
* Each call to split (split4SmartQuickSort method), the splitValue will be passed and also the split4SmartQuickSort
* will return the averages of left subarray and right subarray. The two averages, each will be used for the
* following calls to smartQuickSort.
*
* #param first the first element
* #param last the last element
* #param splitVal the pivot value for splitting the array
*/
static void smartQuickSort(int first, int last, int splitVal)
{
if (first < last)
{
int splitPoint;
SmartQuickSortPivot leftRightAverages = new SmartQuickSortPivot();
splitPoint = split4SmartQuickSort(first, last, splitVal, leftRightAverages);
if (first <= splitPoint)
{
smartQuickSort(first, splitPoint - 1, leftRightAverages.left);
}
if (last >= splitPoint)
{
smartQuickSort(splitPoint + 1, last, leftRightAverages.right);
}
}
}
Here is the class used to store the averages to the left/right of the pivot point:
public class SmartQuickSortPivot {
public int left;
public int right;
}
And finally the main method used for testing:
public static void main(String[] args)
{
//initValues();
printValues();
System.out.println("values is sorted: " + isSorted());
System.out.println();
//quickSort(0, values.length - 1);
/** you can either compute the average first as the first pivot or simplify choose the first one as the pivot */
smartQuickSort(0, values.length - 1, values[4]);
printValues();
System.out.println("values is sorted: " + isSorted());
System.out.println();
}
}
The line I commented out, //quickSort(0, values.length - 1); is the algorithm I wrote that does not include the leftRightAverages object argument but is essentially the same, and it works perfectly, so i'm very confused why I can't get the "custom" smartQuickSort to work. For simplicity, I commented out the initValues() method and instead used a preset array that looks like this:
static int[] values = {2,5,1,66,89,44,32,51,8,6}; // values to be sorted
Things I've tried (and failed at):
1.) Move the lines leftRightAverages.left = leftAvg / leftCount; , leftRightAverages.right = rightAvg / rightCount; outside of the do-while loop, which (I think) due to the recursive nature of the function, eventually gives me a divide by zero RTE.
2.) Change the return value of split4SmartQuickSort() from last to different combinations of rightLeftAverages.left and rightLeftAverages.right, which causes a stack overflow from the recursion. This is where I am really confused, as I'm not exactly sure what this method should be returning in this particular implementation of quick sort (and more importantly, how to properly calculate it).
I think my issue here is twofold; I'm either not correctly calculating the averages on each side of the pivot (I've used numerous pivot points and none of them seem to make a difference), and I'm not returning the proper calculation from the split4SmartQuickSort() method itself. If I remove the rightLeftAverages object from the method argument and use a more traditional approach to quick sort, the algorithm works fine. This is why I think those 2 issues I listed are why the algorithm doesn't function correctly. the return value from split4SmartQuickSort() (I think) acts as the new pivot point for sorting, using the splitVal argument as the original pivot point.
Yes this is my homework, but I've put hours of genuine effort into this thing, with no luck. My prof doesn't answer emails over the weekend and his office hours are during one of my other classes, so I have nowhere else to turn.
I think that you have problems with this because it's hard in this case to use one integer split point. Here is why:
Imagine that at some of the algorithm you got 44, 51, 89, 66 to partition with the average of 62.5 ~ 62. If you use 62 as pivot element there is uncertainty what to return as a split point (because you can return index 1 or 2 (values 51 or 89 correspondingly)).
Let's suppose that you pick 2. This will lead to invalid algorithm (let's remember that the split point (pivot) a_j is the point that divides array into two subarrays such for each i < j a_i < a_j and for each k > j a_j < a_k) because 89 !< 66 and cannot be a split point.
What you kind of need to do is to return something in the middle as a split point. To do this you need to return SmartQuickSortPivot object instead of int and use its left/right values as ending/starting indexes for your left/right arrays.
import java.util.Arrays;
public class Temp {
public static class SmartQuickSortPivot {
public int left;
public int right;
}
static int[] values = {2,5,1,66,89,44,32,51,8,6}; // values to be sorted
/**
* split4SmartQuickSort splits the array (from first to last) into two subarrays, left and right, using the
* provided splitVal. It needs to calculate on the fly the average of all the elements of the left subarray
* and average of all elements of the right subarray, and store the two averages in the #pivot object.
* The following implementation is only copy of the code from
* the split function (from line 247) and you should enhance the function to implement what we need to calculate the averages
* as the pivot for the left and right subarray.
*
* Please be noted that splitVal may not even exist in the array since we choose the average.
* But this should not impact the correctness algorithm of splitting and sorting.
* #param first
* #param last
* #param splitVal
* #param leftRightAverages
* #return
*/
static SmartQuickSortPivot split4SmartQuickSort(int first, int last, int splitVal, SmartQuickSortPivot leftRightAverages)
{
int i = first,j = last;
int sumLeft = 0;
int sumRight = 0;
while (i < j) {
while (values[i] < splitVal){
sumLeft += values[i];
i++;
}
while (values[j] > splitVal){
sumRight += values[j];
j--;
}
if (i < j) {
swap(i, j);
}
}
leftRightAverages.left = (i - first == 0) ? values[first] : sumLeft / (i - first);
leftRightAverages.right = (last - j == 0) ? values[last] : sumRight / (last - j);
SmartQuickSortPivot smartQuickSortPivot = new SmartQuickSortPivot();
smartQuickSortPivot.left = i;
smartQuickSortPivot.right = j;
return smartQuickSortPivot;
}
private static void swap(int i, int j) {
int temp = values[i];
values[i] = values[j];
values[j] = temp;
}
/**
* Smart quick sort allows the use of a better splitting value (the pivot value) when to split the array
* into two. In this algorithm, we will use the average of the array (subarray) of all elements as the pivot.
*
* Each call to split (split4SmartQuickSort method), the splitValue will be passed and also the split4SmartQuickSort
* will return the averages of left subarray and right subarray. The two averages, each will be used for the
* following calls to smartQuickSort.
*
* #param first the first element
* #param last the last element
* #param splitVal the pivot value for splitting the array
*/
static void smartQuickSort(int first, int last, int splitVal)
{
if (first < last)
{
SmartQuickSortPivot splitPoint;
SmartQuickSortPivot leftRightAverages = new SmartQuickSortPivot();
splitPoint = split4SmartQuickSort(first, last, splitVal, leftRightAverages);
if (first < splitPoint.left)
{
smartQuickSort(first, splitPoint.left - 1, leftRightAverages.left);
}
if (last > splitPoint.right)
{
smartQuickSort(splitPoint.right + 1, last, leftRightAverages.right);
}
}
}
public static void main(String[] args)
{
/** you can either compute the average first as the first pivot or simplify choose the first one as the pivot */
smartQuickSort(0, values.length - 1, values[5]);
System.out.println(Arrays.toString(values));
}
}
Thanks to the great advice below, I got the algorithm working, but it still was not sorting duplicates correctly (infinite loop when dupes encountered). After playing with the code, I now have a complete working algorithm. The change was in the split4SmartQuickSort() only, so here is that method updated:
static SmartQuickSortPivot split4SmartQuickSort
(int first, int last, int splitVal, SmartQuickSortPivot leftRightAverages)
{
int f = first;
int l = last;
int sumLeft = 0;
int sumRight = 0;
while (f < l)
{
while (values[f] < splitVal)
{
sumLeft += values[f];
f++;
}
while (values[l] > splitVal)
{
sumRight += values[l];
l--;
}
if (f <= l)
{
swap(f, l);
//handling duplicates in the list
if (values[f] == values[l])
{
f++;
}
}
}
if (f - first == 0)
{
leftRightAverages.left = values[first];
}
else
{
leftRightAverages.left = sumLeft / (f - first);
}
if (last - l == 0)
{
leftRightAverages.right = values[last];
}
else
{
leftRightAverages.right = sumRight / (last - l);
}
//create SmartQuickSortPivot object to be returned. Used in
//smartQuickSort as the split point for sorting
SmartQuickSortPivot sqsp = new SmartQuickSortPivot();
sqsp.left = f;
sqsp.right = l;
return sqsp;
}
And finally, the smartQuickSort() algorithm:
static void smartQuickSort(int first, int last, int splitVal)
{
if (first < last)
{
SmartQuickSortPivot splitPoint;
SmartQuickSortPivot leftRightAverages = new SmartQuickSortPivot();
splitPoint = split4SmartQuickSort(first, last, splitVal, leftRightAverages);
if (first <= splitPoint.left)
{
smartQuickSort(first, splitPoint.left - 1, leftRightAverages.left);
}
if (last >= splitPoint.right)
{
smartQuickSort(splitPoint.right + 1, last, leftRightAverages.right);
}
}
}
Thanks again to #shyyko-serhiy, as they deserve most of the credit for getting this thing working :)
This question already has answers here:
Finding multiple entries with binary search
(15 answers)
Closed 3 years ago.
I've been tasked with creating a method that will print all the indices where value x is found in a sorted array.
I understand that if we just scanned through the array from 0 to N (length of array) it would have a running time of O(n) worst case. Since the array that will be passed into the method will be sorted, I'm assuming that I can take advantage of using a Binary Search since this will be O(log n). However, this only works if the array has unique values. Since the Binary Search will finish after the first "find" of a particular value. I was thinking of doing a Binary Search for finding x in the sorted array, and then checking all values before and after this index, but then if the array contained all x values, it doesn't seem like it would be that much better.
I guess what I'm asking is, is there a better way to find all the indices for a particular value in a sorted array that is better than O(n)?
public void PrintIndicesForValue42(int[] sortedArrayOfInts)
{
// search through the sortedArrayOfInts
// print all indices where we find the number 42.
}
Ex: sortedArray = { 1, 13, 42, 42, 42, 77, 78 } would print: "42 was found at Indices: 2, 3, 4"
You will get the result in O(lg n)
public static void PrintIndicesForValue(int[] numbers, int target) {
if (numbers == null)
return;
int low = 0, high = numbers.length - 1;
// get the start index of target number
int startIndex = -1;
while (low <= high) {
int mid = (high - low) / 2 + low;
if (numbers[mid] > target) {
high = mid - 1;
} else if (numbers[mid] == target) {
startIndex = mid;
high = mid - 1;
} else
low = mid + 1;
}
// get the end index of target number
int endIndex = -1;
low = 0;
high = numbers.length - 1;
while (low <= high) {
int mid = (high - low) / 2 + low;
if (numbers[mid] > target) {
high = mid - 1;
} else if (numbers[mid] == target) {
endIndex = mid;
low = mid + 1;
} else
low = mid + 1;
}
if (startIndex != -1 && endIndex != -1){
for(int i=0; i+startIndex<=endIndex;i++){
if(i>0)
System.out.print(',');
System.out.print(i+startIndex);
}
}
}
Well, if you actually do have a sorted array, you can do a binary search until you find one of the indexes you're looking for, and from there, the rest should be easy to find since they're all next to each-other.
once you've found your first one, than you go find all the instances before it, and then all the instances after it.
Using that method you should get roughly O(lg(n)+k) where k is the number of occurrences of the value that you're searching for.
EDIT:
And, No, you will never be able to access all k values in anything less than O(k) time.
Second edit: so that I can feel as though I'm actually contributing something useful:
Instead of just searching for the first and last occurrences of X than you can do a binary search for the first occurence and a binary search for the last occurrence. which will result in O(lg(n)) total. once you've done that, you'll know that all the between indexes also contain X(assuming that it's sorted)
You can do this by searching checking if the value is equal to x , AND checking if the value to the left(or right depending on whether you're looking for the first occurrence or the last occurrence) is equal to x.
public void PrintIndicesForValue42(int[] sortedArrayOfInts) {
int index_occurrence_of_42 = left = right = binarySearch(sortedArrayOfInts, 42);
while (left - 1 >= 0) {
if (sortedArrayOfInts[left-1] == 42)
left--;
}
while (right + 1 < sortedArrayOfInts.length) {
if (sortedArrayOfInts[right+1] == 42)
right++;
}
System.out.println("Indices are from: " + left + " to " + right);
}
This would run in O(log(n) + #occurrences)
Read and understand the code. It's simple enough.
Below is the java code which returns the range for which the search-key is spread in the given sorted array:
public static int doBinarySearchRec(int[] array, int start, int end, int n) {
if (start > end) {
return -1;
}
int mid = start + (end - start) / 2;
if (n == array[mid]) {
return mid;
} else if (n < array[mid]) {
return doBinarySearchRec(array, start, mid - 1, n);
} else {
return doBinarySearchRec(array, mid + 1, end, n);
}
}
/**
* Given a sorted array with duplicates and a number, find the range in the
* form of (startIndex, endIndex) of that number. For example,
*
* find_range({0 2 3 3 3 10 10}, 3) should return (2,4). find_range({0 2 3 3
* 3 10 10}, 6) should return (-1,-1). The array and the number of
* duplicates can be large.
*
*/
public static int[] binarySearchArrayWithDup(int[] array, int n) {
if (null == array) {
return null;
}
int firstMatch = doBinarySearchRec(array, 0, array.length - 1, n);
int[] resultArray = { -1, -1 };
if (firstMatch == -1) {
return resultArray;
}
int leftMost = firstMatch;
int rightMost = firstMatch;
for (int result = doBinarySearchRec(array, 0, leftMost - 1, n); result != -1;) {
leftMost = result;
result = doBinarySearchRec(array, 0, leftMost - 1, n);
}
for (int result = doBinarySearchRec(array, rightMost + 1, array.length - 1, n); result != -1;) {
rightMost = result;
result = doBinarySearchRec(array, rightMost + 1, array.length - 1, n);
}
resultArray[0] = leftMost;
resultArray[1] = rightMost;
return resultArray;
}
Another result for log(n) binary search for leftmost target and rightmost target. This is in C++, but I think it is quite readable.
The idea is that we always end up when left = right + 1. So, to find leftmost target, if we can move right to rightmost number which is less than target, left will be at the leftmost target.
For leftmost target:
int binary_search(vector<int>& nums, int target){
int n = nums.size();
int left = 0, right = n - 1;
// carry right to the greatest number which is less than target.
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
// when we are here, right is at the index of greatest number
// which is less than target and since left is at the next,
// it is at the first target's index
return left;
}
For the rightmost target, the idea is very similar:
int binary_search(vector<int>& nums, int target){
while(left <= right){
int mid = (left + right) / 2;
// carry left to the smallest number which is greater than target.
if(nums[mid] <= target)
left = mid + 1;
else
right = mid - 1;
}
// when we are here, left is at the index of smallest number
// which is greater than target and since right is at the next,
// it is at the first target's index
return right;
}
I came up with the solution using binary search, only thing is to do the binary search on both the sides if the match is found.
public static void main(String[] args) {
int a[] ={1,2,2,5,5,6,8,9,10};
System.out.println(2+" IS AVAILABLE AT = "+findDuplicateOfN(a, 0, a.length-1, 2));
System.out.println(5+" IS AVAILABLE AT = "+findDuplicateOfN(a, 0, a.length-1, 5));
int a1[] ={2,2,2,2,2,2,2,2,2};
System.out.println(2+" IS AVAILABLE AT = "+findDuplicateOfN(a1, 0, a1.length-1, 2));
int a2[] ={1,2,3,4,5,6,7,8,9};
System.out.println(10+" IS AVAILABLE AT = "+findDuplicateOfN(a2, 0, a2.length-1, 10));
}
public static String findDuplicateOfN(int[] a, int l, int h, int x){
if(l>h){
return "";
}
int m = (h-l)/2+l;
if(a[m] == x){
String matchedIndexs = ""+m;
matchedIndexs = matchedIndexs+findDuplicateOfN(a, l, m-1, x);
matchedIndexs = matchedIndexs+findDuplicateOfN(a, m+1, h, x);
return matchedIndexs;
}else if(a[m]>x){
return findDuplicateOfN(a, l, m-1, x);
}else{
return findDuplicateOfN(a, m+1, h, x);
}
}
2 IS AVAILABLE AT = 12
5 IS AVAILABLE AT = 43
2 IS AVAILABLE AT = 410236578
10 IS AVAILABLE AT =
I think this is still providing the results in O(logn) complexity.
A Hashmap might work, if you're not required to use a binary search.
Create a HashMap where the Key is the value itself, and then value is an array of indices where that value is in the array. Loop through your array, updating each array in the HashMap for each value.
Lookup time for the indices for each value will be ~ O(1), and creating the map itself will be ~ O(n).
Find_Key(int arr[], int size, int key){
int begin = 0;
int end = size - 1;
int mid = end / 2;
int res = INT_MIN;
while (begin != mid)
{
if (arr[mid] < key)
begin = mid;
else
{
end = mid;
if(arr[mid] == key)
res = mid;
}
mid = (end + begin )/2;
}
return res;
}
Assuming the array of ints is in ascending sorted order; Returns the index of the first index of key occurrence or INT_MIN. Runs in O(lg n).
It is using Modified Binary Search. It will be O(LogN). Space complexity will be O(1).
We are calling BinarySearchModified two times. One for finding start index of element and another for finding end index of element.
private static int BinarySearchModified(int[] input, double toSearch)
{
int start = 0;
int end = input.Length - 1;
while (start <= end)
{
int mid = start + (end - start)/2;
if (toSearch < input[mid]) end = mid - 1;
else start = mid + 1;
}
return start;
}
public static Result GetRange(int[] input, int toSearch)
{
if (input == null) return new Result(-1, -1);
int low = BinarySearchModified(input, toSearch - 0.5);
if ((low >= input.Length) || (input[low] != toSearch)) return new Result(-1, -1);
int high = BinarySearchModified(input, toSearch + 0.5);
return new Result(low, high - 1);
}
public struct Result
{
public int LowIndex;
public int HighIndex;
public Result(int low, int high)
{
LowIndex = low;
HighIndex = high;
}
}
public void printCopies(int[] array)
{
HashMap<Integer, Integer> memberMap = new HashMap<Integer, Integer>();
for(int i = 0; i < array.size; i++)
if(!memberMap.contains(array[i]))
memberMap.put(array[i], 1);
else
{
int temp = memberMap.get(array[i]); //get the number of occurances
memberMap.put(array[i], ++temp); //increment his occurance
}
//check keys which occured more than once
//dump them in a ArrayList
//return this ArrayList
}
Alternatevely, instead of counting the number of occurances, you can put their indices in a arraylist and put that in the map instead of the count.
HashMap<Integer, ArrayList<Integer>>
//the integer is the value, the arraylist a list of their indices
public void printCopies(int[] array)
{
HashMap<Integer, ArrayList<Integer>> memberMap = new HashMap<Integer, ArrayList<Integer>>();
for(int i = 0; i < array.size; i++)
if(!memberMap.contains(array[i]))
{
ArrayList temp = new ArrayList();
temp.add(i);
memberMap.put(array[i], temp);
}
else
{
ArrayList temp = memberMap.get(array[i]); //get the lsit of indices
temp.add(i);
memberMap.put(array[i], temp); //update the index list
}
//check keys which return lists with length > 1
//handle the result any way you want
}
heh, i guess this will have to be posted.
int predefinedDuplicate = //value here;
int index = Arrays.binarySearch(array, predefinedDuplicate);
int leftIndex, rightIndex;
//search left
for(leftIndex = index; array[leftIndex] == array[index]; leftIndex--); //let it run thru it
//leftIndex is now the first different element to the left of this duplicate number string
for(rightIndex = index; array[rightIndex] == array[index]; rightIndex++); //let it run thru it
//right index contains the first different element to the right of the string
//you can arraycopy this [leftIndex+1, rightIndex-1] string or just print it
for(int i = leftIndex+1; i<rightIndex; i++)
System.out.println(array[i] + "\t");
I'm implementing a mergeSort function. I understand the logic of divide and conquer, but the actual merging part is confusing me. This is a past homework problem, but I'm trying to understand it.
/**
* Implement merge sort.
*
* It should be:
* stable
* Have a worst case running time of:
* O(n log n)
*
* And a best case running time of:
* O(n log n)
*
* You can create more arrays to run mergesort, but at the end,
* everything should be merged back into the original T[]
* which was passed in.
*
* ********************* IMPORTANT ************************
* FAILURE TO DO SO MAY CAUSE ClassCastException AND CAUSE
* YOUR METHOD TO FAIL ALL THE TESTS FOR MERGE SORT
* ********************************************************
*
* Any duplicates in the array should be in the same relative position
* after sorting as they were before sorting.
*
* #throws IllegalArgumentException if the array or comparator is null
* #param <T> data type to sort
* #param arr the array to be sorted
* #param comparator the Comparator used to compare the data in arr
*/
For this method, the parameters, public, static, and generics can't be changed. I don't know how to do the recursive merge function.
public static <T> void mergesort(T[] arr, Comparator<T> comparator) {
if (arr == null || comparator == null) {
throw new IllegalArgumentException("Null arguments were passed.");
}
if (arr.length >= 2) {
//Midpoint from which we will split the array.
int middle = arr.length / 2;
//Each half of the split array
T[] left = (T[]) new Object[middle];
T[] right = (T[]) new Object[arr.length - middle];
//Copy items from original into each half
for (int i = 0; i < middle; i++) {
left[i] = arr[i];
}
for (int i = middle; i < length; i++) {
right[i] = arr[i];
}
//Keep splitting until length is 1
mergesort(left, comparator);
mergesort(right, comparator);
//merge each array back into original which would now be sorted.
merge(left, right, middle, arr, comparator);
merge(right, middle, arr, comparator);
}
}
private static <T> T[] merge(T[] left, T[] right, int middle, T[] arr,
Comparator<T>
comparator) {
int i = 1, j = middle + 1, k = 1;
while (i <= middle && j <= arr.length) {
arr[k++] = (comparator.compare(arr[k], partioned[i]) < 0)
? arr[j++] : partioned[i++];
}
while (i <= middle) {
arr[k++] = partioned[k++];
}
}
Here's one possible solution. I don't remember if there's a better way to do this, since I generally just use Collections.sort().
Note that there's no need to return a value, since the contents of the original array are going to be modified.
There's also no need to pass in the middle index.
private static <T> void merge(T[] left, T[] right, T[] dest) {
if (left.length + right.length != dest.length)
throw new IllegalArgumentException(
"left length + right length must equal destination length");
int leftIndex = 0;
int rightIndex = 0;
int destIndex = 0;
while (destIndex < dest.length) {
if (leftIndex >= left.length) //no more entries in left array, use right
dest[destIndex++] = right[rightIndex++];
else if (rightIndex >= right.length) // no more entries in right array, use left
dest[destIndex++] = left[leftIndex++];
else if (left[leftIndex] < right[rightIndex]) //otherwise pick the lower value
dest[destIndex++] = left[leftIndex++];
else
dest[destIndex++] = right[rightIndex++];
}
}
I have array with constant size (size = 20 in real life), duplicates are allowed For example:
1 2 2 3 3 4 5 6 7 8 9
Now exactly one element updates:
1 5 2 3 3 4 5 6 7 8 9
I need to resort this array. Should I just use bubblesort?
update I don't know how to call what I wrote. But i suppose it is not possible to sort faster. comments are welcome!
// array is already almost sorted and INCREASING, element at pos need to be inserted to the right place
private void SortQuotes(List<Quote> quoteList, int pos)
{
var quoteToMove = quoteList[pos];
if (pos == 0 || quoteList[pos - 1].Price < quoteToMove.Price)
{
MoveElementsDown(quoteList, pos);
} else if (pos == quoteList.Count - 1 || quoteList[pos + 1].Price > quoteToMove.Price)
{
MoveElementsUp(quoteList, pos);
}
}
private void MoveElementsDown(List<Quote> quoteList, int pos)
{
var quoteToInsert = quoteList[pos];
var price = quoteToInsert.Price;
for (int i = pos - 1; i >= 0; i--)
{
var nextQuote = quoteList[i];
if (nextQuote.Price > price)
{
quoteList[i + 1] = quoteList[i];
if (i == 0) // last element
{
quoteList[i] = quoteToInsert;
}
}
else
{
quoteList[i + 1] = quoteToInsert;
break;
}
}
}
private void MoveElementsUp(List<Quote> quoteList, int pos)
{
var quoteToInsert = quoteList[pos];
var price = quoteToInsert.Price;
for (int i = pos + 1; i < quoteList.Count; i++)
{
var nextQuote = quoteList[i];
if (nextQuote.Price < price)
{
quoteList[i - 1] = quoteList[i];
if (i == quoteList.Count - 1) // last element
{
quoteList[i] = quoteToInsert;
}
}
else
{
quoteList[i - 1] = quoteToInsert;
break;
}
}
}
updated i do know which element is odd, i.e. it's position is known!
This solution shifts each element by one until the right position for the odd element is found. As it has been overwritten already in the first step, it is saved in a temporary variable 'h' and then written to the final position. It requires the minimum of comparisions and shift operations:
static void MoveOddElementToRightPosition(int[] a, int oddPosition)
{
int h = a[oddPosition];
int i;
if (h > a[oddPosition + 1])
for (i = oddPosition; i < a.Count()-1 && a[i+1] <= h; i++)
a[i] = a[i+1];
else
for (i = oddPosition; i > 0 && a[i-1] >= h; i--)
a[i] = a[i - 1];
a[i] = h;
}
Bubblesort will use n^2 time if the last element needs to get to the front. Use insertion sort.
As the array is small, insertion sort takes roughly ~O(n) time for small arrays and if you are just updating 1 value, insertion sort should fulfil your purpose in the best possible way.
It can be done in O(n). If you don't know the element then search for the element in O(n) and then You just need to compare and swap for each element and that would take O(n). So total 2n which means O(n).If you know the element which has been modified then compare and swap for each element.
If you're interested in replacing an element quickly, then you can also use a structure where deletion and insertion is fast, like for example a TreeSet in Java. That means O(log(n)) theoretically, but if you just manipulate arrays of 20 elements it may not be worth it
If the set of all different elements is finite, like in your example where you just use numbers for 1 to 9, then there is a solution in O(1). Instead of having a sorted list you just keep an array with the number of occurrences of your elements.
If you still want to keep everything in an array, then the fastest way is this
find the position A of of the element you're going to remove by bisection in O(log(n)).
find the position B of where your new element is going to end up in the same way. More precisely B is the smallest index where new_element < a[k] for every k > B
if A < B, move all elements between A + 1 and B to their left, then set the new element to position B. if B > A, you do the same thing but to the right. Now this step is in O(n), but there's no logic, it's just moving memory around. In C you'd use memmove for this and it's heavily optimized, but I don't know any Java equivalent.
You don't need to sort it again.
Only one element changes. So you just need to go through the list and put the changed number to appropriate place. This will be of O(n) complexity.
int a[] = {1, 5, 2, 3, 3, 4, 5, 6, 7, 8, 9};
int count = 0;
//find the odd element
for(int jj=1; jj< a.length; jj++){
if(a[jj] < a[count])
break;
else count++;
}
System.out.println(" Odd position " + count);
//put odd element to proper position
for(int k= count+1; k<a.length; k++){
if(a[count] > a[k]){
int t = a[count];
a[count] = a[k];
a[k] = t;
count++;
}
}
Above is the working code tested for given input.
Enjoy.
Bubblesort is quite OK in this case with 20 compares max.
But finding the new position with binary search is O(log(n)), that is 5 compares in this case.
Somewhat faster, if you need the last bit odd speed use the binary search otherwise you can stick with bubble sort.
Here is a naive implementation in plain C. Remove the fprintf(stderr, ... after testing. The ITEM can be anything, as long as a comparison function is possible. Otherwise: use pointers to ITEM, (and maybe add an extra sizeofelem argument, ala qsort)
#include <stdio.h>
#include <string.h>
typedef int ITEM;
int item_cmp(ITEM one, ITEM two);
unsigned one_bubble( ITEM *arr, unsigned cnt, unsigned hot , int (*cmp)(ITEM,ITEM) );
int item_cmp(ITEM one, ITEM two)
{
fprintf(stderr,"Cmp= %u to %u: %d\n", one, two, one-two);
if (one > two) return 1;
else if (one < two) return -1;
else return 0;
}
unsigned one_bubble( ITEM *arr, unsigned cnt, unsigned hot , int (*cmp)(ITEM,ITEM) )
{
unsigned goal = cnt;
int diff;
ITEM temp;
/* hot element should move to the left */
if (hot > 0 && (diff=cmp( arr[hot-1], arr[hot])) > 0) {
/* Find place to insert (this could be a binary search) */
for (goal= hot; goal-- > 0; ) {
diff=cmp( arr[goal], arr[hot]);
if (diff <= 0) break;
}
goal++;
fprintf(stderr,"Move %u LEFT to %u\n", hot, goal);
if (goal==hot) return hot;
temp = arr[hot];
/* shift right */
fprintf(stderr,"memmove(%u,%u,%u)\n", goal+1, goal, (hot-goal) );
memmove(arr+goal+1, arr+goal, (hot-goal) *sizeof temp);
arr[goal] = temp;
return goal; /* new position */
}
/* hot element should move to the right */
else if (hot < cnt-1 && (diff=cmp( arr[hot], arr[hot+1])) > 0) {
/* Find place to insert (this could be a binary search) */
for (goal= hot+1; goal < cnt; goal++ ) {
diff=cmp( arr[hot], arr[goal]);
if (diff <= 0) break;
}
goal--;
fprintf(stderr,"Move %u RIGHT to %u\n", hot, goal);
if (goal==hot) return hot;
temp = arr[hot];
/* shift left */
fprintf(stderr,"memmove(%u,%u,%u)\n", hot, hot+1, (goal-hot) );
memmove(arr+hot, arr+hot+1, (goal-hot) *sizeof temp);
arr[goal] = temp;
return goal; /* new position */
}
fprintf(stderr,"Diff=%d Move %u Not to %u\n", diff, hot, goal);
return hot;
}
ITEM array[10] = { 1,10,2,3,4,5,6,7,8,9,};
#define HOT_POS 1
int main(void)
{
unsigned idx;
idx = one_bubble(array, 10, HOT_POS, item_cmp);
printf("%u-> %u\n", HOT_POS, idx );
for (idx = 0; idx < 10; idx++) {
printf("%u: %u\n", idx, array[idx] );
}
return 0;
}