Adjusting index in a vector in C++ - c++

I am writing a code in which the values of vector indexes are displayed as per the sorted order of the elements they hold:
For example:
values -> 3 2 4 1 5
indices -> 1 2 3 4 5 //yeah I know C++ indexing starts with 0. Well, while printing, I will add 1
result -> 4 2 1 3 5 //Bear with me. I know its confusing. I will clarify below
Now, the result has been obtained by sorting the elements and printing their earlier indices.
Like:
values(sorted) -> 1 2 3 4 5
indices(before sorting) -> 4 2 1 3 5
Now, there are many ways to do it with some suggesting to store the previous values and search and print the previous index, while others suggesting to create a new vector and copy the previous indices in it and then sorting them and.... (Well I didn't read further, 'cause that's definitely not how I was gonna proceed)
I tried a different approach while trying to not use a second vector.
So here's the approach:
while (!vec_students.empty()) {
std::vector<int>::iterator iterator = std::min_element(vec_students.begin(), vec_students.end());
std::cout << std::distance(vec_students.begin(), iterator) + 1 << " ";
vec_students.erase(iterator);
}
Now in this approach, the problem I am facing is that once I use erase, the index of all elements decreases by a certain incrementing value. So this was the solution I thought of:
while (!vec_students.empty()) {
static int i = 0; //yeah I know standard static variables are initialised to 1.
std::vector<int>::iterator iterator = std::min_element(vec_students.begin(), vec_students.end());
std::cout << std::distance(vec_students.begin(), iterator) + i << " ";
vec_students.erase(iterator);
i++;
}
Now the thought goes like this:
Initial Solution:
vector: 2 3 1
expected output: 3 1 2 (For explanation refer above)
first index = indexof(min(2,3,1)) -> 2 (while printing add 1) -> 3
second index = indexof(min(2,3)) -> 0 (while printing....) -> 1
third index = indexof(min(3)) -> 0 (while...) -> 1
Then I realized that the vector size decreases which means, indices will decrease (by 1 in this case)
so I added the extra i thingy.
Solution working:
vector: 2 3 1 i = 0
first index = indexof(min(2,3,1)) -> 3 -> add i -> 3 -> increment i -> i = 1
second index = indexof(min(2,3)) -> 0 -> add i -> 1 -> increment i -> i = 2
third index = indexof(min(3)) -> 0 -> add i -> 2 -> increment i -> i = 3
and the program ends.
But in the above case, instead of 3 1 2 I am getting 3 2 3 (first value right, rest incremented by 1)
What is wrong with my logic?

the index of all elements decreases by a certain incrementing value.
Not all, just the ones that come after the one you removed. Here's one way to do it without making another vector:
#include <algorithm>
#include <iostream>
#include <limits>
#include <vector>
int main() {
std::vector<int> v{3, 2, 4, 1, 5};
auto const* beg = v.data();
auto sz = v.size();
while (sz--) {
auto const min = std::min_element(v.begin(), v.end());
std::cout << &*min - beg << ' ';
*min = std::numeric_limits<int>::max();
}
}
This won't work properly if you have INT_MAX in your vector. In any case, making a second vector could yield better solutions. An example:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
int main() {
std::vector<int> v{3, 2, 4, 1, 5};
std::vector<int const*> addresses;
addresses.reserve(v.size());
std::transform(v.cbegin(), v.cend(), std::back_inserter(addresses),
[](auto const& elm) { return &elm; });
std::sort(addresses.begin(), addresses.end(),
[](int const* const ptr1, int const* const ptr2) {
return *ptr1 < *ptr2;
});
for (auto* p : addresses) {
std::cout << p - v.data() << ' ';
}
}

You think you should add indices because the vector is shrink.
Well, not really, only those after the removed element should be effected.
example.
[2,1,3] => [2,3]
index of 2 remains at 0,
while index of 3 becomes 1 from 2

Related

C++ Priority Queue - ordering intervals

Given initially a time interval. Every time, we pick the largest interval and split that into two halves. If there is a tie, then we pick the interval with the lowest starting point.
for example - [0,9]
first split - P1 [0,4] and P2 [4,9]
For second split :
dist(P1) = 3 => if pick P1, new intervals would be [0,2] and [2,4].
dist(P2) = 4 => if pick P2, new intervals are [4, 6] and [6,9]
In both the cases, we have to create sub interval of distance 1. So, it's a tie. and, we pick P1 as P1 < P2.
[0,2], [2, 4], [4, 9]
Third Split:
[0,2], [2, 4], [4,6], [6,9]
Fourth Split:
There is a tie, s0, picked [0,2]
[0,1], [1,2], [2,4], [4,6], [6, 9]
Fifth Split:
[0,1], [1,2], [2,3], [3,4], [4,6], [6,9]
a possible candidate to be on the top : [4,6]
But, I always get [1,2] on top.
#include <iostream>
#include <queue>
using namespace std;
int main()
{
auto dist{ [](const auto & p) {
return p.second - p.first - 1;
} };
auto comp{
[&dist](const auto & p1, const auto & p2) {
if (abs(dist(p1) - dist(p2)) <= 1) {
return p1.first > p2.first;
}
return dist(p1) < dist(p2);
}
};
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(comp)> maxQ{comp};
maxQ.push({ 0, 9 }); // initial interval
for (int i{ 0 }; i < 5; ++i) {
auto ii{ maxQ.top() };
maxQ.pop();
int mid = (ii.first + ii.second) / 2;
maxQ.push({ ii.first, mid });
maxQ.push({ mid, ii.second });
}
while (!maxQ.empty()) {
auto& ii{ maxQ.top() };
cout << ii.first << " : " << ii.second << endl;
maxQ.pop();
}
}
I'm getting the following output :
1 : 2
6 : 9
0 : 1
2 : 3
3 : 4
4 : 6
IMO, 1 : 2 interval shouldn't be on top. Could someone help me here, why is it so.
It turns out that this issue has more to do with how priority queues comparators are designed, refer to The reason of using `std::greater` for creating min heap via `priority_queue`
The gist of it is that when two nodes are compared, if the comparator returns true, p1 will fall below p2. So a basic ">" comparator, will have smaller nodes at the top, and bigger nodes at the bottom.
To visualize the issue, I ran through it in a debugger, this is the moment at which the (1,2) is being put above (6,9). This is the current state of the priority queue:
2 : 4
6 : 9
4 : 6
0 : 1
1 : 2
We see that (2,4) is in front (6,9), which is expected since our comparison function says that (2,4) < (6,9) as explained above.
Then, the code goes to pop the top of the priority queue, meaning replaces (2,4) with the new biggest interval. How priority queues in C++ do this, is they swap the first element and last elements of the heap, and then reduce the size of it by 1 (so we lose the original first element).
So after the swap and size reduction, our heap looks like this:
1 : 2
6 : 9
4 : 6
0 : 1
Then, since the previously deemed smallest element is now at the top of the queue, we need to find its rightful spot.
So (1,2) is going to look at its children, (6,9) and (4,6), and see which is more important.
With our comparison operator, (4,6) is the more important node.
It then compares, (1,2) with the most important of the previous two nodes, (4,6), to see if it needs to perform a swap to make the queue valid.
It then finds that (1,2) is more important because 1 < 4. Thus, (1,2) stays in its spot and we're left with:
1 : 2
6 : 9
4 : 6
0 : 1
We can plainly see that [1,2] is ordered before [4,6], by plugging it into your comparator:
comp([1,2], [4,6])
if (abs(dist([1,2]) - dist([4,6])) <= 1) { // abs(0 - 1) <= 1 or 1 <= 1
return 1 > 4; // false
}
return dist([1,2]) < dist([4,6]); // not reached
Only you can correct the comparator to achieve whatever your goal is here, but the existing code is wrong if you want [1,2] to be ordered after [4,6].
At a guess, though, based on your description, you might try:
if (abs(dist(p1)) == abs(dist(p2)))
But I'd go to some lengths to ensure that your ordering is strict weak as it must be for the container. Sprinkling some more abs around may help.
Overall this is quite a complex comparator that's not easy to understand at a glance.
I think It is because the ordering of intervals is not strict.
e.g. P1(0,1), P2(4,6) and P3(6,9)
P1 should come before P2.
P2 should come before P3.
P3 should come before P1.
That's crazy. How could I set a strict pair ordering here?

How to use boost::range::adaptors::transformed over std::unordered_set?

I'm trying to use boost::adaptors::transformed over a std::unordered_set but seems to produce weird behaviors even on quite small experiments.
I'm using Boost 1.58.0 on Ubuntu 16.04 with gcc 5.4.0.
Added elements after range initialization are not listed while iterating the range:
#include <iostream>
#include <vector>
#include <unordered_set>
#include <boost/range/adaptor/transformed.hpp>
struct double_int
{
typedef int result_type;
int operator()(int x) const { return x * 2; }
};
int main()
{
std::unordered_set<int> set;
for(int i = 0; i < 5; ++i)
set.insert(i); //adding ints to set
auto range = set | boost::adaptors::transformed(double_int());
set.insert(10); //adding some other int
//this produces: '8 0 2 4 6'
for(auto i : range)
std::cout << i << std::endl;
//this produces: '10 4 0 1 2 3'
for(auto i : set)
std::cout << i << std::endl;
//element 10 is not doubled!
return 0;
}
Following the same scheme with other std containers (like std::list) work as intended, doubling the latter added elements.
Even more weirdly if the set is initialized using:
std::unordered_set<int> set = {0,1,2,3,4,5};
range iteration gives only '10' while container's '10 0 1 2 3 4 5'
Could someone tell me what is wrong with this example?
transformed doesn't store a reference to the range as a range; it grabs the begin/end iterators of the range at the time of the range adaptor's construction.
When you later insert into the set after the adaptor's been constructed, the new element might not be in the range delimited by the old begin/end iterators prior to the insertion. Or, even worse, the insertion will invalidate all the iterators if it triggers a rehash.
I can't really see the problem:
Your exact sample code (live) prints:
8 20 6 4 0 2
4 10 3 2 0 1
Which seems to be what you should expect.
Using the other elements:
10 8 6 4 2 0
5 4 3 2 1 0
IDEA:
It could be Undefined Behaviour to assign the template expression:
auto range = set | boost::adaptors::transformed(double_int());
Because double_int() might be kept in the transformed adaptor BY REFERENCE (haven't checked).
See whether this removes the issue for you:
for (auto i : set | boost::adaptors::transformed(double_int()))
std::cout << i << " ";

Efficient way to reverse three consecutive subranges [A,B,C] -> [C,B,A]

I've got an array [A,B,C] consisting of three consecutive sub-arrays A, B and C. I'd like to reverse the larger array into [C,B,A]. My current attempt involves thee calls to std::rotate, as shown below. I wonder if there is a more straightforward/efficient way to achieve this, preferably using an std algorithm.
Step 1: "swap" sub-array B,C
[A,B,C] -> [A|B,C] -> [A,C,B]
Step 2: "swap" sub-array A,C
[A,C,B] -> [A,C|B] -> [C,A,B]
Step 3: "swap" sub-array A,B
[C,A,B] -> [C|A,B] -> [C,B,A]
Edit
For example given the array [1,2,3|4,5,6|7,8,9] I would like to "reverse" it into [7,8,9|4,5,6|1,2,3]
Sample implementation. Please note that the sizes of the "ranges" are merely illustrative.
Reverse the whole array then reverse each subarray.
[1,2,3|4,5,6|7,8,9]
[9,8,7|6,5,4|3,2,1]
[7,8,9|4,5,6|1,2,3]
This will take two linear passes and is extremely CPU cache friendly.
Here's a version with Ranges V3:
array<int, 9> arr = {{1,2,3,4,5,6,7,8,9}};
auto v = view::chunk(arr, 3) | view::reverse | view::join;
array<int, 9> reversed;
ranges::copy(v, reversed.begin());
for(int n : arr) cout << n << ' '; cout << '\n';
for(int n : reversed) cout << n << ' '; cout << '\n';
Prints:
1 2 3 4 5 6 7 8 9
7 8 9 4 5 6 1 2 3
Can't say much about performance, but it didn't compile quickly for me.

C++ Multiplying elements in a vector

I have been looking for a more optimal solution to the following and I cannot seem to find one.
Let's say I have a vector:
std::vector<double> vars = {1, 2, 3}
I want to perform 1 * 2 * 3 I know that I can do the following:
int multi = 1;
for(int i = 0; (i < vars.size()-1); i++)
{
multi *= vars[i];
}
But, is there a more "C++11" way to do this? I really wanted to do this using lambda and so that I can calculate the multiply (product) of the vector without having another function inside the class, I'd rather have it calculated inside the function.
Yes, as usual, there is an algorithm (though this one's in <numeric>), std::accumulate (live example):
using std::begin;
using std::end;
auto multi = std::accumulate(begin(vars), end(vars), 1, std::multiplies<double>());
std::multiplies is in <functional>, too. By default, std::accumulate uses std::plus, which adds two values given to operator(). std::multiplies is a functor that multiplies them instead.
In C++14, you can replace std::multiplies<double> with std::multiplies<>, whose operator() is templated and will figure out the type. Based on what I've seen with Eric Niebler's Ranges proposal, it could possibly later look like vars | accumulate(1, std::multiplies<>()), but take that with a grain of salt.
You can use a ranged based for loop like:
std::vector<double> vars = {1, 2, 3}
int multi = 1;
for (const auto& e: vars)
multi *= e;
Another way is using inclusive_scan (C++17 and above)
The advantage is you can get multiplies of first "N" elements in a vector.
Below is the code. Explanation in comments.
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
int main()
{
//INPUT VECTOR
std::vector<int> data{ 3, 1, 4, 1, 5, 9, 2, 6 };
//OUTPUT VECTOR WITH MULTIPLIES
//FIRST ELEMENT - 3
//SECOND ELEMENT - 3 * 1
//THIRD ELEMENT - 3 * 1 * 4
//FOURTH ELEMENT - 3 * 1 * 4 * 1
// ..
// ..
//LAST ELEMENT - 3 * 1 * 4 * 1 * 5 * 9 * 2 * 6
std::vector<int> multiplies(data.size());
//Multiply all numbers in given vector.
inclusive_scan(data.begin(), data.end(),
multiplies.begin(),
std::multiplies<>{});
//PRODUCT OF FIRST 5 ELEMENTS.
std::cout << "Product of first 5 elements :: " << multiplies[4] << std::endl;
//PRODUCT OF ALL ELEMENTS
std::cout << "Product of all elements :: " << multiplies[data.size() - 1] << std::endl;
}
Also there is an overload where the execution policy can be specified.
Sequential execution or Parallel execution. Need to include "execution" header.
//MULTIPLY ALL NUMBERS IN VECTOR WITH PARALLEL EXECUTION.
inclusive_scan(std::execution::par,data.begin(), data.end(),
multiplies.begin(),
std::multiplies<>{});

Splitting an STL list?

I have a circular linked list that looks something like this:
4 -> 3 -> 2 -> 5 -> 0 -> 1 -> beginning
I want to split this list into two segments, reverse one of the segments, and then rejoin the list. Something like this:
Split, an O(1) operation
4 ** 3 -> 2 -> 5 ** 0 -> 1 -> beginning
Reverse, an O(n) operation
0 ** 3 -> 2 -> 5 ** 4 -> 1 -> beginning
Rejoin, an O(1) operation
0 -> 3 -> 2 -> 5 -> 4 -> 1 -> beginning
The STL does not appear to have a circular linked list, but I'm hoping I can get away with representing the list as a (forward) list. This, requires:
* A way to split the lists into sublists
* A way to merge the lists together
Merging the sublists together should be easy using std::list::splice, and it should be an O(1) operation. Yay!
However, I can't find a good O(1) way to split the lists into sublists. One approach is to use iterators, but I don't think that works in my case because the sublist goes off the end of the list and resumes at the beginning of the list.
Is there an efficient way to implement this algorithm using the STL? Or should I just give up and write my own circular linked list library?
Thanks!
I am not sure how helpful this is, but here it is:
template<typename I>
void inner(I b, I e)
{
std::reverse(b, e); // l = {4 5 2 3 0 1}
}
template<typename L, typename I>
void outer(L& l, I b, I e)
{
l.splice(l.begin(), l, e, l.end()); // l = {0 1 4 3 2 5}
std::reverse(l.begin(), b); // l = {4 1 0 3 2 5}
}
int main ()
{
std::list<int> t, l{4, 3, 2, 5, 0, 1};
std::cout << l << std::endl;
auto b = l.begin(), e = l.begin();
std::advance(b, 1);
std::advance(e, 4);
bool in = true;
in ? inner(b, e) : outer(l, b, e);
std::cout << l << std::endl;
}
There are two options:
reverse the inner part of the list (3 2 5)
reverse the outer part (0 1 -> 4)
and you might want to reverse the shorter one, but you'll have to count for this, which is linear-time. I have written two separate functions inner,outer for these two tasks.
inner is very simple (nothing but a wrapper) and working as expected. However, outer is leaving the list in a state that is equivalent to the expected modulo some rotation (circular shift). If you are modelling a circular list, this should be fine. But if you want the output exactly as in your example, you'll have to count again, to find the right point where to rotate. I would avoid this, as it is linear-time.
Note that no splitting is actually needed, because reversal is in-place. Also, operation
l.splice(l.begin(), l, e, l.end());
is constant-time.
Check live example.