std::unordered_set insert, get the position where item was inserted - c++

Assume I have class MyClass
class MyClass
{
public:
MyClass( std::string str ) : _str(str) {}
void SetPosition ( int i ) { _pos = i; }
std::string _str;
int _pos;
};
namespace std
{
template<> struct hash<shared_ptr<MyClass>>
{
size_t operator()( const shared_ptr<MyClass> & ptr ) const
{
return hash<string>()( ptr->_str ) + hash<int>()( ptr->_pos );
}
};
}
When using std::vector, I was able to do this:
std::string str = "blah";
auto ptr = std::make_shared<MyClass>( str );
std::vector<std::shared_ptr<MyClass>> vector;
vector.push_back( ptr );
ptr->SetPosition ( std::addressof( vector.back() ) - std::addressof( vector[0] ) );
std::cout << ptr->_str << " is at " << ptr->_pos << std::endl;
In order to calculate where in the vector, my object pointer was placed.
However, If I want to use std::unordered_set (which I do), then:
std::string str = "blah";
auto ptr = std::make_shared<MyClass>( str );
std::unordered_set<std::shared_ptr<MyClass>> set;
auto res = set.insert( ptr );
ptr->SetPosition ( std::addressof( res.first ) - std::addressof( set[0] ) );
std::cout << ptr->_str << " is at " << ptr->_pos << std::endl;
Will not work.
Neither will
std::addressof( set.begin() );
Nor will,
std::addressof( set.begin().first );
or any other way I try to use the front iterator.
Does this make sense? Or should I rely on set.size() and assume that my pointer was inserted at the end?
Is there any way to safely get the position where that pointer was inserted using something similar to the above code?

unordered_set, like the name implies, is unordered. You can keep track of the position of your elements in a vector, because as long as you don't erase anything, they won't change places. But that's not true of unordered_set. For instance, on my implementation, here's what printing all the elements in order after every insert would yield:
std::unordered_set<int> s;
s.insert(0); // 0
s.insert(1); // 1 0
s.insert(2); // 2 1 0
s.insert(3); // 3 2 1 0
...
s.insert(22); // 22 0 1 2 3 ... 19 20 21
...
s.insert(48); // 48 47 46 45 ... 22 0 1 2 3 4 ... 21
So what I'm trying to say is order is definitely not something that makes sense for you to rely on.
With your vector, however, you can do much better in terms of setting position:
vector.push_back(ptr);
ptr->SetPosition(vector.size() - 1);

Related

C++ linked List nodes not changing

I am trying to reorder Linked List such that given linked list (Head),
L0 → L1 → … → Ln - 1 → Ln
changes to,
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
void reorderList(ListNode* head) {
ListNode* h=head;
map<int,ListNode*> allNodes;
int count=0;
while(h!=nullptr){
ListNode* temp=new ListNode(h->val);
allNodes[count]=temp;
count++;
h=h->next;
}
auto rItr=allNodes.rbegin();
auto itr=allNodes.begin();
head=nullptr;// tried with and without this line same result
while(itr->first!=rItr->first){
head=itr->second;
head=head->next;
head=rItr->second;
itr++;
rItr++;
head=head->next;
}
head=itr->second;
}
I am creating new nodes and storing them in the map, Why is the Linked List not getting updated?
Input [1,2,3,4,5]
Expected output:
[1,5,2,4,3]
Actual Output
[1,2,3,4,5]
That is no change
To make the assignment there is no need to allocate new nodes. Moreover it is inefficient and requires additional memory.
Pay attention to that within your function you are changing the pointer to the head node but the original pointer to the head node is passed to the function by value. That is the function deals with a copy of the value of the original pointer to the head node. Changing the copy does not influence on the value stored in the original pointer to the head node.
As an idea I can suggest the following approach to the assignment.
First, find the middle of the list. Using the middle split the list
into two lists.
Second, reverse the second list.
Third, combine the two lists in one list.
Here is a demonstrative program that shows the approach using the standard container std::forward_list that represents a singly-linked list (it is not a production code.:)).
#include <iostream>
#include <forward_list>
#include <iterator>
template <typename T>
typename std::forward_list<T>::const_iterator
find_middle( const std::forward_list<T> &list )
{
auto middle = std::begin( list );
for ( auto last = std::begin( list );
last != std::end( list ) && ( std::advance( last, 1 ), last != std::end( list ) );
std::advance( last, 1 ) )
{
std::advance( middle, 1 );
}
return middle;
}
template <typename T>
void merge( std::forward_list<T> &first, std::forward_list<T> &second )
{
for ( auto position = std::begin( first );
position != std::end( first ) && !second.empty(); )
{
position = first.insert_after( position, second.front() );
std::advance( position, 1 );
second.erase_after( second.before_begin() );
}
if ( !second.empty() )
{
first.insert_after( std::end( first ), std::begin( second ), std::end( second ) );
second.clear();
}
}
int main()
{
std::forward_list<int> first = { 1, 2, 3, 4, 5 };
for ( const auto &current : first )
{
std::cout << current << ' ';
}
std::cout << '\n';
std::cout << '\n';
auto middle = find_middle( first );
if ( middle != std::begin( first ) )
{
std::forward_list<int> second;
second.splice_after( second.before_begin(), first, middle, std::cend( first ) );
for ( const auto &current : first )
{
std::cout << current << ' ';
}
std::cout << '\n';
for ( const auto &current : second )
{
std::cout << current << ' ';
}
std::cout << '\n';
std::cout << '\n';
second.reverse();
for ( const auto &current : first )
{
std::cout << current << ' ';
}
std::cout << '\n';
for ( const auto &current : second )
{
std::cout << current << ' ';
}
std::cout << '\n';
std::cout << '\n';
merge( first, second );
for ( const auto &current : first )
{
std::cout << current << ' ';
}
std::cout << '\n';
for ( const auto &current : second )
{
std::cout << current << ' ';
}
std::cout << '\n';
}
return 0;
}
The program output is
1 2 3 4 5
1 2 3
4 5
1 2 3
5 4
1 5 2 4 3
So what you need is to write a function that finds the middle node (similar to the function find_middle shown in the demonstrative program). Then to write a function that splits a list based on the middle node into two lists. Then to write a function that reverses a singly-linked list. And then it will be easy to write a function that combines two list in one list.

How to convert a C++ STL vector iterator to a vector reverse iterator?

I have used a nested for-loop to carry out insertion sort on a C++ STL <vector>. The first for-loop is over an iterator and the second one, over a reverse_itr.
I need to pass the index (iterator pointer value) from the first loop to the second. I have tried the following approach but it gives me this error
error: no match for ‘operator!=’ (operand types are
‘__gnu_cxx::__normal_iterator<int*, std::vector<int> >’ and
‘std::vector<int>::reverse_iterator’ {aka
‘std::reverse_iterator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > >’})
void insertionSort(int size, vector<int> arr) {
for(auto itr = arr.begin(); itr != arr.end() - 1; ++itr) {
int element = *(itr + 1);
cout << "Element being compared with preceding sub-array is : " << element << endl;
for(auto r_itr = (itr + 1); r_itr != arr.rend(); ++r_itr) {
if(*(r_itr+1) <= element) {
*r_itr = element;
break;
}
else {
*r_itr = *(r_itr+1);
}
}
}
}
I searched up quite a lot online, found a way to convert a reverse iterator to an iterator (using itr.base()) but not the other way round.
Also I am new to C++ STL and algorithms, please feel free to suggest any way to improve my code with respect to the "clean"-ness of code or the algorithm itself!
The class template std::vector has random access iterators. So there is no any need to convert a given "forward" iterator to a "reverse" iterator.
Just use with iterators the operator -- instead of the operator ++ or vice versa where it is required.
Pay attention to that the parameter size is not used in your function declared like
void insertionSort(int size, vector<int> arr);
If you want to sort a range from a vector then use two parameters declared as iterators that specify a range.
For example
void insertionSort( std::vector<int>::iterator first, std::vector<int>::iterator last );
Or you can write a more general function for vectors of any types using templates.
Here is a demonstrative program.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <cstdlib>
#include <ctime>
void insertionSort( std::vector<int>::iterator first, std::vector<int>::iterator last )
{
if ( first != last )
{
for ( auto current = first; ++current != last; )
{
typename std::iterator_traits<decltype( current )>::value_type
value( *current );
auto prev = current, next = current;
while ( next != first && value < *--prev )
{
*next-- = *prev;
}
if ( next != current ) *next = value;
}
}
}
int main()
{
std::vector<int> v;
const int N = 10;
std::srand( ( unsigned int )std::time( nullptr ) );
std::generate_n( std::back_inserter( v ), N, [=]{ return std::rand() % N; } );
for ( const auto &item : v )
{
std::cout << item << ' ';
}
std::cout << '\n';
insertionSort( std::begin( v ), std::end( v ) );
for ( const auto &item : v )
{
std::cout << item << ' ';
}
std::cout << '\n';
return 0;
}
Its output might look like
0 6 6 7 4 3 4 6 6 1
0 1 3 4 4 6 6 6 6 7
Try changing it to for(vector<int>::reverse_iterator r_itr(next(itr)); r_itr != arr.rend(); ++r_itr)
To expand on their working, reverse_iterator is not implemented the same as iterator. The logical and physical address for an iterator are the same but for reverse_iterator, the logical and physical address are not the same. For example: s.end() and s.rbegin() have the same physical address but *s.end() will give you an error but *s.rbegin() will give you the last value of the container s.
The code below will make things clear:
#include <iostream>
#include <set>
using namespace std;
int main()
{
set<int> S{ 1, 2, 3 };
set<int>::iterator itr = S.find(2);
cout << *itr << endl;
set<int>::reverse_iterator r_itr(itr);
cout << *r_itr << endl;
cout << itr._Ptr << ' ' << r_itr.base()._Ptr << endl;
//S.erase(r_itr); // ERROR!
S.erase(r_itr.base());
for (int e : S)
cout << e << ' ';
}
On my machine, it produced the following output:
2
1
00F85DA8 00F85DA8
1 3

Placement New and Perfect Forwarding

I have the following code that intends to create an array, but without default initialization of its objects. I would like to forward perfectly to placement new, which seems to happen, but I find that the objects' destructor is called inside the emplace function.
#include <iostream>
#include <memory> // std::uninitialized_copy, std::allocator...
#include <utility> // std::move...
#include <bitset>
struct Int {
int i;
Int ( ) : i ( -1 ) { std::cout << "default constructed\n"; }
Int ( const int i_ ) : i ( i_ ) { std::cout << i << " constructed\n"; }
Int ( Int && int_ ) : i ( std::move ( int_.i ) ) { std::cout << i << " move constructed\n"; }
Int ( const Int & int_ ) : i ( int_.i ) { std::cout << i << " copy constructed\n"; }
~Int ( ) { std::cout << i << " destructed\n"; i = -1; }
};
template <typename T, size_t S = 64>
class NoInitArray {
std::bitset<S> m_used;
T *m_array = reinterpret_cast < T* > ( ::operator new ( sizeof ( T ) * S ) );
public:
T const &operator [ ] ( const size_t idx_ ) const {
return m_array [ idx_ ];
}
NoInitArray ( ) { }
~NoInitArray ( ) {
for ( size_t idx = 0; idx < S; ++idx ) {
if ( m_used [ idx ] ) {
reinterpret_cast< const T* > ( m_array + idx )->~T ( );
}
}
}
template<typename ...Args>
void emplace ( const size_t idx_, Args &&... value_ ) {
std::cout << "start emplace\n";
m_used [ idx_ ] = 1;
new ( m_array + idx_ ) T ( std::forward<T> ( value_ ) ... );
std::cout << "end emplace\n";
}
};
int main ( ) {
NoInitArray<Int> nia;
nia.emplace ( 0, 0 );
nia.emplace ( 1, 1 );
std::cout << nia [ 1 ].i << std::endl;
nia.emplace ( 2, 2 );
return 0;
}
The result of running this program is as follows:
start emplace
0 constructed
0 move constructed
0 destructed
end emplace
start emplace
1 constructed
1 move constructed
1 destructed
end emplace
1
start emplace
2 constructed
2 move constructed
2 destructed
end emplace
0 destructed
1 destructed
2 destructed
It shows that the objects are constructed once and destructed twice (which obviously is UB), once inside the emplace function, and then once at destruction of the NoInitArray.
The question is "Why is the destructor of my Int object called inside the emplace function"?
Compiler, latest Clang/LLVM on Windhoze.
EDIT1: I've added move and copy constructors to the Int struct, now the count matches, i.e. 2 constructions and 2 destructions.
EDIT2: Changing the Placement New line from new ( m_array + idx_ ) T ( std::forward<T> ( value_ ) ... ); to new ( m_array + idx_ ) T ( value_ ... ); avoids the superfluous construction/destruction, without the need for a move constructor.
EDIT3: Just for future readers. As per above, the ~NoInitArray() leaks memory. Calling delete on m_array is bad news as well as this calls (in Clang/LLVM) the destructor of m_array [ 0 ] (but as far as I've understood now, that is in no way guaranteed, i.e. UB). std::malloc/std::free seems to be the way to go, but some say that if you do that all hell will break lose, and one may lose a leg.
"It shows that the objects are constructed once and destructed twice" is not true. The output X move constructed should be included as one construction so the constructions are twice.
The line
new ( m_array + idx_ ) T ( std::forward<T> ( value_ ) ... );
should be
new ( m_array + idx_ ) T ( std::forward<Args&&> ( value_ )... );
std::forward<T>(value_) calls the constructor when T=Int, and this temporary object is moved, so there is an extra move constructor call.
EDIT
In your edit 2 you replace the line without std::forward anymore. In this case, OK, but the differences emerge when you call the emplace like this
nia.emplace ( 0, Int(0) );
Without std::forward, new T(value_...) would call the copy constructor, while new T(std::forward<Args&&>(value_)...) would call the move constructor.
EDIT-2
It should be new T(std::forward<Args>(value_)...). Thanks to #Constantin Baranov.
I think the constructor and destructor are called in the step: std::forward<T> ( value_ ) in new ( m_array + idx_ ) T ( std::forward<T> ( value_ ) ... ).
The std::forward<T>(value_) will create a temp value T.

Range based for loop with pointer to vector in C++11

Consider the following example:
vector<vector<char>*> *outer = new vector<vector<char>*>();
{
vector<char> *inner = new vector<char>();
inner->push_back(0);
inner->push_back(1);
inner->push_back(2);
outer->push_back(inner);
inner->push_back(3);
}
auto x = outer->at(0);
for (auto c : x) {
cout << c << ",";
}
I would like to iterate through the values of the vector<char>*; how can I accomplish that?
Why don't you simply dereference x?
for (auto c : *x) { // Here
cout << c << ",";
}
It will take *xby reference and, so, won't make a copy.
The type of x is vector<char>* so you need to iterate over what is pointed to by x; you need to dereference x.
for (auto&& c : *x) {
// ^ dereference the container for a range
// ^ note the use of &&
As a side note;
the characters for the integer values 0, 1 etc. are not printable, I'd test with 0x31 etc or just push back characters '0', '1' etc.
it is generally preferable to use auto&& in the range based for loops to avoid copies or subtle bugs when the container returns proxy object.
Here is a demonstrative program
#include <iostream>
#include <vector>
#include <cstring>
int main()
{
const char *s = "Hello Lu4";
std::vector<std::vector<char> *> *v =
new std::vector<std::vector<char> *>( 1, new std::vector<char>( s, s + std::strlen( s ) ) );
for ( char c : *( *v )[0] ) std::cout << c;
std::cout << std::endl;
for ( auto p : *v ) delete p;
delete v;
}
The program output is
Hello Lu4
Also you could use the following loop
for ( auto inner : *v )
{
for ( char c : *inner ) std::cout << c;
std::cout << std::endl;
}
If to use this loop in the demonstrative program you will get the same result as above because the "outer" vector contains only one element with index 0.

C++ - Find adjacent elements in std::map

What is the most efficient way to look up the adjacent elements in a STL map using the examples I mention below:
Suppose I have a map of integer - string:
1 -> Test1
5 -> Test2
10 -> Test3
20 -> Test4
50 -> Test5
If I call:
get_adjacent(1) // Returns iterator to 1 and 5
get_adjacent(2) // Returns iterator to 1 and 5
get_adjacent(24) // Returns iterator to 20 and 50
get_adjacent(50) // Returns iterator to 20 and 50
Use std::lower_bound and std::upper_bound for exactly this.
Better yet std::map::equal_range combines the power of both:
See it live on http://liveworkspace.org/code/d3a5eb4ec726ae3b5236b497d81dcf27
#include <map>
#include <iostream>
const auto data = std::map<int, std::string> {
{ 1 , "Test1" },
{ 5 , "Test2" },
{ 10 , "Test3" },
{ 20 , "Test4" },
{ 50 , "Test5" },
};
template <typename Map, typename It>
void debug_print(Map const& map, It it)
{
if (it != map.end())
std::cout << it->first;
else
std::cout << "[end]";
}
void test(int key)
{
auto bounds = data.equal_range(key);
std::cout << key << ": " ; debug_print(data, bounds.first) ;
std::cout << ", " ; debug_print(data, bounds.second) ;
std::cout << '\n' ;
}
int main(int argc, const char *argv[])
{
test(1);
test(2);
test(24);
test(50);
}
Outputs:
1: 1, 5
2: 5, 5
24: 50, 50
50: 50, [end]