Binary search on strings instead of numbers - c++

I am reading up on binary search on an array of numbers and I believe I know how it works and how to implement it. Now I need to know how to do binary search on an array of strings ? I know binary search requires the array to be sorted already. suppose I have an array of strings already sorted how do I implement binary search on it ? I know if it was an array of numbers I would go to the middle index of the array and determin if the required search no is on the left or the right and do that recursively. How would i do that for strings?

As long as the concept of "it is equal to" and "it is less than" are defined for the type you are working on, you can implement the algorithm. It does not matter if the values are numbers, letters, or custom objects. The following example demonstrates this concept:
template<typename Iterator>
Iterator search(Iterator initial, Iterator final, const typename Iterator::value_type& value) {
if(value < *initial) {
// bail out immediately
return final;
}
while(initial != final) {
auto mid = std::next(initial, std::distance(initial, final) / 2);
if(*mid == value) {
return mid;
} else if(*mid < value) {
initial = std::next(mid);
} else {
final = std::prev(mid);
}
}
return final;
}
As long as the operations *mid == value and *mid < value are defined, I can search in a container of any type (another requirement is that I must be able to randomly access my iterator).
This is by no means a full answer, and there are many more details involved, but hopefully you get the idea.
Full sample program:
#include <vector>
#include <list>
#include <iostream>
template<typename Iterator>
Iterator search(Iterator initial, Iterator final, const typename Iterator::value_type& value) {
if(value < *initial) {
// bail out immediately
return final;
}
while(initial != final) {
auto mid = std::next(initial, std::distance(initial, final) / 2);
if(*mid == value) {
return mid;
} else if(*mid < value) {
initial = std::next(mid);
} else {
final = std::prev(mid);
}
}
return final;
}
int main() {
{
std::vector<int> v {1, 2, 3, 4, 5};
auto it = search(v.begin(), v.end(), 3);
if(it == v.end()) {
std::cout << "Not Found!" << std::endl;
} else {
std::cout << "Found in position: " << std::distance(v.begin(), it)
<< " (value is : " << *it << ")" << std::endl;
}
}
{
std::vector<char> v {'a', 'b', 'c', 'd', 'e'};
auto it = search(v.begin(), v.end(), 'd');
if(it == v.end()) {
std::cout << "Not Found!" << std::endl;
} else {
std::cout << "Found in position: " << std::distance(v.begin(), it)
<< " (value is : " << *it << ")" << std::endl;
}
}
{
std::list<float> v {-1, 0, 1, 2, 3, 4};
auto it = search(v.begin(), v.end(), 0);
if(it == v.end()) {
std::cout << "Not Found!" << std::endl;
} else {
std::cout << "Found in position: " << std::distance(v.begin(), it)
<< " (value is : " << *it << ")" << std::endl;
}
}
{
std::vector<char> v {'a', 'b', 'c', 'd', 'e'};
auto it = search(v.begin(), v.end(), 'f');
if(it == v.end()) {
std::cout << "Not Found!" << std::endl;
} else {
std::cout << "Found in position: " << std::distance(v.begin(), it)
<< " (value is : " << *it << ")" << std::endl;
}
}
}
Sample Run:
Found in position: 2 (value is : 3)
Found in position: 3 (value is : d)
Found in position: 1 (value is : 0)
Not Found!

A naive approach would be to assign a unique value to each letter (if you're doing English, that's easy; only 26 values), and compare the values of the first letter of each string. If the letters are the same, then you compare the second letter, and so on.

If you use array of std::string, it just the same since you have all compare operators.
so you just need to replace the type of the array and you can do the search as is for numbers.

Exact same way. If you use std::string, it already has operator== and operator < which are all you need for binary search. If you just have character pointers, you can use strcmp, where a negative value is less than and 0 is equal.

Related

How do I loop through a unordered_map in C++ without auto?

I am trying to loop through an unordered_map, to find if any of its VALUES is greater than 2. But this syntax is wrong
unordered_map<int, int> mp;
for (int i = 0; i < N; i++)
{
mp[arr[i]]++;
}
for (int i = 0; i < mp.size(); i++ ) {
cout << mp[i].second << " " << endl; //mp[i].second is wrong syntax
if (mp[i].second > 2) {
cout << "WRONG";
x = false;
break;
}
}
how can I do this?
It seems that you assumed(incorrectly) that mp[i] is std::pair<int, int> when in fact mp[i] gives you the mapped value which is of type int in your example. This can be seen from std::map::operator[]:
T& operator[]( Key&& key ); (2)
Returns a reference to the value that is mapped to a key equivalent to key, performing an insertion if such key does not already exist.
(emphasis mine)
Method 1
Since you don't want to use auto, you can explicitly write the type std::pair<int, int> in the range-based for loop as shown below:
//---------vvvvvvvvvvvvvvvvvvvvvvvv--------------------->not using auto here as you want
for (const std::pair<const int, int> &pair_elem: mp) {
cout << pair_elem.second << " " << endl;
if (pair_elem.second > 2) {
cout << "WRONG";
//other code here
}
}
Method 2
Note that you can also use std::find_if instead of a for loop as shown below:
auto iter = std::find_if(mp.begin(), mp.end(), [](const std::pair<int,int>& elem)
{ return elem.second > 2;});
if (iter != mp.end())
std::cout << "Greater found ";
else
{
std::cout<<"Greater not found"<<std::endl;
}
Demo
your question is missing a lot of information and literarily , if you searched on web , you would find many ways to loop over unordered_map in C++ , but anyways , here is the edited version of your code with a test case:
#include <iostream>
#include <unordered_map>
using namespace std;
int main() {
const int N = 10;
int arr[N] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
bool x = true;
unordered_map<int, int> mp;
unordered_map<int, int>:: iterator it;
for (int i = 0; i < N; i++)
{
mp[arr[i]]++;
}
mp[9] = 5;
for (it = mp.begin(); it != mp.end(); it++) {
cout << "key is " << it->first << " ,value is " << it->second << " " << endl;
if (it -> second > 2) {
cout << "WRONG";
x = false;
cout << "key is " << it->first << " ,value is " << it->second << " is greater than 2" << endl;
break;
}
}
return 0;
}

vector how find first and last current value

Task: find first and last current values.
Example:
vector = {1,2,3,1,6,2,1};
need value 1 =>
first index = 0, last = 6; (index/position);
vector<int>::iterator it = find(v.begin(), v.end(), 1);
if (it != v.end())
{
cout << "Element Found" << std::endl;
// Get index of element from iterator
int index = distance(v.begin(), it);
int lastindex = distance(v.end(), it); // bad try to find
cout <<"Index of first element in vector : "<<index<<" last elem ";
// bad code <<lastindex - index <<endl;
}
else
{
cout << "Element Not Found" << std::endl;
}
I found first position but, I can't find last position.
Need help)
You can use reverse iterator to find the last element as follows:
DEMO
int main()
{
std::vector<int> v = {1,2,3,1,6,2,1};
const int currentVal = 1;
const auto itf = std::find(v.cbegin(), v.cend(), currentVal);
const auto itb = std::find(v.crbegin(), v.crend(), currentVal);
if(itf != v.cend()){
std::cout << "front pos: " << std::distance(v.cbegin(), itf) << std::endl;
std::cout << "back pos : " << (v.size() - std::distance(v.crbegin(), itb) - 1) << std::endl;
}
else{
std::cout << currentVal << " is not found." << std::endl;
}
return 0;
}
You can use "rbegin" and "rend" to reverse the list and find the last occurrence by using the same code you have now.
vector = {1,2,3,1,6,2,1};
need value 1 =>
first index = 0, last = 6; (index/position);
vector<int>::iterator it = find(v.begin(), v.end(), 1);
vector<int>::iterator it_reverse = find(v.rbegin(), v.rend(),1);
if (it != v.end() && it_reverse != v.rend())
{
cout << "Element Found" << std::endl;
// Get index of element from iterator
int index = distance(v.begin(), it);
int lastindex = distance(v.rend(), it_reverse); // bad try to find
cout <<"Index of first element in vector : "<<index<<" last elem ";
}
else
{
cout << "Element Not Found" << std::endl;
}

Trying to read multiple modes into a vector and then print them out c++

Basically I am trying to find the mode in a vector for example if the vector is 1 1 1, it just returns no mode. also it needs to be able to read up to two possible modes prof said not to worry about more than 2 modes. Any suggestions would be very helpful.
Also the void calcMode is part of it it won't let me add it to the code i don't know how to change it.
void calcMode(vector <double> const& vec)
{
int counter = 1;
int max = 0;
vector<double> mode;
for (int pass = 0; pass < vec.size() - 1; pass++)
{
if (vec[pass] == vec[pass + 1])
{
counter++;
// If they are the same number add it to the mode vector
if (counter > max)
{
mode.clear();
max = counter;
mode.push_back(vec[pass]);
}
// if it is greater clear the vector and assign it the new value
else if (counter == max)
{
mode.push_back(vec[pass]);
}
}
else
counter = 1; // reset counter.
}
// print out the freq and the mode(s)
cout << mode.size() << endl;
cout << "Mode: " << setw(25) << setprecision(3);
cout << setw(25) << setprecision(3);
if (vec.size() == 1)
{
cout << "Freq = " << counter << endl;
cout << vec[0] << endl;
}
else if (vec.size() == 2)
{
cout << "Freq = " << counter << endl;
cout << vec[0] << vec[1] << endl;
}
else
cout << "No mode" << endl;
}
Using standard library where possible and where it makes sense.
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
// comparator function. When you compare two std::pair, you get both sides
// in the compare. We only want the value side, so we define a function to
// only test the value side.
static bool comp(std::pair<unsigned,unsigned> lhs,
std::pair<unsigned,unsigned> rhs)
{
return (lhs.second < rhs.second);
}
int main()
{
std::vector <double> const& vec {0, 1, 1, 2, 2, 4, 3, 2, 4, 0, 1, 3};
std::map<double, int> freq;
// build frequency count
for (double val:vec)
{
freq[val]++;
}
// or
//std::for_each(vec.begin(), vec.end(), [&freq](double val){ freq[val]++; });
// not sure if using for_each makes sense here. I don't think we get much
// find highest frequency
std::map<double, int>::iterator found = std::max_element(freq.begin(),
freq.end(),
comp);
// cache what we found so we have it later
std::pair<double, int> mode = *found;
std::cout << found->first << " freq: " << found->second << '\n';
// remove so we don't find again
freq.erase(found);
// look for next highest frequency
found = std::max_element(freq.begin(), freq.end(), comp);
// test for same frequency
if (mode.second == found->second)
{
std::cout << found->first << " freq: " << found->second << '\n';
}
}
std::max_element documentation
std::for_each documentation

Print adjacency list by using multimap c++

I constructed an adjacency list based on the following link: Adjacency list
struct Node
{
string name;
int id;
};
typedef std::multimap<Node,Node> Graph;
Graph g;
g.insert (Graph::value_type(node1, node3));
g.insert (Graph::value_type(node1, node4));
g.insert (Graph::value_type(node1, node5));
g.insert (Graph::value_type(node2, node6));
g.insert (Graph::value_type(node3, node6));
How can I print the multimap by following the structure of the following image (Adjacency list)?
Graph::const_iterator it = g.begin();
while (it != g.end())
{
std::pair<Graph::const_iterator, Graph::const_iterator> range
= g.equal_range(it->first);
std::cout << it->first << ": "; // print vertex
for (; range.first != range.second; ++range.first)
{
std::cout << range.first->second << ", "; // print adjacent vertices
}
std::cout << std::endl;
it = range.second;
}
Output:
1: 3, 4, 5,
2: 6,
3: 6,
4: 7,
5: 7, 8, 9,
9: 5,
DEMO
If you don't want that redundant equal_range call, you can operate with a single iterator as long as two adjacent elements are equal in terms of ordering:
Graph::key_compare cmp = g.key_comp();
Graph::const_iterator it = g.begin(), itEnd = g.end(), prev;
while (it != itEnd)
{
std::cout << it->first << ": "; // print vertex
do
{
std::cout << it->second << ", "; // print adjacent vertices
prev = it++;
}
while (it != itEnd && !cmp(prev->first, it->first));
std::cout << std::endl;
}
DEMO 2
The following alternative solution makes use of C++11's range-based for loop. It iterates through all entries regardless the source node, and compares the current source node with the previous one. If they're different, start a new line in the output.
Compared to the solution using equal_range, this alternative is a little bit more cache-friendly: it only iterates over the whole graph once in the order of the nodes. equal_range first searches for the end of the range with the same source node, and then the loop iterates again over these elements. My alternative solution avoids this. (Of course, I did no benchmark and this is not to be meant as the ultimate fastest solution, but I just wanted to provide an alternative.)
std::ostream& operator<<(std::ostream& os, const Graph& g)
{
auto prev = g.begin()->first;
os << prev << ": ";
for (auto e : g) {
if (e.first < prev || prev < e.first)
os << std::endl << (prev = e.first) << ": ";
os << e.second << ", ";
}
return os << std::endl;
}
Live demo
If you implement operator!= for your Node type, the if-line can be simplified to a more readable comparison:
std::ostream& operator<<(std::ostream& os, const Graph& g)
{
auto prev = g.begin()->first;
os << prev << ": ";
for (auto e : g) {
if (e.first != prev) // <--- now more readable
os << std::endl << (prev = e.first) << ": ";
os << e.second << ", ";
}
return os << std::endl;
}

How to detect first or last element iterating over a container?

How to do the following in more stylish/short way?
for(i=container.begin(); i!=container.end(); ++i) {
if (i!=container.begin()) {
cout << ", ";
}
cout << *i;
j=i;
if (++j==container.end()) {
cout << "!" << endl;
}
}
Solutions like foreach are acceptable (actions on first and last elements need to be configurable, though).
P.S.
There are many answers that are handling first element, but not last. Here is what I mean by handling last element:
for(i=container.begin(); i!=container.end(); ++i) {
j=i;
if (i==container.begin()) {
cout << "[" << *i << "]" << endl;
} else if (++j==container.end()) {
cout << ", (" << *i << ")" << "!" << endl;
} else {
cout << ", " << *i;
}
}
Don't you think it's very easy to handle first element outside the cycle body? The real problem is the last one! I'm sorry for not being able to clarify the important point asking the question. I think I'll just accept the top ranked answer eventually.
Boost has next / prior which can sometimes help in such situations.
for(i=container.begin(); i!=container.end(); ++i) {
if (boost::next(i) == container.end()) {
std::cout << "!" << std::endl;
}
}
Although for this specific case, I'd simply output the first element, loop from second till last while always outputting the ',' and then output the '!' after the loop has ended. (as others have suggested already)
I don't see the point in moving the special cases inside the loop, and then checking inside the loop for them....
My advice here would be: there is no point in detecting anything within this loop !
Since your special cases are at the beginning and the end of your container, it is easy to remove their processing from within the loop.
The following function will print the contents of any container class whose elements can be <<'ed to an std::ostream:
template < class Container >
void print(Container const & container)
{
typename Container::const_iterator current = container.begin();
typename Container::const_iterator const end = container.end();
if (current != end)
{
std::cout << *current;
for (++current; current != end; ++current)
{
std::cout << ", " << *current;
}
std::cout << "!" << std::endl;
}
}
In your code,
if (i==container.end()) {
cout << "!" << endl;
}
will never happen.
My own approach would be to use the container size (I think size() is now constant time for all Standard Library containers). Maintain a count in the loop and you are at the end when count == size() - 1, and at the beginning when count == 0, obviously.
As container is not defined by you, I used the simplest - vector
template <class T>
string vector_join( const vector<T>& v, const string& token ){
ostringstream result;
for (typename vector<T>::const_iterator i = v.begin(); i != v.end(); i++){
if (i != v.begin()) result << token;
result << *i;
}
return result.str();
}
//usage
cout << vector_join( container, ", " ) << "!";
Shift the ++i a bit:
i = container.begin();
while(i != container.end()) {
if (i != container.begin()) {
cout << ", ";
}
cout << *i;
if (++i == container.end()) {
cout << "!" << endl;
}
}
template < class TContainerType>
void print(TContainerType const & i_container)
{
typename TContainerTypeconst ::const_iterator current = i_container.begin();
typename TContainerTypeconst ::const_iterator const end = i_container.end();
if(current != end)
{
std::cout << *current++;
while(current != end)
std::cout << ", " << *current++;
}
std::cout << "!" << std::endl;
}
Take the second part out of the loop.
for(i=container.begin(); i!=container.end(); ++i) {
if (i != container.begin()) {
cout << ", ";
}
cout << *i;
}
cout << "!" << endl;