Implementing random partition point in partition function with pointers - c++

I'm trying to convert my partition function shown below from choosing the second last element as the partition to a random element. My original idea was to use the rand function something along the lines of
int *pivot = rand() % last;
However i've realised this wont work trying to parse an integer to a pointer integer. Is there any way i can get around this issue.
int *partition(int *first, int *last)
{
int *pivot = last - 1;
int *i = first;
int *j = last - 1;
for (;;)
{
while (comp_less(*i, *pivot) && i < last)
{
++i;
}
while (*j >= *pivot && j > first)
{
--j;
}
if (i >= j)
break;
swap(*i, *j);
}
swap(*(last - 1), *i);
return i;
}

You need to pick a distance in the range [0, std::distance(first, last)), and add that to first.
Note that rand produces low quality pseudorandom numbers, and rand() % N does not uniformly pick numbers.
int * random_pivot(int * first, int * last)
{
thread_local std::mt19937 random_engine(std::random_device{}());
std::uniform_int_distribution<std::ptrdiff_t> range(0, std::distance(first, last) - 1);
return first + range(random_engine);
}
If you are using random choices elsewhere, it would be better to pass random_engine in by reference, and only initialise one (per-thread) std::mt19937.

Related

Finding largest value in array using recursion

I've been studying to ' Data Abstraction and Problem Solving with C++ ' book at recently, however i did stuck at some point.
I'm at recursion chapter, and i faced with a problem which is ' Finding largest value in an array'. As you know i have to solve this problem with recursion perspective and actually i already solved that with this algorithm ;
Which is basically, start from first item to last item of an array and algorithm is comparing every value with each other and in the and largest item of array stands alone in array(which is invoking the base case)
int largestValue(int anArray[], int first , int last){
int value;
// base case
if(first == last){
value = anArray[first];
}
else if(anArray[first]>anArray[first+1]){
anArray[first + 1] = anArray[first];
value = largestValue(anArray, first + 1, last);
}else{ // anArray[first] < anArray[first + 1]
value = largestValue(anArray, first + 1, last);
}
return value;
BUT , after i read the description of problem, it is said ' you have to solve this problem with multipath recursion.'
For better understanding i'm putting screenshot of problem:
And i couldn't figured out algorithm with ' Multipath Recursion ' perspective.
Split the array in the middle, then call the function for each half:
template<class Random_it>
// typename std::iterator_traits<Random_it>::value_type
auto recursive_max(Random_it first, Random_it last) {
assert(first != last);
if (last - first == 1)
return *first;
const auto mid = first + (last - first) / 2;
const auto l_max = recursive_max(first, mid);
const auto r_max = recursive_max(mid, last);
return (r_max > l_max) ? r_max : l_max;
}
Usage example:
std::vector<int> vec{/* init values */};
const auto max = recursive_max(vec.begin(), vec.end());
Demo
Note that here first and last represent a half-open interval [first, last), in agreement with a convention that is widely used in the C++ standard library.
I would recommend you use a helper function that calls your largestvalue function like so:
int largestValue(int arr[], int size){
int middle = (size - 1)/2;
int first_max = largestValue(arr, 0, middle);
int second_max = largestValue(arr, middle + 1, largest - 1);
if(first_max < second_max){
return second_max;
}
return first_max;
}
The recursive algorithm just splits the array in two and finds the largest value recursively, then compares the two and returns the highest.
What you need to do is to use the boundaries first and last which tell you which part of the array to calculate the largest value of.
A simple solution would be the following:
int largestValue(int anArray[], int first, int last) {
if (first == last) {
return anArray[first];
}
int middle = (first+last)/2;
int left = largestValue(anArray, first, middle);
int right = largestValue(anArray, middle+1, last);
int max;
if (left > right) {
max = left;
} else {
max = right;
}
return max;
}

QUICK SORT stack overflow c++ big numbers

good day
I am trying to use quick sort with 10000 numbers but it is giving me stack overflow error. it works with random numbers but it does not with descending and ascending numbers.
'
thank you
void quickSort(long* array, long start, long last)
{
if (start < last)
{
int p = partition(array, start, last);
quickSort(array, start, p-1);
quickSort(array, p + 1, last);
}
}
int partition(long* array, long start, long last)//first partition
{
int j = start + 1;
for (long i = start + 1;i <= last;i++)
{
if (array[i] < array[start])
{
swap(array[i], array[j]);
j++;
}
}
swap(array[start], array[j - 1]);
return j - 1;
}
'
For sorted elements, you can avoid this problem by choosing the median of the three elements array[start], array[last] and array[(start + last + 1)/2] as your pivot value.
int median_of_3(long* array, long start, long last)
{
long a = (start + last + 1)/2, b = start, c = last;
if (array[c] < array[a]) swap(array[c], array[a]);
if (array[b] < array[a]) swap(array[b], array[a]);
if (array[c] < array[b]) swap(array[c], array[b]);
return partition(array, start, last);
}
An additional strategy to avoid a large stack depth is to calculate which partition is smaller, and recursively call the smaller one. The other partition can then be optimized into a loop (tail recursion optimization).
void quickSort(long* array, long start, long last)
{
if (start >= last) return;
int p = median_of_3(array, start, last);
int next_start[2] = { start, p + 1 };
int next_last[2] = { p - 1, last };
bool i = p > (start + last)/2;
quickSort(array, next_start[i], next_last[i]);
/*
* If the compiler does not optimize the tail call below into
* a loop, it is easy to do the optimization manually.
*/
quickSort(array, next_start[!i], next_last[!i]);
}
Introspection can also be used to avoid a large stack depth. You track your recursive call depth, and if it is "too deep", you fail safe into a different sorting strategy, like merge sort or heap sort. This is the behavior currently used by std::sort.
void introSortImpl(long* array, long start, long last, int depth)
{
if (--depth == 0) {
heapSort(array, start, last);
return;
}
if (start >= last) return;
int p = median_of_3(array, start, last);
int next_start[2] = { start, p + 1 };
int next_last[2] = { p - 1, last };
bool i = p > (start + last)/2;
introSortImpl(array, next_start[i], next_last[i], depth);
introSortImpl(array, next_start[!i], next_last[!i], depth);
}
void introspectionSort(long* array, long start, long last)
{
introSortImpl(array, start, last, log2(start - last) * 3);
}
the code is okay but your compiler uses stack very ineffectively. you just need to raise reserved stack amount. it happens much more often in debug profiles rather than release ones just because compiler preserves large stack chunks to check if stack was broken during execution of your procedure.
Example of Lomuto partition scheme like quicksort that uses recursion on the smaller partition, updates l or r, then loops back to split the larger partition into two partitions, repeating the process. Worst case stack space is O(log2(n)) which should avoid stack overflow. Worst case time complexity is still O(n^2) (depending on how partition is implemented).
Some call this example a half recursion. It's not an example of tail recursion, since tail recursion means that the recursive function just returns after calling itself. The second call in the original question example is a tail call.
void quicksort(int * tab, int l, int r)
{
int q;
while(l < r)
{
q = partition(tab, l, r);
if(q - l < r - q) { // recurse into the smaller partition
quicksort(tab, l, q - 1);
l = q + 1;
} else {
quicksort(tab, q + 1, r);
r = q - 1;
}
} // loop on the larger partition
}

Binary search for finding the lowest and largest element in a sorted array than a given value?

So, I was trying to implement the binary search algorithm (as generic as possible which can adapt to different cases). I searched for this on the internet, and some use, while (low != high) and some use, while (low <= high) and some other different condition which is very confusing.
Hence, I started writing the code for finding the first element which is greater than a given element. I wish to know if there is a more elegant solution than this?
Main code:
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <utility>
#include <algorithm>
#include <stack>
#include <queue>
#include <climits>
#include <set>
#include <cstring>
using namespace std;
int arr1[2000];
int n;
int main (void)
{
int val1,val2;
cin>>n;
for (int i = 0; i < n; i++)
cin>>arr1[i];
sort(arr1,arr1+n);
cout<<"Enter the value for which next greater element than this value is to be found";
cin>>val1;
cout<<"Enter the value for which the first element smaller than this value is to be found";
cin>>val2;
int ans1 = binarysearch1(val1);
int ans2 = binarysearch2(val2);
cout<<ans1<<"\n"<<ans2<<"\n";
return 0;
}
int binarysearch1(int val)
{
while (start <= end)
{
int mid = start + (end-start)/2;
if (arr[mid] <= val && arr[mid+1] > val)
return mid+1;
else if (arr[mid] > val)
end = mid-1;
else
start = mid+1;
}
}
Similarly, for finding the first element which is smaller than the given element,
int binarysearch2(int val)
{
while (start <= end)
{
int mid = start + (end-start)/2;
if (arr[mid] >= val && arr[mid] < val)
return mid+1;
else if (arr[mid] > val)
end = mid-1;
else
start = mid+1;
}
}
I often get super confused when I have to modify binary search for such abstraction. Please let me know if there is simpler method for the same? Thanks!
As you say, there are different ways to express the end condition for binary search and it completely depends on what your two limits mean. Let me explain mine, which I think it's quite simple to understand and it lets you modify it for other cases without thinking too much.
Let me call the two limits first and last. We want to find the first element greater than a certain x. The following invariant will hold all the time:
Every element past last is greater than x and every element before
first is smaller or equal (the opposite case).
Notice that the invariant doesn't say anything about the interval [first, last]. The only valid initialization of the limits without further knowledge of the vector is first = 0 and last = last position of the vector. This satisfies the condition as there's nothing after last and nothing before first, so everything is right.
As the interval [first, last] is unknown, we will have to proceed until it's empty, updating the limits in consequence.
int get_first_greater(const std::vector<int>& v, int x)
{
int first = 0, last = int(v.size()) - 1;
while (first <= last)
{
int mid = (first + last) / 2;
if (v[mid] > x)
last = mid - 1;
else
first = mid + 1;
}
return last + 1 == v.size() ? -1 : last + 1;
}
As you can see, we only need two cases, so the code is very simple. At every check, we update the limits to always keep our invariant true.
When the loop ends, using the invariant we know that last + 1 is greater than x if it exists, so we only have to check if we're still inside our vector or not.
With this in mind, you can modify the binary search as you want. Let's change it to find the last smaller than x. We change the invariant:
Every element before first is smaller than x and every element
after last is greater or equal than x.
With that, modifying the code is really easy:
int get_last_smaller(const std::vector<int>& v, int x)
{
int first = 0, last = int(v.size()) - 1;
while (first <= last)
{
int mid = (first + last) / 2;
if (v[mid] >= x)
last = mid - 1;
else
first = mid + 1;
}
return first - 1 < 0 ? -1 : first - 1;
}
Check that we only changed the operator (>= instead of >) and the return, using the same argument than before.
It is hard to write correct programs. And once a program has been verified to be correct, it should have to be modified rarely and reused more. In that line, given that you are using C++ and not C I would advise you to use the std C++ libraries to the fullest extent possible. Both features that you are looking for is given to you within algorithm.
http://en.cppreference.com/w/cpp/algorithm/lower_bound
http://en.cppreference.com/w/cpp/algorithm/upper_bound
does the magic for you, and given the awesome power of templates you should be able to use these methods by just adding other methods that would implement the ordering.
HTH.
To answer the question in part, it would be possible to factor out the actual comparison (using a callback function or similar), depending on whether the first element which is larger than the element is to be searched or the first element which is smaller. However, in the first code block, you use
arr[mid] <= val && arr[mid+1] > val
while in the second block, the index shift in the second condition
if (arr[mid] >= val && arr[mid] < val)
is omitted, which seems to be inconsistent.
Your search routines had some bugs [one was outright broken]. I've cleaned them up a bit, but I started from your code. Note: no guarantees--it's late here, but this should give you a starting point. Note the "lo/hi" is standard nomenclature (e.g. lo is your start and hi is your end). Also, note that hi/lo get set to mid and not mid+1 or mid-1
There are edge cases to contend with. The while loop has to be "<" or "mid+1" will run past the end of the array.
int
binarysearch_larger(const int *arr,int cnt,int val)
// arr -- array to search
// cnt -- number of elements in array
// val -- desired value to be searched for
{
int mid;
int lo;
int hi;
int match;
lo = 0;
hi = cnt - 1;
match = -1;
while (lo < hi) {
mid = (hi + lo) / 2;
if (arr[mid] <= val) && (arr[mid+1] > val)) {
if ((mid + 1) < cnt)
match = mid + 1;
break;
}
if (arr[mid] > val)
hi = mid;
else
lo = mid;
}
return match;
}
int
binarysearch_smaller(const int *arr,int cnt,int val)
// arr -- array to search
// cnt -- number of elements in array
// val -- desired value to be searched for
{
int mid;
int lo;
int hi;
int match;
lo = 0;
hi = cnt - 1;
match = -1;
while (lo < hi) {
mid = (hi + lo) / 2;
if (arr[mid] <= val) && (arr[mid+1] > val)) {
match = mid;
break;
}
if (arr[mid] > val)
hi = mid;
else
lo = mid;
}
// the condition here could be "<=" or "<" as you prefer
if ((match < 0) && (arr[cnt - 1] <= val))
match = cnt - 1;
return match;
}
Below is a generic algorithm that given a sorted range of elements and a value, it returns a pair of iterators, where the value of the first iterator is the first element in the sorted range that compares smaller than the entered value, and the value of the second iterator is the first element in that range that compares greater than the entered value.
If the pair of the returned iterators points to the end of the range it means that entered range was empty.
I've made it as generic as I could and it also handles marginal cases and duplicates.
template<typename BidirectionalIterator>
std::pair<BidirectionalIterator, BidirectionalIterator>
lowhigh(BidirectionalIterator first, BidirectionalIterator last,
typename std::iterator_traits<BidirectionalIterator>::value_type const &val) {
if(first != last) {
auto low = std::lower_bound(first, last, val);
if(low == last) {
--last;
return std::make_pair(last, last);
} else if(low == first) {
if(first != last - 1) {
return std::make_pair(first, std::upper_bound(low, last - 1, val) + 1);
} else {
return std::make_pair(first, first);
}
} else {
auto up = std::upper_bound(low, last, val);
return (up == last)? std::make_pair(low - 1, up - 1) : std::make_pair(low - 1, up);
}
}
return std::make_pair(last, last);
}
LIVE DEMO

Meximum Subarray Sum using Divide-And-Conquer

Working on an implementation of finding the Greatest Contiguous Sum of a sequence using the Divide and Conquer method as seen here.
My return value is often incorrect.
For example:
{5, 3} returns 5 instead of 8.
{-5, 3} returns 0 instead of 3.
{ 6, -5, 7 } returns 7 instead of 8.
Other notes:
decrementing or incrementing AT the first or last iterators throws an exception, saying that I either can't increment, decrement, or dereference at that point. There's a bug somewhere in GCSMid, I think, but I haven't been able to solve it.
this implementation uses random-access iterators, signified as RAIter
//function max- finds greatest number given 3 size_ts
size_t max(size_t a, size_t b, size_t c)
{
if (a >= b && a >= c)
{
return a;
}
else if (b >= a && b >= c)
{
return b;
}
else
{
return c;
}
}
//function gcsMid
//main algorithm to find subsequence if it spans across the center line
template<typename RAIter>
size_t gcsMid(RAIter first, RAIter center, RAIter last)
{
size_t sum = 0;
size_t leftSum = 0;
size_t rightSum = 0;
//to the left of center
for (RAIter i = center; i > first; i--)
{
sum += *i;
if(sum > leftSum)
{
leftSum = sum;
}
}
//to right of center
sum = 0;
for (RAIter j = (center + 1); j < last; j++)
{
sum += *j;
if (sum > rightSum)
{
rightSum = sum;
}
}
//return the sums from mid
return leftSum + rightSum;
}
//main function to call
template<typename RAIter>
int gcs(RAIter first, RAIter last)
{
size_t size = distance(first, last);
//base case is when the subarray only has 1 element. when first == last
if (first == last || size == 1)
{
if (size < 1)
{
return 0;
}
if (*first < 0)
{
return 0;
}
return *first;
}
//middle point
RAIter center = first + (size/2);
//return max of leftsum, rightsum, and midsum
return max(gcs(first, center),
gcs(center + 1, last),
gcsMid(first, center, last));
}
You have two problems with your code:
A. This loop:
for (RAIter i = center; i > first; i--)
does not include first in the loop. The reference algorithm does. You can't just use >= as the reference algorithm does as it doesn't work for iterators. Either add an extra bit of code to check first at the end, or change your loop so it somehow includes first (maybe a do while loop would suit better).
B. These definitions:
size_t sum = 0;
size_t leftSum = 0;
size_t rightSum = 0;
should not be size_t as size_t is unsigned. This means that when the sum goes negative, checks like if(sum > leftSum) no longer work, as the negative value (which underflows) is bigger than the positive value.
Change them to int.
The best way to find these kinds of errors is to run the code through a debugger. You can then step through each line of your code and see what the variable values are. This makes it easy to spot things like negative numbers becoming large positive numbers as above.

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
}