I'm trying to write a function that can do a binary search for any given STL container (i.e lists, vectors, etc). My approach was using two iterators, one for the beginning and end of the sequences.
template <class ln, class T> int binarysearch_list(ln first, ln last, T search_value) {
int low = 0;
int high = 0;
int midpoint = 0;
int start = 0;
while(first != last) {
first++;
start++;
}
high = start;
while(low <= high) {
ln current = next(first, midpoint);
if(search_value == *current) { // if we already find search_value at the midpoint we can simply exit
return midpoint;
} else if(search_value > midpoint) {
low = midpoint + 1; // change the low to the next element after midpoint
} else if(search_value < midpoint) {
high = midpoint - 1;
}
}
return -1;
}
The first while loop is used to get the size, the second one is the actual binary search. I get an error at ln current = next(first, midpoint), I'm not sure how to tackle increment the iterator. Ideally, where you see "if(search_value == *current)" it will act like vector[midpoint] or list(midpoint). I need a way to read the nth element of any given sequence using a begin and end iterator, and I need a way to flexibly increment an iterator by n. I've tried advance(first, amount), (first+amount), and many others but I'm stumped!
Related
Here are some instructions I was given:
Create two regular c-type functions that take in an integer vector by reference, searches for a particular int target and then returns an iterator pointing to the target. Implement a linear search and a binary search.
vector<int>::iterator searchListBinary(vector<int>& arg, int target) {
//vector<int>::iterator itrIndex = arg.begin();
vector<int>::iterator first = arg.begin();
vector<int>::iterator last = arg.end();
vector<int>::iterator mid = first + (last - first) / 2;
while (first <= last){ //while there is a space to search for
if (*mid == target) { //If middle value is target
return (mid); //return the value of iterator.
}
else if (target < *mid) { //This means that the target is on the left side of the the vector,
//Middle value is larger than the targeted value.
last = mid - 1;
}
else {
first = mid + 1; //outputting 707
}
}
//if the code gets here, that means the
//target value was not in the vector
return(arg.end());
}
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;
}
I have to find the first instance of the sub string "xy" in a char array, by using divide and conquer to split my array into half (so array[0...mid] and array[mid+1...size] where mid = size+1/2) and recursively running my algorithm on both halves. The substring 'xy' could be in the left half, it could be in the right half, or it could be between the two halves. It returns the index of 'x' if the first 'xy' is found, otherwise returns a -1. My method is allowed two parameters, the (pointer to) array and the size of the array. I tried to do it by using a modified binary search, and the code is as follows:
(PS. this is pseudocode that resembles C++, doesn't have to be proper just the logic has to be good)
public int xy-search(char* data, int n){ //starts at l=0 and r == n-1
int l = 0; //left index
int r = n-1; // right index
if (n==1)
return -1;
if (l>r) // not found
return -1;
int mid = l+r/2; //get mid point
if (data[mid] == ‘x’ && data[mid+1] == ‘y’)
return mid;
else if (l==r) // not found
return -1;
else {
int left = xy-search(data, left); //check left
int right = xy-search(data+left+1, n - left - 1); // check right
if (left != -1) //if found at left, return index
return left;
if (right != -1) //if found at right, return index
return right;
else
return -1;
}
}
I need someone to check my work and tell me if I am going about it wrong. Also, I feel like there should be a condition that checks the left first and if that fails, then the right, as we are looking for the first instance of 'xy'.
i don't know why you want to use divide and concur.
any way. your data might be large and you want to use multi thread and so on....
i think you can use something like this:
int xy_Find(string str , int start , int end)
{
int min = (start + end) / 2;
if (str.substr(start,2) == "xy")
{
return start;
}
if (end - start <= 2)
{
return -1 ;
}
else
{
int leftPos = xy_Find(str , start , min + 1);
if (leftPos != -1)
{
return leftPos;
}
int rightPos = xy_Find(str , min , end);
if (rightPos != -1)
{
return rightPos;
}
}
return -1;
}
there is one thing, i divided it into tow parts. but they have one common character so if "xy" is at mid it wont work wrong.
A binary search is used when the data is sorted or if you can be sure if one half of the array doesn't contain the data you're searching, so, in your case the efficiency of your algorithm will be worse than a naive linear search.
Even then if binary search is the way you want to go, your code has some problems. For binary search you need to pass two indexes. The starting point and the ending point,so that data is divided properly.
int xysearch(char *data, int start,int end){
int l=start;
int r=end;
if(l>=r){
return -1;
}
int mid=(l+r)/2;
int left=xysearch(data,l,mid);
if(left!=-1){
return left;
}
if(mid+1<strlen(data)&&data[mid]=='x'&&data[mid+1]=='y'){
return mid;
}
int right=xysearch(data,mid+1,r);
if(right!=-1){
return right;
}
return -1;
}
edit: Now the program checks left first then the right
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
I'm 99% sure my problem is that I'm setting low to zero every start. But I'm not sure how to keep low consistently representative of the low index regardless of the depth of my recursion. If it accurately told me the index of the low index I don't think I would have a problem.
Here's my code so far:
int recBSearch(vector<int> v, int size, int item)
{
int index = size / 2;
int curr = v[index];
int low = 0;
int high = size -1;
if (v[index] == item)
return index;
else if (v[index] > item)
{
high = index;
index = (high+low)/2;
size = high - low;
return recBSearch(v, size, item);
}
else if (v[index] < item)
{
low = index;
index = (high+low)/2;
size = high - low;
return recBSearch(v, size, item);
}
return -1;
}
This won't work when you are trying to search in the upper half of the vector, because what you really need to create is a slice of the vector.
There is already a binary search but if you are determined to write your own, use an iterator-range in the parameters. (You can either pass in two plain iterators or a boost range).
You want -1 if not found else the iterator location, so in your slice (iterator range) you would need to specify a starting index number in case it is found.
You could also pass, as an alternative, the vector (by const reference) and the range in which you wish to search.
Your last line is unreachable. Instead it should be the terminating condition of your recursion before you do any evaluation. (If your range is empty)
The version that would iterate by passing by reference and using index numbers (simplest) would look like this:
int recBSearch( std::vector<int> const& vec, int start, int end, int value )
{
if( start == end )
{
return -1;
}
int index = (start + end) / 2;
// continue from here
}
end would indicate "one past the last element" so if the vector has size 5, the first iteration would pass 0 and 5. If the vector is empty, you pass 0 and 0.
As an exercise, "can it be done with 3 parameters"?
Yes...
typedef std::vector<int>::const_iterator citer;
int recBSearch( citer start, citer end, int value )
{
if( start == end )
{
return -1;
}
citer middle = start + (end-start)/2;
if( *value == *middle )
{
return middle - start;
}
else if ( *value < *middle )
{
return recBSearch( start, middle, value );
}
else // note the change here
{
int res = recBSearch( middle+1, end, value );
if( res == -1 )
return -1;
else
return res + 1 + (middle-start);
}
}
If you want to do it recursive, your method needs to take the search range as parameters. Else you can't keep track of where to search in the rucursive call assuming you always give the full vector to the function.
So your method signature should be like:
int recBSearch(vector<int> v, int first, int last, int item)
Binary search basically works by dividing your range in 2 halves and searching in each one of them. Your code shows that you operate on the lower half for your both branches. You need to pass to the recursive call the higher half of your v vector in the second else if as well as size/2 instead of size.