Partition algorithm with two loops c++ - c++

I was given pseudo code for a partition algorithm but I'm not really sure how to implement it.
Below is the pseudo code and my implementation. Please let me know if this is correct/explain what it's doing. Right now I have a partial understanding of it but it is not correct.
Input: 0.963, 0.003, 0.0251, 0.353, 0.667, 0.838, 0.335, 0.915, 0.796, 0.833, 0.345, 0.871, 0.089, 0.888, 0.701, 0.735
Expected: 0.003 0.0251 0.089 0.335 0.345 0.353 0.667 0.701 0.735 0.796 0.833 0.838 0.871 0.888 0.915 0.963
Actual: 0.003000 0.025100 0.353000 0.667000 0.838000 0.335000 0.915000 0.796000 0.833000 0.345000 0.871000 0.089000 0.888000 0.7 01000 0.735000 0.963000
int partition_data( float xdata[], int ndata, float xmiddle ) {
int left;
int right;
int j,i;
float temp;
for(i = 0; i < xmiddle; i ++){
if(left == right){
left += 1;
}
else{
for( j = ndata - 1; j >= xmiddle; j--){
if(left == right){
right -= 1;
}
else{
temp = xdata[j];
xdata[j] = xdata[i];
xdata[i] = temp;
right -= 1;
if(left == right){
left += 1;
break;
}
}
}
}
}
}

As others already said, both here and at Code Review, the pseudocode is quite non-standard, and hard to read (although it's not true it is mis-indented).
However, if you're not interested in improving it, but just want to implement it 'as is', here it is, word-by-word translated into C language:
int Partition(float X[], int ndata, float xmiddle)
{
int left = 0;
int right = ndata - 1;
while(1) { // 'left' loop
if(X[left] < xmiddle)
{
if(left == right)
return left + 1;
left ++;
}
else
while(1) { // 'right' loop
if(X[right] >= xmiddle)
{
if(left == right)
return left;
right --;
}
else
{
float tmp = X[left]; // these three lines
X[left] = X[right]; // swap the two values
X[right] = tmp; // X[left] and X[right]
right --;
if(left == right)
return left + 1;
left ++;
break; // exit the 'right' loop
}
} // end of 'right' loop
} // end of 'left' loop
} // end of Parition
The code is actually C, in C++ you might make it a function template with a type parameter instead of explicit float, so that the function may partition arrays of different types (as long as operators < and >= are defined). You might also use std::swap for efficient swapping data and dropping the explicit tmp variable.

Maybe the assignment was to implement a full quicksort?
Quicksort with conventional pre-increment / pre-decrement Hoare partition scheme:
int Partition(float x[], int lo, int hi)
{
float xmiddle = x[(lo+hi)/2];
int left = lo - 1;
int right = hi + 1;
float tmp;
while (1){
while (x[++left] < xmiddle);
while (x[--right] > xmiddle);
if(left >= right)
break;
tmp = x[left];
x[left] = x[right];
x[right] = tmp;
}
return right;
}
void QuickSort(float x[], int lo, int hi){
int pivot;
if (lo < hi){
pivot = Partition(x, lo, hi);
QuickSort(x, lo, pivot);
QuickSort(x, pivot+1, hi);
}
}
One issue with quicksort is that a minor bug in the partition scheme may work for most data patterns and only fail for specific data patterns, making it difficult to determine if a quicksort is really bug free.
Quicksort using modified version from the psuedo code above. This works for the example data, but I'm not sure it's bug free. Partition should return an index that points to a value == xmiddle:
int Partition(float X[], int lo, int hi)
{
int left = lo;
int right = hi;
float xmiddle = X[(lo+hi)/2];
float tmp;
while(1) {
while(X[left] < xmiddle){
if(left == right)
return left+1;
left++;
}
while(X[right] >= xmiddle){
if(left == right)
return left;
right--;
}
tmp = X[left];
X[left] = X[right];
X[right] = tmp;
right --;
if(left == right)
return left + 1;
left ++;
}
}
void QuickSort(float x[], int lo, int hi){
int pivot;
if (lo < hi){
pivot = Partition(x, lo, hi);
QuickSort(x, lo, pivot);
QuickSort(x, pivot+1, hi);
}
}

Explanation of the algorithm
The routine re-arranges data so that items less than the supplied xmiddle value remain on the left side of the array (at smaller indices) and items greater than xmiddle are put to the right part (at bigger indices). The return value is a length of the left part.
Variables left and right scan the array from both ends to find items which should be swapped.
The 'left loop' (starting at line 4) increments the left index until it finds an item greater than or equal to xmiddle (line 5) or until it reaches the right position (line 6). As right is initially the last index of the array (and later it can only be decremented), the loop must eventually stop incrementing left.
If the left index meets right then all items below and at left are less than xmiddle, so left+1 is returned as a length of the left subarray. Note that if xmiddle is chosen bigger than all items of the array, the return value is equal to the array's length (ndata).
If left stops at an item greater than or equal to xmiddle, then the 'otherwise' branch at line 9 executes and the 'right loop' starts. Similar to the left loop it decrements right, stepping over all items greater than or equal to xmiddle until the right index meets left. If the two indices meet (line 11), then all items below them are less than, and all items above and at them are greater than or equal to xmiddle; then left is returned as the length of the left part.
On the other hand, if the right index finds an item less than xmiddle (i.e. not satisfying the condition at line 10), then it needs to be swapped with the item at index left, which is greater than or equal to xmiddle (it didn't satisfy the condition at line 5). Then the 'otherwise' branch at line 13 is executed.
The branch swaps the two values (line 14), so both left and right part become one item longer. To reflect that, the right index is decremented (line 15) and then compared to left. If they got equal (line 16), i.e. we swapped adjacent items, the partitioning is done, so left+1 is returned as a length of the left part. Otherwise left gets incremented (line 17) and we exit the 'right loop' (line 18) and proceed to the closing brace of the 'left loop' in line 20, which brings the execution back to line 4.
Then again left gets incremented (that is the left part grows) until it meets the right part or finds an item to be swapped. And so on...
The loops invariants are:
left ≤ right
X[i] < xmiddle for 0 ≤ i < left
X[i] ≥ xmiddle for right < i < ndata
and the un-tested part of the array gets reduced (the difference right - left is decremented) at least by 1 in every iteration of a loop.

Related

Improving the performance of this search?

Is there way to do the following search using a faster way? The items on A array are sorted in DESC order.
int find_pos(int A[], int value, int items_no, bool only_exact_match)
{
for(int i = 0; i < items_no; i++)
if(value == A[i] || (value > A[i] && !only_exact_match))
return i;
return -1;
}
You can use std::lower_bound algorithm in your case. It performs binary search with O(log N), as other people wrote. It will be something like this:
int find_pos(int A[], int value, int items_no, bool only_exact_match)
{
const int *pos_ptr = std::lower_bound(A, A + items_no, value, std::greater<int>());
const ptrdiff_t pos = pos_ptr - A;
if (pos >= items_no)
return -1;
if (*pos_ptr != value && only_exact_match)
return -1;
return pos;
}
A binary search
int left = 0;
int right = items_no; // Exclusive
while (left < right) {
int mid = (left + right) / 2;
if (value == A[mid])
return mid;
if (value < A[mid]) {
left = mid + 1;
} else {
right = mid;
}
}
return only_exact_match ? -1 : right - 1; // The greater
Because your array is sorted, you can search in steps, akin to a bisection. First, check the midpoint against your value. If it's equal, you have your answer. If it's greater, your value is in the lower half of the array. If not, your value is on the upper half. Repeat this process by bisecting the remaining elements of the array until you find your value, or run out of elements. As for your second if clause, if no matching value is found, the closest smaller element is element i+1, if that exists (i.e. you are not at the end of the array).

Verify if the sum of 2 numbers is x using DivideEtImpera

I want to check if the sum of 2 numbers (a and b) from a given array A is x in O(nlogn). I wrote the following code but it appears it's not working properly. Why is that?
bool flag = false;
int pairDivideEtImpera(int A[], int left, int right, int x) {
if (left == right)
return A[left];
int mid = (left + right) / 2;
if (pairDivideEtImpera(A, left, mid, x) + pairDivideEtImpera(A, mid + 1, right, x) == x)
flag = true;
}
I take every 2 elements from the array and I verify if the sum is x. If so, the flag will be true and I'll know I have a pair (a,b), where a+b = x. What am I missing and why this doesn't show the right thing?
I don't thing you can use divide-and-conquer in this case. Here is a solution in O(nlogn)
STEP 1: You sort the array, it takes you O(nlogn) operations.
STEP 2: You look at the smallest and biggest value. If the sum is too big you decrease the big value, if it is too small you increase the small value. Do this until you find x or big value == small calue
Here is the code for step 2 (with a sorted array A)
int left =0;
int right=A.length - 1;
bool flag = false;
while (right>left){
int y = A[left]+A[right]
if (y==x){
flag = true;
break;
} else if (y>x) {
right--;
} else {
left++;
}
}

Counting number of comparisons in Quick Sort using middle element as Pivot C++

I'm taking a course on Coursera: "Design and Analysis of Algorithms" provided by Stanford University. A text file with 10000 distinct integers is provided in random order. The task is to count the number of comparisons after performing Quick Sort using the middle element as the pivot in each recursive call.
Here is the link to the text file:
https://drive.google.com/drive/u/0/folders/0B_WysIAkKMzzN3o4SEhoS0RjMUU
This is what I did:
#include <iostream>
#include <fstream>
using namespace std;
int partition(int arr[],int left,int pivot,int right);
int quickSort(int arr[], int left, int right);
int count = 0;
int partition(int arr[],int left,int pivot,int right) {
int split = left + 1, tmp;
for (int track = left + 1; track < right; track++) {
if (arr[track] < pivot) {
tmp = arr[track];
arr[track] = arr[split];
arr[split] = tmp;
split++;
}
}
tmp = arr[split - 1];
arr[split - 1] = arr[left];
arr[left] = tmp;
return split - 1;
}
int quickSort(int arr[], int left, int right) {
if (right <= left)
return 0;
int mid = (right + left - 1)/2;
int pivot = arr[mid];
arr[mid] = arr[left];
arr[left] = pivot;
int split = partition(arr,left,pivot,right);
count += right - left - 1;
quickSort(arr,left,split);
quickSort(arr,split + 1,right);
return count;
}
int main() {
int ans;
int arr[10001], i = 0;
ifstream fin("QuickSort.txt");
while (fin >> arr[i]) {
i++;
}
fin.close();
ans = quickSort(arr, 0, i);
//To check if array is sorted
for(int x = 0;x < i;x++) {
cout<<arr[x]<<endl;
}
cout<<endl;
cout << ans;
return 0;
}
Although the array is sorted fine, the count is 150657, which is wrong.
Can someone point out if I'm missing something here? P.S. I've been at it for days, so I'd really appreciate it if someone helped me out!
When you want to count something, the direct approach is just count what you are counting. So far as I can tell, you want to count the number of times the following line is executed:
if (arr[track] < pivot)
So all you need to do is put ++count; before that line. If you want to do less computation and/or understand more, think through how many times it is executed:
Edit: I really wasn't thinking when I wrote the stuff below the ---. It is just wrong. So directly counting matches your original method of computing the count. I tested your code and now believe you are computing the correct count. Try desk checking a small example, and explain why you believe the giant count is wrong.
I could not access your data file, so I can't confirm or deny your program computes the right count for that file.
for (int track = left + 1; track < right; track++)
That loop would execute its body at most (right-left-2) times. Since you know right>left at that point, it is safe to say exactly (rather than "at most") right-left-2.
I think your count is computed correctly. But the global count is so inappropriate, I think that must be corrected. You could eliminate that variable entirely by changing from:
count += right - left - 1;
quickSort(arr,left,split);
quickSort(arr,split + 1,right);
return count;
to
return
right - left - 1
+ quickSort(arr,left,split)
+ quickSort(arr,split + 1,right);
or make count local, changing to:
int count = right - left - 1;
count += quickSort(arr,left,split);
count += quickSort(arr,split + 1,right);
return count;
count += right - left - 1;
I feel that, here instead try using "right - left". Since "right-left+1" is what actually the length of the sub array, this will give the M-1 that you are seeking for. Hope this helps!
Your mid index for picking the middle element is not right. You are no accounting for the fact that you are sending in left and right as certain points in the array and not as 0 and n. For example if you have [5,4,2,1,6,7] and you send the right half of this array then your left is gonna be 3 and right is going to be 5 (indices). The median of this array is going to be the 6 but the index of that number is actually 4. What your line here would return is (5 + 3 - 1)/2 = 3 which is not correct. NOTE: I am assuming right is your last element.
int mid = (right + left - 1)/2;
Try this instead have your array accept left as 0 and right as length of your array instead and for mid try:
int mid = ceil((right-left)/2) - 1 + left
You can also use your old way of (right + left - 1)/2 but make sure you do a ceil and double check to see if you are actually picking the right medians.

Quicksort infinite loop if there are repeating values

I have a quicksort program that works great until I try to sort an array that has a repeating number. The program gets stuck in an infinite loop. I believe this is happening in the While(lower < upper) block of code.
void quickSort(int array[], int size){
if(size < 2) return;
int pivot, lower, upper, temp;
//Set the indeces for the first and last elements
lower = 0;
upper = size - 1;
//Select pivot element randomly
pivot = array[rand() % (size)];
while(lower < upper){
//Lower must be a number < than pivot and upper a number >= pivot
while(array[lower] < pivot){
lower++;
}
while(array[upper] > pivot){
upper--;
}
//Swap upper and lower
temp = array[lower];
array[lower] = array[upper];
array[upper] = temp;
}
//Repeat the past actions on the two partitions of the array recursively
quickSort(array, lower);
quickSort(&array[lower+1], size-lower-1);
}
EDIT: Code added.
From Wikipedia, the pseudo-code for in-place quicksort is as follows:
(Not saying that they should be trusted blindly)
function quicksort(array)
if length(array) > 1
pivot := select any element of array
left := first index of array
right := last index of array
while left ≤ right
while array[left] < pivot
left := left + 1
while array[right] > pivot
right := right - 1
if left ≤ right
swap array[left] with array[right]
left := left + 1
right := right - 1
quicksort(array from first index to right)
quicksort(array from left to last index)
So you see it is quite similar to your algorithm, with minor modifications.
while(lower <= upper){
Also you need to swap only if lower <= upper and then update the indices.
And your code differs in the recursive calls:
quicksort(array from first index to right) {array[0] to array[upper]}
quicksort(array from left to last index) {array[lower] to array[size-1]}
This is because now that it has exited the while loop, upper is less than lower.
Full working code:
#include <iostream>
#include <cstdlib>
using namespace std;
void quickSort(int array[], int size){
if(size < 2) return;
int pivot, lower, upper, temp;
//Set the indeces for the first and last elements
lower = 0;
upper = size - 1;
//Select pivot element randomly
pivot = array[rand() % (size)];
while(lower <= upper){
//Lower must be a number < than pivot and upper a number >= pivot
while(array[lower] < pivot){
lower++;
}
while(array[upper] > pivot){
upper--;
}
//Swap upper and lower
if ( lower <= upper ) {
temp = array[lower];
array[lower] = array[upper];
array[upper] = temp;
lower++;
upper--;
}
}
//Repeat the past actions on the two partitions of the array recursively
quickSort(array, upper+1);
quickSort(&array[lower], size-lower);
}
int main() {
// your code goes here
int myArray[] = { 10, 9, 8, 7, 7, 7, 7, 3, 2, 1};
quickSort( myArray, 10 );
for ( int i = 0; i < 10; i++ )
cout << myArray[i] << " ";
return 0;
}
Output:
1 2 3 7 7 7 7 8 9 10

Searching in a sorted and rotated array

While preparing for an interview I stumbled upon this interesting question:
You've been given an array that is sorted and then rotated.
For example:
Let arr = [1,2,3,4,5], which is sorted
Rotate it twice to the right to give [4,5,1,2,3].
Now how best can one search in this sorted + rotated array?
One can unrotate the array and then do a binary search. But that is no better than doing a linear search in the input array, as both are worst-case O(N).
Please provide some pointers. I've googled a lot on special algorithms for this but couldn't find any.
I understand C and C++.
This can be done in O(logN) using a slightly modified binary search.
The interesting property of a sorted + rotated array is that when you divide it into two halves, atleast one of the two halves will always be sorted.
Let input array arr = [4,5,6,7,8,9,1,2,3]
number of elements = 9
mid index = (0+8)/2 = 4
[4,5,6,7,8,9,1,2,3]
^
left mid right
as seem right sub-array is not sorted while left sub-array is sorted.
If mid happens to be the point of rotation them both left and right sub-arrays will be sorted.
[6,7,8,9,1,2,3,4,5]
^
But in any case one half(sub-array) must be sorted.
We can easily know which half is sorted by comparing start and end element of each half.
Once we find which half is sorted we can see if the key is present in that half - simple comparison with the extremes.
If the key is present in that half we recursively call the function on that half
else we recursively call our search on the other half.
We are discarding one half of the array in each call which makes this algorithm O(logN).
Pseudo code:
function search( arr[], key, low, high)
mid = (low + high) / 2
// key not present
if(low > high)
return -1
// key found
if(arr[mid] == key)
return mid
// if left half is sorted.
if(arr[low] <= arr[mid])
// if key is present in left half.
if (arr[low] <= key && arr[mid] >= key)
return search(arr,key,low,mid-1)
// if key is not present in left half..search right half.
else
return search(arr,key,mid+1,high)
end-if
// if right half is sorted.
else
// if key is present in right half.
if(arr[mid] <= key && arr[high] >= key)
return search(arr,key,mid+1,high)
// if key is not present in right half..search in left half.
else
return search(arr,key,low,mid-1)
end-if
end-if
end-function
The key here is that one sub-array will always be sorted, using which we can discard one half of the array.
The accepted answer has a bug when there are duplicate elements in the array. For example, arr = {2,3,2,2,2} and 3 is what we are looking for. Then the program in the accepted answer will return -1 instead of 1.
This interview question is discussed in detail in the book 'Cracking the Coding Interview'. The condition of duplicate elements is specially discussed in that book. Since the op said in a comment that array elements can be anything, I am giving my solution as pseudo code in below:
function search( arr[], key, low, high)
if(low > high)
return -1
mid = (low + high) / 2
if(arr[mid] == key)
return mid
// if the left half is sorted.
if(arr[low] < arr[mid]) {
// if key is in the left half
if (arr[low] <= key && key <= arr[mid])
// search the left half
return search(arr,key,low,mid-1)
else
// search the right half
return search(arr,key,mid+1,high)
end-if
// if the right half is sorted.
else if(arr[mid] < arr[high])
// if the key is in the right half.
if(arr[mid] <= key && arr[high] >= key)
return search(arr,key,mid+1,high)
else
return search(arr,key,low,mid-1)
end-if
else if(arr[mid] == arr[low])
if(arr[mid] != arr[high])
// Then elements in left half must be identical.
// Because if not, then it's impossible to have either arr[mid] < arr[high] or arr[mid] > arr[high]
// Then we only need to search the right half.
return search(arr, mid+1, high, key)
else
// arr[low] = arr[mid] = arr[high], we have to search both halves.
result = search(arr, low, mid-1, key)
if(result == -1)
return search(arr, mid+1, high, key)
else
return result
end-if
end-function
You can do 2 binary searches: first to find the index i such that arr[i] > arr[i+1].
Apparently, (arr\[1], arr[2], ..., arr[i]) and (arr[i+1], arr[i+2], ..., arr[n]) are both sorted arrays.
Then if arr[1] <= x <= arr[i], you do binary search at the first array, else at the second.
The complexity O(logN)
EDIT:
the code.
My first attempt would be to find using binary search the number of rotations applied - this can be done by finding the index n where a[n] > a[n + 1] using the usual binary search mechanism.
Then do a regular binary search while rotating all indexes per shift found.
int rotated_binary_search(int A[], int N, int key) {
int L = 0;
int R = N - 1;
while (L <= R) {
// Avoid overflow, same as M=(L+R)/2
int M = L + ((R - L) / 2);
if (A[M] == key) return M;
// the bottom half is sorted
if (A[L] <= A[M]) {
if (A[L] <= key && key < A[M])
R = M - 1;
else
L = M + 1;
}
// the upper half is sorted
else {
if (A[M] < key && key <= A[R])
L = M + 1;
else
R = M - 1;
}
}
return -1;
}
If you know that the array has been rotated s to the right, you can simply do a binary search shifted s to the right. This is O(lg N)
By this, I mean, initialize the left limit to s and the right to (s-1) mod N, and do a binary search between these, taking a bit of care to work in the correct area.
If you don't know how much the array has been rotated by, you can determine how big the rotation is using a binary search, which is O(lg N), then do a shifted binary search, O(lg N), a grand total of O(lg N) still.
Reply for the above mentioned post "This interview question is discussed in detail in the book 'Cracking the Coding Interview'. The condition of duplicate elements is specially discussed in that book. Since the op said in comment that array elements can be anything, I am giving my solution as pseudo code in below:"
Your solution is O(n) !! (The last if condition where you check both halves of the array for a single condition makes it a sol of linear time complexity )
I am better off doing a linear search than getting stuck in a maze of bugs and segmentation faults during a coding round.
I dont think there is a better solution than O(n) for a search in a rotated sorted array (with duplicates)
If you know how (far) it was rotated you can still do a binary search.
The trick is that you get two levels of indices: you do the b.s. in a virtual 0..n-1 range and then un-rotate them when actually looking up a value.
You don't need to rotate the array first. You can use binary search on the rotated array (with some modifications).
Let N be the number you are searching for:
Read the first number (arr[start]) and the number in the middle of the array (arr[end]):
if arr[start] > arr[end] --> the first half is not sorted but the second half is sorted:
if arr[end] > N --> the number is in index: (middle + N - arr[end])
if N repeat the search on the first part of the array (see end to be the middle of the first half of the array etc.)
(the same if the first part is sorted but the second one isn't)
public class PivotedArray {
//56784321 first increasing than decreasing
public static void main(String[] args) {
// TODO Auto-generated method stub
int [] data ={5,6,7,8,4,3,2,1,0,-1,-2};
System.out.println(findNumber(data, 0, data.length-1,-2));
}
static int findNumber(int data[], int start, int end,int numberToFind){
if(data[start] == numberToFind){
return start;
}
if(data[end] == numberToFind){
return end;
}
int mid = (start+end)/2;
if(data[mid] == numberToFind){
return mid;
}
int idx = -1;
int midData = data[mid];
if(numberToFind < midData){
if(midData > data[mid+1]){
idx=findNumber(data, mid+1, end, numberToFind);
}else{
idx = findNumber(data, start, mid-1, numberToFind);
}
}
if(numberToFind > midData){
if(midData > data[mid+1]){
idx = findNumber(data, start, mid-1, numberToFind);
}else{
idx=findNumber(data, mid+1, end, numberToFind);
}
}
return idx;
}
}
short mod_binary_search( int m, int *arr, short start, short end)
{
if(start <= end)
{
short mid = (start+end)/2;
if( m == arr[mid])
return mid;
else
{
//First half is sorted
if(arr[start] <= arr[mid])
{
if(m < arr[mid] && m >= arr[start])
return mod_binary_search( m, arr, start, mid-1);
return mod_binary_search( m, arr, mid+1, end);
}
//Second half is sorted
else
{
if(m > arr[mid] && m < arr[start])
return mod_binary_search( m, arr, mid+1, end);
return mod_binary_search( m, arr, start, mid-1);
}
}
}
return -1;
}
First, you need to find the shift constant, k.
This can be done in O(lgN) time.
From the constant shift k, you can easily find the element you're looking for using
a binary search with the constant k. The augmented binary search also takes O(lgN) time
The total run time is O(lgN + lgN) = O(lgN)
To find the constant shift, k. You just have to look for the minimum value in the array. The index of the minimum value of the array tells you the constant shift.
Consider the sorted array
[1,2,3,4,5].
The possible shifts are:
[1,2,3,4,5] // k = 0
[5,1,2,3,4] // k = 1
[4,5,1,2,3] // k = 2
[3,4,5,1,2] // k = 3
[2,3,4,5,1] // k = 4
[1,2,3,4,5] // k = 5%5 = 0
To do any algorithm in O(lgN) time, the key is to always find ways to divide the problem by half.
Once doing so, the rest of the implementation details is easy
Below is the code in C++ for the algorithm
// This implementation takes O(logN) time
// This function returns the amount of shift of the sorted array, which is
// equivalent to the index of the minimum element of the shifted sorted array.
#include <vector>
#include <iostream>
using namespace std;
int binarySearchFindK(vector<int>& nums, int begin, int end)
{
int mid = ((end + begin)/2);
// Base cases
if((mid > begin && nums[mid] < nums[mid-1]) || (mid == begin && nums[mid] <= nums[end]))
return mid;
// General case
if (nums[mid] > nums[end])
{
begin = mid+1;
return binarySearchFindK(nums, begin, end);
}
else
{
end = mid -1;
return binarySearchFindK(nums, begin, end);
}
}
int getPivot(vector<int>& nums)
{
if( nums.size() == 0) return -1;
int result = binarySearchFindK(nums, 0, nums.size()-1);
return result;
}
// Once you execute the above, you will know the shift k,
// you can easily search for the element you need implementing the bottom
int binarySearchSearch(vector<int>& nums, int begin, int end, int target, int pivot)
{
if (begin > end) return -1;
int mid = (begin+end)/2;
int n = nums.size();
if (n <= 0) return -1;
while(begin <= end)
{
mid = (begin+end)/2;
int midFix = (mid+pivot) % n;
if(nums[midFix] == target)
{
return midFix;
}
else if (nums[midFix] < target)
{
begin = mid+1;
}
else
{
end = mid - 1;
}
}
return -1;
}
int search(vector<int>& nums, int target) {
int pivot = getPivot(nums);
int begin = 0;
int end = nums.size() - 1;
int result = binarySearchSearch(nums, begin, end, target, pivot);
return result;
}
Hope this helps!=)
Soon Chee Loong,
University of Toronto
For a rotated array with duplicates, if one needs to find the first occurrence of an element, one can use the procedure below (Java code):
public int mBinarySearch(int[] array, int low, int high, int key)
{
if (low > high)
return -1; //key not present
int mid = (low + high)/2;
if (array[mid] == key)
if (mid > 0 && array[mid-1] != key)
return mid;
if (array[low] <= array[mid]) //left half is sorted
{
if (array[low] <= key && array[mid] >= key)
return mBinarySearch(array, low, mid-1, key);
else //search right half
return mBinarySearch(array, mid+1, high, key);
}
else //right half is sorted
{
if (array[mid] <= key && array[high] >= key)
return mBinarySearch(array, mid+1, high, key);
else
return mBinarySearch(array, low, mid-1, key);
}
}
This is an improvement to codaddict's procedure above. Notice the additional if condition as below:
if (mid > 0 && array[mid-1] != key)
There is a simple idea to solve this problem in O(logN) complexity with binary search.
The idea is,
If the middle element is greater than the left element, then the left part is sorted. Otherwise, the right part is sorted.
Once the sorted part is determined, all you need is to check if the value falls under that sorted part or not. If not, you can divide the unsorted part and find the sorted part from that (the unsorted part) and continue binary search.
For example, consider the image below. An array can be left rotated or right rotated.
Below image shows the relation of the mid element compared with the left most one and how this relates to which part of the array is purely sorted.
If you see the image, you find that the mid element is >= the left element and in that case, the left part is purely sorted.
An array can be left rotated by number of times, like once, twice, thrice and so on. Below image shows that for each rotation, the property of if mid >= left, left part is sorted still prevails.
More explanation with images can be found in below link. (Disclaimer: I am associated with this blog)
https://foolishhungry.com/search-in-rotated-sorted-array/.
Hope this will be helpful.
Happy coding! :)
Here is a simple (time,space)efficient non-recursive O(log n) python solution that doesn't modify the original array. Chops down the rotated array in half until I only have two indices to check and returns the correct answer if one index matches.
def findInRotatedArray(array, num):
lo,hi = 0, len(array)-1
ix = None
while True:
if hi - lo <= 1:#Im down to two indices to check by now
if (array[hi] == num): ix = hi
elif (array[lo] == num): ix = lo
else: ix = None
break
mid = lo + (hi - lo)/2
print lo, mid, hi
#If top half is sorted and number is in between
if array[hi] >= array[mid] and num >= array[mid] and num <= array[hi]:
lo = mid
#If bottom half is sorted and number is in between
elif array[mid] >= array[lo] and num >= array[lo] and num <= array[mid]:
hi = mid
#If top half is rotated I know I need to keep cutting the array down
elif array[hi] <= array[mid]:
lo = mid
#If bottom half is rotated I know I need to keep cutting down
elif array[mid] <= array[lo]:
hi = mid
print "Index", ix
Try this solution
bool search(int *a, int length, int key)
{
int pivot( length / 2 ), lewy(0), prawy(length);
if (key > a[length - 1] || key < a[0]) return false;
while (lewy <= prawy){
if (key == a[pivot]) return true;
if (key > a[pivot]){
lewy = pivot;
pivot += (prawy - lewy) / 2 ? (prawy - lewy) / 2:1;}
else{
prawy = pivot;
pivot -= (prawy - lewy) / 2 ? (prawy - lewy) / 2:1;}}
return false;
}
This code in C++ should work for all cases, Although It works with duplicates, please let me know if there's bug in this code.
#include "bits/stdc++.h"
using namespace std;
int searchOnRotated(vector<int> &arr, int low, int high, int k) {
if(low > high)
return -1;
if(arr[low] <= arr[high]) {
int p = lower_bound(arr.begin()+low, arr.begin()+high, k) - arr.begin();
if(p == (low-high)+1)
return -1;
else
return p;
}
int mid = (low+high)/2;
if(arr[low] <= arr[mid]) {
if(k <= arr[mid] && k >= arr[low])
return searchOnRotated(arr, low, mid, k);
else
return searchOnRotated(arr, mid+1, high, k);
}
else {
if(k <= arr[high] && k >= arr[mid+1])
return searchOnRotated(arr, mid+1, high, k);
else
return searchOnRotated(arr, low, mid, k);
}
}
int main() {
int n, k; cin >> n >> k;
vector<int> arr(n);
for(int i=0; i<n; i++) cin >> arr[i];
int p = searchOnRotated(arr, 0, n-1, k);
cout<<p<<"\n";
return 0;
}
In Javascript
var search = function(nums, target,low,high) {
low= (low || low === 0) ? low : 0;
high= (high || high == 0) ? high : nums.length -1;
if(low > high)
return -1;
let mid = Math.ceil((low + high) / 2);
if(nums[mid] == target)
return mid;
if(nums[low] < nums[mid]) {
// if key is in the left half
if (nums[low] <= target && target <= nums[mid])
// search the left half
return search(nums,target,low,mid-1);
else
// search the right half
return search(nums,target,mid+1,high);
} else {
// if the key is in the right half.
if(nums[mid] <= target && nums[high] >= target)
return search(nums,target,mid+1,high)
else
return search(nums,target,low,mid-1)
}
};
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
import java.util.*;
class Main{
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
int n=sc.nextInt();
int arr[]=new int[n];
int max=Integer.MIN_VALUE;
int min=Integer.MAX_VALUE;
int min_index=0,max_index=n;
for(int i=0;i<n;i++){
arr[i]=sc.nextInt();
if(arr[i]>max){
max=arr[i];
max_index=i;
}
if(arr[i]<min){
min=arr[i];
min_index=i;
}
}
int element=sc.nextInt();
int index;
if(element>arr[n-1]){
index=Arrays.binarySearch(arr,0,max_index+1,element);
}
else {
index=Arrays.binarySearch(arr,min_index,n,element);
}
if(index>=0){
System.out.println(index);
}
else{
System.out.println(-1);
}
}
}
Here are my two cents:
If the array does not contain duplicates, one can find the solution in O(log(n)). As many people have shown it the case, a tweaked version of binary search can be used to find the target element.
However, if the array contains duplicates, I think there is no way to find the target element in O(log(n)). Here is an example shows why I think O(log(n)) is not possible. Consider the two arrays below:
a = [2,.....................2...........3,6,2......2]
b = [2.........3,6,2........2......................2]
All the dots are filled with the number 2. You can see that both arrays are sorted and rotated. If one wants to consider binary search, then they have to cut the search domain by half every iteration -- this is how we get O(log(n)). Let us assume we are searching for the number 3. In the frist case, we can see it hiding in the right side of the array, and on the second case it is hiding in the second side of the array. Here is what we know about the array at this stage:
left = 0
right = length - 1;
mid = left + (right - left) / 2;
arr[mid] = 2;
arr[left] = 2;
arr[right] = 2;
target = 3;
This is all the information we have. We can clearly see it is not enough to make a decision to exclude one half of the array. As a result of that, the only way is to do linear search. I am not saying we can't optimize that O(n) time, all I am saying is that we can't do O(log(n)).
There is something i don't like about binary search because of mid, mid-1 etc that's why i always use binary stride/jump search
How to use it on a rotated array?
use twice(once find shift and then use a .at() to find the shifted index -> original index)
Or compare the first element, if it is less than first element, it has to be near the end
do a backwards jump search from end, stop if any pivot tyoe leement is found
if it is > start element just do a normal jump search :)
Implemented using C#
public class Solution {
public int Search(int[] nums, int target) {
if (nums.Length == 0) return -1;
int low = 0;
int high = nums.Length - 1;
while (low <= high)
{
int mid = (low + high) / 2;
if (nums[mid] == target) return mid;
if (nums[low] <= nums[mid]) // 3 4 5 6 0 1 2
{
if (target >= nums[low] && target <= nums[mid])
high = mid;
else
low = mid + 1;
}
else // 5 6 0 1 2 3 4
{
if (target >= nums[mid] && target <= nums[high])
low= mid;
else
high = mid - 1;
}
}
return -1;
}
}
Search An Element In A Sorted And Rotated Array In Java
package yourPackageNames;
public class YourClassName {
public static void main(String[] args) {
int[] arr = {3, 4, 5, 1, 2};
// int arr[]={16,19,21,25,3,5,8,10};
int key = 1;
searchElementAnElementInRotatedAndSortedArray(arr, key);
}
public static void searchElementAnElementInRotatedAndSortedArray(int[] arr, int key) {
int mid = arr.length / 2;
int pivotIndex = 0;
int keyIndex = -1;
boolean keyIndexFound = false;
boolean pivotFound = false;
for (int rightSide = mid; rightSide < arr.length - 1; rightSide++) {
if (arr[rightSide] > arr[rightSide + 1]) {
pivotIndex = rightSide;
pivotFound = true;
System.out.println("1st For Loop - PivotFound: " + pivotFound + ". Pivot is: " + arr[pivotIndex] + ". Pivot Index is: " + pivotIndex);
break;
}
}
if (!pivotFound) {
for (int leftSide = 0; leftSide < arr.length - mid; leftSide++) {
if (arr[leftSide] > arr[leftSide + 1]) {
pivotIndex = leftSide;
pivotFound = true;
System.out.println("2nd For Loop - PivotFound: " + pivotFound + ". Pivot is: " + arr[pivotIndex] + ". Pivot Index is: " + pivotIndex);
break;
}
}
}
for (int i = 0; i <= pivotIndex; i++) {
if (arr[i] == key) {
keyIndex = i;
keyIndexFound = true;
break;
}
}
if (!keyIndexFound) {
for (int i = pivotIndex; i < arr.length; i++) {
if (arr[i] == key) {
keyIndex = i;
break;
}
}
}
System.out.println(keyIndex >= 0 ? key + " found at index: " + keyIndex : key + " was not found in the array.");
}
}
Another approach that would work with repeated values is to find the rotation and then do a regular binary search applying the rotation whenever we access the array.
test = [3, 4, 5, 1, 2]
test1 = [2, 3, 2, 2, 2]
def find_rotated(col, num):
pivot = find_pivot(col)
return bin_search(col, 0, len(col), pivot, num)
def find_pivot(col):
prev = col[-1]
for n, curr in enumerate(col):
if prev > curr:
return n
prev = curr
raise Exception("Col does not seem like rotated array")
def rotate_index(col, pivot, position):
return (pivot + position) % len(col)
def bin_search(col, low, high, pivot, num):
if low > high:
return None
mid = (low + high) / 2
rotated_mid = rotate_index(col, pivot, mid)
val = col[rotated_mid]
if (val == num):
return rotated_mid
elif (num > val):
return bin_search(col, mid + 1, high, pivot, num)
else:
return bin_search(col, low, mid - 1, pivot, num)
print(find_rotated(test, 2))
print(find_rotated(test, 4))
print(find_rotated(test1, 3))
My simple code :-
public int search(int[] nums, int target) {
int l = 0;
int r = nums.length-1;
while(l<=r){
int mid = (l+r)>>1;
if(nums[mid]==target){
return mid;
}
if(nums[mid]> nums[r]){
if(target > nums[mid] || nums[r]>= target)l = mid+1;
else r = mid-1;
}
else{
if(target <= nums[r] && target > nums[mid]) l = mid+1;
else r = mid -1;
}
}
return -1;
}
Time Complexity O(log(N)).
Question: Search in Rotated Sorted Array
public class SearchingInARotatedSortedARRAY {
public static void main(String[] args) {
int[] a = { 4, 5, 6, 0, 1, 2, 3 };
System.out.println(search1(a, 6));
}
private static int search1(int[] a, int target) {
int start = 0;
int last = a.length - 1;
while (start + 1 < last) {
int mid = start + (last - start) / 2;
if (a[mid] == target)
return mid;
// if(a[start] < a[mid]) => Then this part of the array is not rotated
if (a[start] < a[mid]) {
if (a[start] <= target && target <= a[mid]) {
last = mid;
} else {
start = mid;
}
}
// this part of the array is rotated
else {
if (a[mid] <= target && target <= a[last]) {
start = mid;
} else {
last = mid;
}
}
} // while
if (a[start] == target) {
return start;
}
if (a[last] == target) {
return last;
}
return -1;
}
}
Swift Solution 100% working tested
func searchInArray(A:[Int],key:Int)->Int{
for i in 0..<A.count{
if key == A[i] {
print(i)
return i
}
}
print(-1)
return -1
}