Find the first element in a sorted array that is greater than the target - arrays

In a general binary search, we are looking for a value which appears in the array. Sometimes, however, we need to find the first element which is either greater or less than a target.
Here is my ugly, incomplete solution:
// Assume all elements are positive, i.e., greater than zero
int bs (int[] a, int t) {
int s = 0, e = a.length;
int firstlarge = 1 << 30;
int firstlargeindex = -1;
while (s < e) {
int m = (s + e) / 2;
if (a[m] > t) {
// how can I know a[m] is the first larger than
if(a[m] < firstlarge) {
firstlarge = a[m];
firstlargeindex = m;
}
e = m - 1;
} else if (a[m] < /* something */) {
// go to the right part
// how can i know is the first less than
}
}
}
Is there a more elegant solution for this kind of problem?

One way of thinking about this problem is to think about doing a binary search over a transformed version of the array, where the array has been modified by applying the function
f(x) = 1 if x > target
0 else
Now, the goal is to find the very first place that this function takes on the value 1. We can do that using a binary search as follows:
int low = 0, high = numElems; // numElems is the size of the array i.e arr.size()
while (low != high) {
int mid = (low + high) / 2; // Or a fancy way to avoid int overflow
if (arr[mid] <= target) {
/* This index, and everything below it, must not be the first element
* greater than what we're looking for because this element is no greater
* than the element.
*/
low = mid + 1;
}
else {
/* This element is at least as large as the element, so anything after it can't
* be the first element that's at least as large.
*/
high = mid;
}
}
/* Now, low and high both point to the element in question. */
To see that this algorithm is correct, consider each comparison being made. If we find an element that's no greater than the target element, then it and everything below it can't possibly match, so there's no need to search that region. We can recursively search the right half. If we find an element that is larger than the element in question, then anything after it must also be larger, so they can't be the first element that's bigger and so we don't need to search them. The middle element is thus the last possible place it could be.
Note that on each iteration we drop off at least half the remaining elements from consideration. If the top branch executes, then the elements in the range [low, (low + high) / 2] are all discarded, causing us to lose floor((low + high) / 2) - low + 1 >= (low + high) / 2 - low = (high - low) / 2 elements.
If the bottom branch executes, then the elements in the range [(low + high) / 2 + 1, high] are all discarded. This loses us high - floor(low + high) / 2 + 1 >= high - (low + high) / 2 = (high - low) / 2 elements.
Consequently, we'll end up finding the first element greater than the target in O(lg n) iterations of this process.
Here's a trace of the algorithm running on the array 0 0 1 1 1 1.
Initially, we have
0 0 1 1 1 1
L = 0 H = 6
So we compute mid = (0 + 6) / 2 = 3, so we inspect the element at position 3, which has value 1. Since 1 > 0, we set high = mid = 3. We now have
0 0 1
L H
We compute mid = (0 + 3) / 2 = 1, so we inspect element 1. Since this has value 0 <= 0, we set mid = low + 1 = 2. We're now left with L = 2 and H = 3:
0 0 1
L H
Now, we compute mid = (2 + 3) / 2 = 2. The element at index 2 is 1, and since 1 ≥ 0, we set H = mid = 2, at which point we stop, and indeed we're looking at the first element greater than 0.

You can use std::upper_bound if the array is sorted (assuming n is the size of array a[]):
int* p = std::upper_bound( a, a + n, x );
if( p == a + n )
std::cout << "No element greater";
else
std::cout << "The first element greater is " << *p
<< " at position " << p - a;

After many years of teaching algorithms, my approach for solving binary search problems is to set the start and the end on the elements, not outside of the array. This way I can feel what's going on and everything is under control, without feeling magic about the solution.
The key point in solving binary search problems (and many other loop-based solutions) is a set of good invariants. Choosing the right invariant makes problem-solving a cake. It took me many years to grasp the invariant concept although I had learned it first in college many years ago.
Even if you want to solve binary search problems by choosing start or end outside of the array, you can still achieve it with a proper invariant. That being said, my choice is stated above to always set a start on the first element and end on the last element of the array.
So to summarize, so far we have:
int start = 0;
int end = a.length - 1;
Now the invariant. The array right now we have is [start, end]. We don't know anything yet about the elements. All of them might be greater than the target, or all might be smaller, or some smaller and some larger. So we can't make any assumptions so far about the elements. Our goal is to find the first element greater than the target. So we choose the invariants like this:
Any element to the right of the end is greater than the target. Any
element to the left of the start is smaller than or equal to the
target.
We can easily see that our invariant is correct at the start (ie before going into any loop). All the elements to the left of the start (no elements basically) are smaller than or equal to the target, same reasoning for the end.
With this invariant, when the loop finishes, the first element after the end will be the answer (remember the invariant that the right side of the end are all greater than the target?). So answer = end + 1.
Also, we need to note that when the loop finishes, the start will be one more than the end. ie start = end + 1. So equivalently we can say start is the answer as well (invariant was that anything to the left of the start is smaller than or equal to the target, so start itself is the first element larger than the target).
So everything being said, here is the code.
public static int find(int a[], int target) {
int st = 0;
int end = a.length - 1;
while(st <= end) {
int mid = (st + end) / 2; // or elegant way of st + (end - st) / 2;
if (a[mid] <= target) {
st = mid + 1;
} else { // mid > target
end = mid - 1;
}
}
return st; // or return end + 1
}
A few extra notes about this way of solving binary search problems:
This type of solution always shrinks the size of subarrays by at least 1. This is obvious in the code. The new start or end are either +1 or -1 in the mid. I like this approach better than including the mid in both or one side, and then reason later why the algo is correct. This way it's more tangible and more error-free.
The condition for the while loop is st <= end. Not st < end. That means the smallest size that enters the while loop is an array of size 1. And that totally aligns with what we expect. In other ways of solving binary search problems, sometimes the smallest size is an array of size 2 (if st < end), and honestly I find it much easier to always address all array sizes including size 1.
So hope this clarifies the solution for this problem and many other binary search problems. Treat this solution as a way to professionally understand and solve many more binary search problems without ever wobbling whether the algorithm works for edge cases or not.

How about the following recursive approach:
public static int minElementGreaterThanOrEqualToKey(int A[], int key,
int imin, int imax) {
// Return -1 if the maximum value is less than the minimum or if the key
// is great than the maximum
if (imax < imin || key > A[imax])
return -1;
// Return the first element of the array if that element is greater than
// or equal to the key.
if (key < A[imin])
return imin;
// When the minimum and maximum values become equal, we have located the element.
if (imax == imin)
return imax;
else {
// calculate midpoint to cut set in half, avoiding integer overflow
int imid = imin + ((imax - imin) / 2);
// if key is in upper subset, then recursively search in that subset
if (A[imid] < key)
return minElementGreaterThanOrEqualToKey(A, key, imid + 1, imax);
// if key is in lower subset, then recursively search in that subset
else
return minElementGreaterThanOrEqualToKey(A, key, imin, imid);
}
}

public static int search(int target, int[] arr) {
if (arr == null || arr.length == 0)
return -1;
int lower = 0, higher = arr.length - 1, last = -1;
while (lower <= higher) {
int mid = lower + (higher - lower) / 2;
if (target == arr[mid]) {
last = mid;
lower = mid + 1;
} else if (target < arr[mid]) {
higher = mid - 1;
} else {
lower = mid + 1;
}
}
return (last > -1 && last < arr.length - 1) ? last + 1 : -1;
}
If we find target == arr[mid], then any previous element would be either less than or equal to the target. Hence, the lower boundary is set as lower=mid+1. Also, last is the last index of 'target'. Finally, we return last+1 - taking care of boundary conditions.

My implementation uses condition bottom <= top which is different from the answer by templatetypedef.
int FirstElementGreaterThan(int n, const vector<int>& values) {
int B = 0, T = values.size() - 1, M = 0;
while (B <= T) { // B strictly increases, T strictly decreases
M = B + (T - B) / 2;
if (values[M] <= n) { // all values at or before M are not the target
B = M + 1;
} else {
T = M - 1;// search for other elements before M
}
}
return T + 1;
}

Hhere is a modified binary search code in JAVA with time complexity O(logn) that :
returns index of element to be searched if element is present
returns index of next greater element if searched element is not present in array
returns -1 if an element greater than the largest element of array is searched
public static int search(int arr[],int key) {
int low=0,high=arr.length,mid=-1;
boolean flag=false;
while(low<high) {
mid=(low+high)/2;
if(arr[mid]==key) {
flag=true;
break;
} else if(arr[mid]<key) {
low=mid+1;
} else {
high=mid;
}
}
if(flag) {
return mid;
}
else {
if(low>=arr.length)
return -1;
else
return low;
//high will give next smaller
}
}
public static void main(String args[]) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//int n=Integer.parseInt(br.readLine());
int arr[]={12,15,54,221,712};
int key=71;
System.out.println(search(arr,key));
br.close();
}

kind =0 : exact match
kind=1 : just grater than x
kind=-1 : just smaller than x;
It returns -1 if no match is found.
#include <iostream>
#include <algorithm>
using namespace std;
int g(int arr[], int l , int r, int x, int kind){
switch(kind){
case 0: // for exact match
if(arr[l] == x) return l;
else if(arr[r] == x) return r;
else return -1;
break;
case 1: // for just greater than x
if(arr[l]>=x) return l;
else if(arr[r]>=x) return r;
else return -1;
break;
case -1: // for just smaller than x
if(arr[r]<=x) return r;
else if(arr[l] <= x) return l;
else return -1;
break;
default:
cout <<"please give "kind" as 0, -1, 1 only" << ednl;
}
}
int f(int arr[], int n, int l, int r, int x, int kind){
if(l==r) return l;
if(l>r) return -1;
int m = l+(r-l)/2;
while(m>l){
if(arr[m] == x) return m;
if(arr[m] > x) r = m;
if(arr[m] < x) l = m;
m = l+(r-l)/2;
}
int pos = g(arr, l, r, x, kind);
return pos;
}
int main()
{
int arr[] = {1,2,3,5,8,14, 22, 44, 55};
int n = sizeof(arr)/sizeof(arr[0]);
sort(arr, arr+n);
int tcs;
cin >> tcs;
while(tcs--){
int l = 0, r = n-1, x = 88, kind = -1; // you can modify these values
cin >> x;
int pos = f(arr, n, l, r, x, kind);
// kind =0: exact match, kind=1: just grater than x, kind=-1: just smaller than x;
cout <<"position"<< pos << " Value ";
if(pos >= 0) cout << arr[pos];
cout << endl;
}
return 0;
}

Related

Longest K Sequential Increasing Subsequences

Why I created a duplicate thread
I created this thread after reading Longest increasing subsequence with K exceptions allowed. I realised that the person who was asking the question hadn't really understood the problem, because he was referring to a link which solves the "Longest Increasing sub-array with one change allowed" problem. So the answers he got were actually irrelevant to LIS problem.
Description of the problem
Suppose that an array A is given with length N.
Find the longest increasing sub-sequence with K exceptions allowed.
Example
1)
N=9 , K=1
A=[3,9,4,5,8,6,1,3,7]
Answer: 7
Explanation:
Longest increasing subsequence is : 3,4,5,8(or 6),1(exception),3,7 -> total=7
N=11 , K=2
A=[5,6,4,7,3,9,2,5,1,8,7]
answer: 8
What I have done so far...
If K=1 then only one exception is allowed. If the known algorithm for computing the Longest Increasing Subsequence in O(NlogN) is used (click here to see this algorithm), then we can compute the LIS starting from A[0] to A[N-1] for each element of array A. We save the results in a new array L with size N. Looking into example n.1 the L array would be:
L=[1,2,2,3,4,4,4,4,5].
Using the reverse logic, we compute array R, each element of which contains the current Longest Decreasing Sequence from N-1 to 0.
The LIS with one exception is just sol=max(sol,L[i]+R[i+1]),
where sol is initialized as sol=L[N-1].
So we compute LIS from 0 until an index i (exception), then stop and start a new LIS until N-1.
A=[3,9,4,5,8,6,1,3,7]
L=[1,2,2,3,4,4,4,4,5]
R=[5,4,4,3,3,3,3,2,1]
Sol = 7
-> step by step explanation:
init: sol = L[N]= 5
i=0 : sol = max(sol,1+4) = 5
i=1 : sol = max(sol,2+4) = 6
i=2 : sol = max(sol,2+3) = 6
i=3 : sol = max(sol,3+3) = 6
i=4 : sol = max(sol,4+3) = 7
i=4 : sol = max(sol,4+3) = 7
i=4 : sol = max(sol,4+2) = 7
i=5 : sol = max(sol,4+1) = 7
Complexity :
O( NlogN + NlogN + N ) = O(NlogN)
because arrays R, L need NlogN time to compute and we also need Θ(N) in order to find sol.
Code for k=1 problem
#include <stdio.h>
#include <vector>
std::vector<int> ends;
int index_search(int value, int asc) {
int l = -1;
int r = ends.size() - 1;
while (r - l > 1) {
int m = (r + l) / 2;
if (asc && ends[m] >= value)
r = m;
else if (asc && ends[m] < value)
l = m;
else if (!asc && ends[m] <= value)
r = m;
else
l = m;
}
return r;
}
int main(void) {
int n, *S, *A, *B, i, length, idx, max;
scanf("%d",&n);
S = new int[n];
L = new int[n];
R = new int[n];
for (i=0; i<n; i++) {
scanf("%d",&S[i]);
}
ends.push_back(S[0]);
length = 1;
L[0] = length;
for (i=1; i<n; i++) {
if (S[i] < ends[0]) {
ends[0] = S[i];
}
else if (S[i] > ends[length-1]) {
length++;
ends.push_back(S[i]);
}
else {
idx = index_search(S[i],1);
ends[idx] = S[i];
}
L[i] = length;
}
ends.clear();
ends.push_back(S[n-1]);
length = 1;
R[n-1] = length;
for (i=n-2; i>=0; i--) {
if (S[i] > ends[0]) {
ends[0] = S[i];
}
else if (S[i] < ends[length-1]) {
length++;
ends.push_back(S[i]);
}
else {
idx = index_search(S[i],0);
ends[idx] = S[i];
}
R[i] = length;
}
max = A[n-1];
for (i=0; i<n-1; i++) {
max = std::max(max,(L[i]+R[i+1]));
}
printf("%d\n",max);
return 0;
}
Generalization to K exceptions
I have provided an algorithm for K=1. I have no clue how to change the above algorithm to work for K exceptions. I would be glad if someone could help me.
This answer is modified from my answer to a similar question at Computer Science Stackexchange.
The LIS problem with at most k exceptions admits a O(n log² n) algorithm using Lagrangian relaxation. When k is larger than log n this improves asymptotically on the O(nk log n) DP, which we will also briefly explain.
Let DP[a][b] denote the length of the longest increasing subsequence with at most b exceptions (positions where the previous integer is larger than the next one) ending at element b a. This DP is not involved in the algorithm, but defining it makes proving the algorithm easier.
For convenience we will assume that all elements are distinct and that the last element in the array is its maximum. Note that this does not limit us, as we can just add m / 2n to the mth appearance of every number, and append infinity to the array and subtract one from the answer. Let V be the permutation for which 1 <= V[i] <= n is the value of the ith element.
To solve the problem in O(nk log n), we maintain the invariant that DP[a][b] has been calculated for b < j. Loop j from 0 to k, at the jth iteration calculating DP[a][j] for all a. To do this, loop i from 1 to n. We maintain the maximum of DP[x][j-1] over x < i and a prefix maximum data structure that at index i will have DP[x][j] at position V[x] for x < i, and 0 at every other position.
We have DP[i][j] = 1 + max(DP[i'][j], DP[x][j-1]) where we go over i', x < i, V[i'] < V[i]. The prefix maximum of DP[x][j-1] gives us the maximum of terms of the second type, and querying the prefix maximum data structure for prefix [0, V[i]] gives us the maximum of terms of the first type. Then update the prefix maximum and prefix maximum data structure.
Here is a C++ implementation of the algorithm. Note that this implementation does not assume that the last element of the array is its maximum, or that the array contains no duplicates.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// Fenwick tree for prefix maximum queries
class Fenwick {
private:
vector<int> val;
public:
Fenwick(int n) : val(n+1, 0) {}
// Sets value at position i to maximum of its current value and
void inc(int i, int v) {
for (++i; i < val.size(); i += i & -i) val[i] = max(val[i], v);
}
// Calculates prefix maximum up to index i
int get(int i) {
int res = 0;
for (++i; i > 0; i -= i & -i) res = max(res, val[i]);
return res;
}
};
// Binary searches index of v from sorted vector
int bins(const vector<int>& vec, int v) {
int low = 0;
int high = (int)vec.size() - 1;
while(low != high) {
int mid = (low + high) / 2;
if (vec[mid] < v) low = mid + 1;
else high = mid;
}
return low;
}
// Compresses the range of values to [0, m), and returns m
int compress(vector<int>& vec) {
vector<int> ord = vec;
sort(ord.begin(), ord.end());
ord.erase(unique(ord.begin(), ord.end()), ord.end());
for (int& v : vec) v = bins(ord, v);
return ord.size();
}
// Returns length of longest strictly increasing subsequence with at most k exceptions
int lisExc(int k, vector<int> vec) {
int n = vec.size();
int m = compress(vec);
vector<int> dp(n, 0);
for (int j = 0;; ++j) {
Fenwick fenw(m+1); // longest subsequence with at most j exceptions ending at this value
int max_exc = 0; // longest subsequence with at most j-1 exceptions ending before this
for (int i = 0; i < n; ++i) {
int off = 1 + max(max_exc, fenw.get(vec[i]));
max_exc = max(max_exc, dp[i]);
dp[i] = off;
fenw.inc(vec[i]+1, off);
}
if (j == k) return fenw.get(m);
}
}
int main() {
int n, k;
cin >> n >> k;
vector<int> vec(n);
for (int i = 0; i < n; ++i) cin >> vec[i];
int res = lisExc(k, vec);
cout << res << '\n';
}
Now we will return to the O(n log² n) algorithm. Select some integer 0 <= r <= n. Define DP'[a][r] = max(DP[a][b] - rb), where the maximum is taken over b, MAXB[a][r] as the maximum b such that DP'[a][r] = DP[a][b] - rb, and MINB[a][r] similarly as the minimum such b. We will show that DP[a][k] = DP'[a][r] + rk if and only if MINB[a][r] <= k <= MAXB[a][r]. Further, we will show that for any k exists an r for which this inequality holds.
Note that MINB[a][r] >= MINB[a][r'] and MAXB[a][r] >= MAXB[a][r'] if r < r', hence if we assume the two claimed results, we can do binary search for the r, trying O(log n) values. Hence we achieve complexity O(n log² n) if we can calculate DP', MINB and MAXB in O(n log n) time.
To do this, we will need a segment tree that stores tuples P[i] = (v_i, low_i, high_i), and supports the following operations:
Given a range [a, b], find the maximum value in that range (maximum v_i, a <= i <= b), and the minimum low and maximum high paired with that value in the range.
Set the value of the tuple P[i]
This is easy to implement with complexity O(log n) time per operation assuming some familiarity with segment trees. You can refer to the implementation of the algorithm below for details.
We will now show how to compute DP', MINB and MAXB in O(n log n). Fix r. Build the segment tree initially containing n+1 null values (-INF, INF, -INF). We maintain that P[V[j]] = (DP'[j], MINB[j], MAXB[j]) for j less than the current position i. Set DP'[0] = 0, MINB[0] = 0 and MAXB[0] to 0 if r > 0, otherwise to INF and P[0] = (DP'[0], MINB[0], MAXB[0]).
Loop i from 1 to n. There are two types of subsequences ending at i: those where the previous element is greater than V[i], and those where it is less than V[i]. To account for the second kind, query the segment tree in the range [0, V[i]]. Let the result be (v_1, low_1, high_1). Set off1 = (v_1 + 1, low_1, high_1). For the first kind, query the segment tree in the range [V[i], n]. Let the result be (v_2, low_2, high_2). Set off2 = (v_2 + 1 - r, low_2 + 1, high_2 + 1), where we incur the penalty of r for creating an exception.
Then we combine off1 and off2 into off. If off1.v > off2.v set off = off1, and if off2.v > off1.v set off = off2. Otherwise, set off = (off1.v, min(off1.low, off2.low), max(off1.high, off2.high)). Then set DP'[i] = off.v, MINB[i] = off.low, MAXB[i] = off.high and P[i] = off.
Since we make two segment tree queries at every i, this takes O(n log n) time in total. It is easy to prove by induction that we compute the correct values DP', MINB and MAXB.
So in short, the algorithm is:
Preprocess, modifying values so that they form a permutation, and the last value is the largest value.
Binary search for the correct r, with initial bounds 0 <= r <= n
Initialise the segment tree with null values, set DP'[0], MINB[0] and MAXB[0].
Loop from i = 1 to n, at step i
Querying ranges [0, V[i]] and [V[i], n] of the segment tree,
calculating DP'[i], MINB[i] and MAXB[i] based on those queries, and
setting the value at position V[i] in the segment tree to the tuple (DP'[i], MINB[i], MAXB[i]).
If MINB[n][r] <= k <= MAXB[n][r], return DP'[n][r] + kr - 1.
Otherwise, if MAXB[n][r] < k, the correct r is less than the current r. If MINB[n][r] > k, the correct r is greater than the current r. Update the bounds on r and return to step 1.
Here is a C++ implementation for this algorithm. It also finds the optimal subsequence.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
using ll = long long;
const int INF = 2 * (int)1e9;
pair<ll, pair<int, int>> combine(pair<ll, pair<int, int>> le, pair<ll, pair<int, int>> ri) {
if (le.first < ri.first) swap(le, ri);
if (ri.first == le.first) {
le.second.first = min(le.second.first, ri.second.first);
le.second.second = max(le.second.second, ri.second.second);
}
return le;
}
// Specialised range maximum segment tree
class SegTree {
private:
vector<pair<ll, pair<int, int>>> seg;
int h = 1;
pair<ll, pair<int, int>> recGet(int a, int b, int i, int le, int ri) const {
if (ri <= a || b <= le) return {-INF, {INF, -INF}};
else if (a <= le && ri <= b) return seg[i];
else return combine(recGet(a, b, 2*i, le, (le+ri)/2), recGet(a, b, 2*i+1, (le+ri)/2, ri));
}
public:
SegTree(int n) {
while(h < n) h *= 2;
seg.resize(2*h, {-INF, {INF, -INF}});
}
void set(int i, pair<ll, pair<int, int>> off) {
seg[i+h] = combine(seg[i+h], off);
for (i += h; i > 1; i /= 2) seg[i/2] = combine(seg[i], seg[i^1]);
}
pair<ll, pair<int, int>> get(int a, int b) const {
return recGet(a, b+1, 1, 0, h);
}
};
// Binary searches index of v from sorted vector
int bins(const vector<int>& vec, int v) {
int low = 0;
int high = (int)vec.size() - 1;
while(low != high) {
int mid = (low + high) / 2;
if (vec[mid] < v) low = mid + 1;
else high = mid;
}
return low;
}
// Finds longest strictly increasing subsequence with at most k exceptions in O(n log^2 n)
vector<int> lisExc(int k, vector<int> vec) {
// Compress values
vector<int> ord = vec;
sort(ord.begin(), ord.end());
ord.erase(unique(ord.begin(), ord.end()), ord.end());
for (auto& v : vec) v = bins(ord, v) + 1;
// Binary search lambda
int n = vec.size();
int m = ord.size() + 1;
int lambda_0 = 0;
int lambda_1 = n;
while(true) {
int lambda = (lambda_0 + lambda_1) / 2;
SegTree seg(m);
if (lambda > 0) seg.set(0, {0, {0, 0}});
else seg.set(0, {0, {0, INF}});
// Calculate DP
vector<pair<ll, pair<int, int>>> dp(n);
for (int i = 0; i < n; ++i) {
auto off0 = seg.get(0, vec[i]-1); // previous < this
off0.first += 1;
auto off1 = seg.get(vec[i], m-1); // previous >= this
off1.first += 1 - lambda;
off1.second.first += 1;
off1.second.second += 1;
dp[i] = combine(off0, off1);
seg.set(vec[i], dp[i]);
}
// Is min_b <= k <= max_b?
auto off = seg.get(0, m-1);
if (off.second.second < k) {
lambda_1 = lambda - 1;
} else if (off.second.first > k) {
lambda_0 = lambda + 1;
} else {
// Construct solution
ll r = off.first + 1;
int v = m;
int b = k;
vector<int> res;
for (int i = n-1; i >= 0; --i) {
if (vec[i] < v) {
if (r == dp[i].first + 1 && dp[i].second.first <= b && b <= dp[i].second.second) {
res.push_back(i);
r -= 1;
v = vec[i];
}
} else {
if (r == dp[i].first + 1 - lambda && dp[i].second.first <= b-1 && b-1 <= dp[i].second.second) {
res.push_back(i);
r -= 1 - lambda;
v = vec[i];
--b;
}
}
}
reverse(res.begin(), res.end());
return res;
}
}
}
int main() {
int n, k;
cin >> n >> k;
vector<int> vec(n);
for (int i = 0; i < n; ++i) cin >> vec[i];
vector<int> ans = lisExc(k, vec);
for (auto i : ans) cout << i+1 << ' ';
cout << '\n';
}
We will now prove the two claims. We wish to prove that
DP'[a][r] = DP[a][b] - rb if and only if MINB[a][r] <= b <= MAXB[a][r]
For all a, k there exists an integer r, 0 <= r <= n, such that MINB[a][r] <= k <= MAXB[a][r]
Both of these follow from the concavity of the problem. Concavity means that DP[a][k+2] - DP[a][k+1] <= DP[a][k+1] - DP[a][k] for all a, k. This is intuitive: the more exceptions we are allowed to make, the less allowing one more helps us.
Fix a and r. Set f(b) = DP[a][b] - rb, and d(b) = f(b+1) - f(b). We have d(k+1) <= d(k) from the concavity of the problem. Assume x < y and f(x) = f(y) >= f(i) for all i. Hence d(x) <= 0, thus d(i) <= 0 for i in [x, y). But f(y) = f(x) + d(x) + d(x + 1) + ... + d(y - 1), hence d(i) = 0 for i in [x, y). Hence f(y) = f(x) = f(i) for i in [x, y]. This proves the first claim.
To prove the second, set r = DP[a][k+1] - DP[a][k] and define f, d as previously. Then d(k) = 0, hence d(i) >= 0 for i < k and d(i) <= 0 for i > k, hence f(k) is maximal as desired.
Proving concavity is more difficult. For a proof, see my answer at cs.stackexchange.

Searching a key in a circular right shifted sorted array in log N time complexity [duplicate]

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
}

C: Two different binary search implementation, one stuck in infinite loop

Here are two implementation of "forgetful" binary search, since they don't check for the exact match until they're finished.
1)
int bsearch1(int A[], int n, int target)
{
int low = 0, high = n - 1, mid = 0;
while (low < high)
{
mid = (low + high) >> 1;
if (target > A[mid])
low = mid + 1;
else
high = mid;
}
if (low == high)
{
if (A[low] == target)
return low;
}
return -1;
}
2)
int bsearch2(int A[], int n, int target)
{
int low = 0, high = n - 1, mid = 0;
while (low < high)
{
mid = (low + high) >> 1;
if (target < A[mid])
high = mid - 1;
else
low = mid;
}
if (low == high)
{
if (A[low] == target)
return low;
}
return -1;
}
NOTES: n is the length of array A, target is the element to find.
bsearch1 works fine, but bsearch2 runs into infinite loop, e.g A=[1,3,5,6], target=5. The diff between them is the conditional statement in while loop, the one in bsearch2 is just the opposite of bsearch1. Both are perfectly right in logic. How can I know in advance bsearch2 is "wrong"? Can any one prove that the conditional statement in bsearch2 will lead to infinite loop (maybe in a mathematic view)? I can't find any clues and evidences until now.
EDIT:
I have evaluated the whole process of the example A=[1,3,5,6], target=5:
1.low = 0, high = 3, mid = 1, A[mid] = 3
2.low = 1, high = 3, mid = 2, A[mid] = 5
3.low = 2, high = 3, mid = 2, A[mid] = 5
...
n.low = 2, high = 3, mid = 2, A[mid] = 5
I found that bsearch2 can not reach to low == high this condition, thus can not exit the while loop. But I don't know why low and high can not reach to low == high in the end likes bsearch1.
Your second algorithm suffers from a repeating cycle as soon as you encounter a partition where high == (low+1). When that happens, you essentially have mid = (low + low + 1)/2, which is equivalent to (2*low)/2 + 1/2. With integer division, this results in mid = low + 0. Since your only movement on the low side is low = mid, but they're already equivalent, you have an infinite loop.
The reason this does not happen on the first implementation is the direction of the integer division loss. It is always down. Therefore high moving down does not suffer from this, and in-fact actually takes advantage of it.
To account for this in bsearch2 the same way bsearch1 takes advantage of the natural low-direction bias, the disgruntled rounding has to be accounted for in the mid-point calculation so it always moves in favor of the high-side. To do that, force the error out by biasing the calculation in the opposite direction. I.e. for bsearch2, do this:
mid = (low + high + 1) >> 1;
and truth be told, to avoid overflow, this really should be
mid = low + ((high - low + 1) >> 1);
This will achieve the same effect adjusting bsearch2 midpoints that bsearch1 does. An example is worth noting:
#include <stdio.h>
int bsearch2(int A[], int n, int target)
{
int low = 0, high = n - 1, mid = 0;
while (low < high)
{
mid = low + ((high - low + 1) >> 1);
if (target < A[mid])
high = mid - 1;
else
low = mid;
}
if (low == high)
{
if (A[low] == target)
return low;
}
return -1;
}
int main()
{
// build a sorted array from 1...20
int A[20];
for (int i=0; i<sizeof(A)/sizeof(*A); ++i)
A[i] = i+1;
for (int i=0; i<=sizeof(A)/sizeof(*A)+1; ++i)
printf("Search for %d found index %d\n", i, bsearch2(A, sizeof(A)/sizeof(*A), i));
return 0;
}
Output
Search for 0 found index -1
Search for 1 found index 0
Search for 2 found index 1
Search for 3 found index 2
Search for 4 found index 3
Search for 5 found index 4
Search for 6 found index 5
Search for 7 found index 6
Search for 8 found index 7
Search for 9 found index 8
Search for 10 found index 9
Search for 11 found index 10
Search for 12 found index 11
Search for 13 found index 12
Search for 14 found index 13
Search for 15 found index 14
Search for 16 found index 15
Search for 17 found index 16
Search for 18 found index 17
Search for 19 found index 18
Search for 20 found index 19
Search for 21 found index -1
I hope that made sense.
When you enter the loop second time,
low is 1, high is 3 and so mid is 2.
In the while loop, there is no 'equal' check so what is happening is that everytime target (5) is actually equal to A[mid], so you're stuck in while loop.
while entering into while add a not equal to target check
while (low<high && A[mid]!=target) {
…
}
It should work.
First of all, run the second algorithm, wait until it stuck in infinite loop. Then put a break point and see current values like mid, high and low.
I think that mid exceeds low and takes negative value. So try this code:
high = mid - 1 >= 0 ? mid - 1 : 0;
So essentially it is caused by the property of the division of int type. In your case the "shift". They are in favor of the "Low" rather than the "high". This cannot be seen in the logic.
The conclusion is that if you don't want to use complicated if(l== r-1) , ( ==) ? : ... thing, you always move the high rather than the low, to the mid. i.e., high = mid. Thanks for the question.
Also, in the question of "rotated sorted array with duplicates", in the corner where mid == A[high], we want to do --high. Actually we compare mid to A[high], not to A[L], because moving high is a better choice all the time.
https://leetcode.com/problems/search-in-rotated-sorted-array-ii/
bool search(vector<int>& nums, int target) {
//the key of binary search
//1. l and r have to always "squeeze" the target. That is how we move r and l, and how we consider to do r = m or r = m-1 (see 378. Kth Smallest Element in a Sorted Matrix )
// if(count < k) { l = midv+1;} else { r = midv;}
//2. Try to exclude half of the data
int l = 0, r = nums.size()-1;
int m, mval;
while(l<=r) {
m = l + ((r-l)>>1), mval = nums[m];
if (mval == target) return true;
if( mval < nums[r]) { //first see where the pivot is
if(mval < target && target <= nums[r]) l = m+1;
else r = m-1;
} else if ( mval > nums[r] ) {
if(nums[l] <= target && target < mval) r = m-1;
else l = m+1;
} else {//mval == nums[r], cannot decide pivot
--r; //move r while still be confident taht target is still in between
}
}
return false;
}

O(log n) algorithm to find best insert position in sorted array

I'm trying to make an algorithm that finds the best position to insert the target into the already sorted array.
The goal is to either return the position of the item if it exists in the list, else return the position it would go into to keep the list sorted.
So say I have a list:
0 1 2 3 4 5 6
---------------------------------
| 1 | 2 | 4 | 9 | 10 | 39 | 100 |
---------------------------------
And my target item is 14
It should return an index position of 5
Pseudo-code I currently have:
array = generateSomeArrayOfOrderedNumbers()
number findBestIndex(target, start, end)
mid = abs(end - start) / 2
if (mid < 2)
// Not really sure what to put here
return start + 1 // ??
if (target < array[mid])
// The target belongs on the left side of our list //
return findBestIndex(target, start, mid - 1)
else
// The target belongs on the right side of our list //
return findBestIndex(target, mid + 1, end)
I not really sure what to put at this point. I tried to take a binary search approach to this, but this is the best I could come up with after 5 rewrites or so.
There's several problems with your code:
mid = abs(end - start) / 2
This is not the middle between start and end, it's half the distance between them (rounded down to an integer). Later you use it like it was indeed a valid index:
findBestIndex(target, start, mid - 1)
Which it is not. You probably meant to use mid = (start + end) // 2 or something here.
You also miss a few indices because you skip over the mid:
return findBestIndex(target, start, mid - 1)
...
return findBestIndex(target, mid + 1, end)
Your base case must now be expressed a bit differently as well. A good candidate is the condition
if start == end
Because now you definitely know you're finished searching. Note that you also should consider the case where all the array elements are smaller than target, so you need to insert it at the end.
I don't often search binary, but if I do, this is how
Binary search is something that is surprisingly hard to get right if you've never done it before. I usually use the following pattern if I do a binary search:
lo, hi = 0, n // [lo, hi] is the search range, but hi will never be inspected.
while lo < hi:
mid = (lo + hi) // 2
if check(mid): hi = mid
else: lo = mid + 1
Under the condition that check is a monotone binary predicate (it is always false up to some point and true from that point on), after this loop, lo == hi will be the first number in the range [0..n] with check(lo) == true. check(n) is implicitely assumed to be true (that's part of the magic of this approach).
So what is a monotone predicate that is true for all indices including and after our target position and false for all positions before?
If we think about it, we want to find the first number in the array that is larger than our target, so we just plug that in and we're good to go:
lo, hi = 0, n
while lo < hi:
mid = (lo + hi) // 2
if (a[mid] > target): hi = mid
else: lo = mid + 1
return lo;
this is the code I have used:
int binarySearch( float arr[] , float x , int low , int high )
{
int mid;
while( low < high ) {
mid = ( high + low ) / 2;
if( arr[mid]== x ) {
break;
}
else if( arr[mid] > x ) {
high=mid-1;
}
else {
low= mid+1;
}
}
mid = ( high + low ) / 2;
if (x<=arr[mid])
return mid;
else
return mid+1;
}
the point is that even when low becomes equal to high you have to check.
see this example for instance:
0.5->0.75
and you are looking for true position of 0.7 or 1.
in both cases when going out of while loop: low=high=1
but one of them should be placed in position 1 and the other in position 2.
You are on the right track.
First, you do not need abs in mid = abs(end + start) / 2
Assume abs here means absolute value, because end should always be no less than start, unless there is some mistake in your code. So here abs never helps but may be potentially hiding your problem make it hard to debug.
You do not need if (mid < 2) section either , nothing special about mid smaller than two.
array = generateSomeArrayOfOrderedNumbers()
int start = 0;
int end = array.size();
int findBestIndex(target, start, end){
if (start == end){ //you already searched entire array, return the position to insert
if (stat == 0) return 0; // if it's the beginning of the array just return 0.
if(array[start] > target) return start -1; //if last searched index is bigger than target return the position before it.
else return start;
}
mid = (end - start) / 2
// find correct position
if(target == array[mid]) return mid;
if (target < array[mid])
{
// The target belongs on the left side of our list //
return findBestIndex(target, start, mid - 1)
}
else
{
// The target belongs on the right side of our list //
return findBestIndex(target, mid + 1, end)
}
}
I solved this by counting the number of elements that are strictly smaller (<) than the key to insert. The retrieved count is the insert position. Here is a ready to use implementation in Java:
int binarySearchCount(int array[], int left, int right, int key) {
if(left > right) {
return -1; // or throw exception
}
int mid = -1; //init with arbitrary value
while (left <= right) {
// Middle element
mid = (left + right) / 2;
// If the search key on the left half
if (key < array[mid]) {
right = mid - 1;
}
// If the search key on the right half
else if (key > array[mid]) {
left = mid + 1;
}
// We found the key
else {
// handle duplicates
while(mid > 0 && array[mid-1] == array[mid]) {
--mid;
}
break;
}
}
// return the number of elements that are strictly smaller (<) than the key
return key <= array[mid] ? mid : mid + 1;
}
Below is the code that is used to search a target value (which is a list of an array) from the sorted array (It contains duplicate values).
It returns the array of positions where we can insert the target values.
Hope this code helps you in any way.
Any suggestions are welcome.
static int[] climbingLeaderboard(int[] scores, int[] alice) {
int[] noDuplicateScores = IntStream.of(scores).distinct().toArray();
int[] rank = new int[alice.length];
for (int k = 0; k < alice.length; k++) {
int i=0;
int j = noDuplicateScores.length-1;
int pos=0;
int target = alice[k];
while(i<=j) {
int mid = (j+i)/2;
if(target < noDuplicateScores[mid]) {
i = mid +1;
pos = i;
}else if(target > noDuplicateScores[mid]) {
j = mid-1;
pos = j+1;
}else {
pos = mid;
break;
}
}
rank[k] = pos+1;
}
return rank;
}
Here is a solution by tweaking the binary search using python.
def func(x, y):
start = 0
end = len(x)
while start <= end:
mid = (start + end)//2
print(start, end, mid)
if mid + 1 >= len(x):
return mid + 1
if x[mid] < y and x[mid + 1] > y:
return mid + 1
elif x[mid] > y:
end = mid - 1
else:
start = mid + 1
return 0
func([1,2,4,5], 3)
Solution with slightly modified binary search in java
int findInsertionIndex(int[] arr, int t) {
int s = 0, e = arr.length - 1;
if(t < arr[s])return s;
if(t > arr[e])return e;
while (s < e){
int mid = (s + e)/2;
if(arr[mid] >= t){
e = mid - 1;
}
if(arr[mid] < t){
s = mid + 1;
}
}
return arr[s] < t? s + 1 : s;
}
The above code works upon these possible scenarios:
If arr[mid] > target -> target index lies in left half, Find the index of first max value of target and return it.
If arr[mid] < target -> target index lies in right half, Find the index of first min value of target and return the index + 1 to point the target/insertion index.
if arr[mid] == target -> Find the first occurring index of target value and return it.

Faster algorithm to find how many numbers are not divisible by a given set of numbers

I am trying to solve an online judge problem: http://opc.iarcs.org.in/index.php/problems/LEAFEAT
The problem in short:
If we are given an integer L and a set of N integers s1,s2,s3..sN, we have to find how many numbers there are from 0 to L-1 which are not divisible by any of the 'si's.
For example, if we are given, L = 20 and S = {3,2,5} then there are 6 numbers from 0 to 19 which are not divisible by 3,2 or 5.
L <= 1000000000 and N <= 20.
I used the Inclusion-Exclusion principle to solve this problem:
/*Let 'T' be the number of integers that are divisible by any of the 'si's in the
given range*/
for i in range 1 to N
for all subsets A of length i
if i is odd then:
T += 1 + (L-1)/lcm(all the elements of A)
else
T -= 1 + (L-1)/lcm(all the elements of A)
return T
Here is my code to solve this problem
#include <stdio.h>
int N;
long long int L;
int C[30];
typedef struct{int i, key;}subset_e;
subset_e A[30];
int k;
int gcd(a,b){
int t;
while(b != 0){
t = a%b;
a = b;
b = t;
}
return a;
}
long long int lcm(int a, int b){
return (a*b)/gcd(a,b);
}
long long int getlcm(int n){
if(n == 1){
return A[0].key;
}
int i;
long long int rlcm = lcm(A[0].key,A[1].key);
for(i = 2;i < n; i++){
rlcm = lcm(rlcm,A[i].key);
}
return rlcm;
}
int next_subset(int n){
if(k == n-1 && A[k].i == N-1){
if(k == 0){
return 0;
}
k--;
}
while(k < n-1 && A[k].i == A[k+1].i-1){
if(k <= 0){
return 0;
}
k--;
}
A[k].key = C[A[k].i+1];
A[k].i++;
return 1;
}
int main(){
int i,j,add;
long long int sum = 0,g,temp;
scanf("%lld%d",&L,&N);
for(i = 0;i < N; i++){
scanf("%d",&C[i]);
}
for(i = 1; i <= N; i++){
add = i%2;
for(j = 0;j < i; j++){
A[j].key = C[j];
A[j].i = j;
}
temp = getlcm(i);
g = 1 + (L-1)/temp;
if(add){
sum += g;
} else {
sum -= g;
}
k = i-1;
while(next_subset(i)){
temp = getlcm(i);
g = 1 + (L-1)/temp;
if(add){
sum += g;
} else {
sum -= g;
}
}
}
printf("%lld",L-sum);
return 0;
}
The next_subset(n) generates the next subset of size n in the array A, if there is no subset it returns 0 otherwise it returns 1. It is based on the algorithm described by the accepted answer in this stackoverflow question.
The lcm(a,b) function returns the lcm of a and b.
The get_lcm(n) function returns the lcm of all the elements in A.
It uses the property : LCM(a,b,c) = LCM(LCM(a,b),c)
When I submit the problem on the judge it gives my a 'Time Limit Exceeded'. If we solve this using brute force we get only 50% of the marks.
As there can be upto 2^20 subsets my algorithm might be slow, hence I need a better algorithm to solve this problem.
EDIT:
After editing my code and changing the function to the Euclidean algorithm, I am getting a wrong answer, but my code runs within the time limit. It gives me a correct answer to the example test but not to any other test cases; here is a link to ideone where I ran my code, the first output is correct but the second is not.
Is my approach to this problem correct? If it is then I have made a mistake in my code, and I'll find it; otherwise can anyone please explain what is wrong?
You could also try changing your lcm function to use the Euclidean algorithm.
int gcd(int a, int b) {
int t;
while (b != 0) {
t = b;
b = a % t;
a = t;
}
return a;
}
int lcm(int a, int b) {
return (a * b) / gcd(a, b);
}
At least with Python, the speed differences between the two are pretty large:
>>> %timeit lcm1(103, 2013)
100000 loops, best of 3: 9.21 us per loop
>>> %timeit lcm2(103, 2013)
1000000 loops, best of 3: 1.02 us per loop
Typically, the lowest common multiple of a subset of k of the s_i will exceed L for k much smaller than 20. So you need to stop early.
Probably, just inserting
if (temp >= L) {
break;
}
after
while(next_subset(i)){
temp = getlcm(i);
will be sufficient.
Also, shortcut if there are any 1s among the s_i, all numbers are divisible by 1.
I think the following will be faster:
unsigned gcd(unsigned a, unsigned b) {
unsigned r;
while(b) {
r = a%b;
a = b;
b = r;
}
return a;
}
unsigned recur(unsigned *arr, unsigned len, unsigned idx, unsigned cumul, unsigned bound) {
if (idx >= len || bound == 0) {
return bound;
}
unsigned i, g, s = arr[idx], result;
g = s/gcd(cumul,s);
result = bound/g;
for(i = idx+1; i < len; ++i) {
result -= recur(arr, len, i, cumul*g, bound/g);
}
return result;
}
unsigned inex(unsigned *arr, unsigned len, unsigned bound) {
unsigned i, result = bound, t;
for(i = 0; i < len; ++i) {
result -= recur(arr, len, i, 1, bound);
}
return result;
}
call it with
unsigned S[N] = {...};
inex(S, N, L-1);
You need not add the 1 for the 0 anywhere, since 0 is divisible by all numbers, compute the count of numbers 1 <= k < L which are not divisible by any s_i.
Create an array of flags with L entries. Then mark each touched leaf:
for(each size in list of sizes) {
length = 0;
while(length < L) {
array[length] = TOUCHED;
length += size;
}
}
Then find the untouched leaves:
for(length = 0; length < L; length++) {
if(array[length] != TOUCHED) { /* Untouched leaf! */ }
}
Note that there is no multiplication and no division involved; but you will need up to about 1 GiB of RAM. If RAM is a problem the you can use an array of bits (max. 120 MiB).
This is only a beginning though, as there are repeating patterns that can be copied instead of generated. The first pattern is from 0 to S1*S2, the next is from 0 to S1*S2*S3, the next is from 0 to S1*S2*S3*S4, etc.
Basically, you can set all values touched by S1 and then S2 from 0 to S1*S2; then copy the pattern from 0 to S1*S2 until you get to S1*S2*S3 and set all the S3's between S3 and S1*S2*S3; then copy that pattern until you get to S1*S2*S3*S4 and set all the S4's between S4 and S1*S2*S3*S4 and so on.
Next; if S1*S2*...Sn is smaller than L, you know the pattern will repeat and can generate the results for lengths from S1*S2*...Sn to L from the pattern. In this case the size of the array only needs to be S1*S2*...Sn and doesn't need to be L.
Finally, if S1*S2*...Sn is larger than L; then you could generate the pattern for S1*S2*...(Sn-1) and use that pattern to create the results from S1*S2*...(Sn-1) to S1*S2*...Sn. In this case if S1*S2*...(Sn-1) is smaller than L then the array doesn't need to be as large as L.
I'm afraid your problem understanding is maybe not correct.
You have L. You have a set S of K elements. You must count the sum of quotient of L / Si. For L = 20, K = 1, S = { 5 }, the answer is simply 16 (20 - 20 / 5). But K > 1, so you must consider the common multiples also.
Why loop through a list of subsets? It doesn't involve subset calculation, only division and multiple.
You have K distinct integers. Each number could be a prime number. You must consider common multiples. That's all.
EDIT
L = 20 and S = {3,2,5}
Leaves could be eaten by 3 = 6
Leaves could be eaten by 2 = 10
Leaves could be eaten by 5 = 4
Common multiples of S, less than L, not in S = 6, 10, 15
Actually eaten leaves = 20/3 + 20/2 + 20/5 - 20/6 - 20/10 - 20/15 = 6
You can keep track of the distance until then next touched leaf for each size. The distance to the next touched leaf will be whichever distance happens to be smallest, and you'd subtract this distance from all the others (and wrap whenever the distance is zero).
For example:
int sizes[4] = {2, 5, 7, 9};
int distances[4];
int currentLength = 0;
for(size = 0 to 3) {
distances[size] = sizes[size];
}
while(currentLength < L) {
smallest = INT_MAX;
for(size = 0 to 3) {
if(distances[size] < smallest) smallest = distances[size];
}
for(size = 0 to 3) {
distances[size] -= smallest;
if(distances[size] == 0) distances[size] = sizes[size];
}
while( (smallest > 1) && (currentLength < L) ) {
currentLength++;
printf("%d\n", currentLength;
smallest--;
}
}
#A.06: u r the one with username linkinmew on opc, rite?
Anyways, the answer just requires u to make all possible subsets, and then apply inclusion exclusion principle. This will fall well within the time bounds for the data given. For making all possible subsets, u can easily define a recursive function.
i don't know about programming but in math there is a single theorem which works on a set that has GCD 1
L=20, S=(3,2,5)
(1-1/p)(1-1/q)(1-1/r).....and so on
(1-1/3)(1-1/2)(1-1/5)=(2/3)(1/2)(4/5)=4/15
4/15 means there are 4 numbers in each set of 15 number which are not divisible by any number rest of it can be count manually eg.
16, 17, 18, 19, 20 (only 17 and 19 means there are only 2 numbers thatr can't be divided by any S)
4+2=6
6/20 means there are only 6 numbers in first 20 numbers that can't be divided by any s

Resources