Following the review of my old merge sort implementation here, it appears that the biggest improvement to make was to use iterators instead of copying the vector
s. Having never used iterators before, I'd like a review of the new code following that review:
#include <iostream>
#include <vector>
#include <algorithm>
template<typename T>
void Merge(T, T, T, T);
template<typename T>
void MergeSort(T, T);
int main()
{
// As last time, this is just test code and does not need to be reviewed, but
// is included for completeness
std::vector<int> a;
for (int i = 0; i < 10; i++) {
a.push_back(10.0 * rand() / RAND_MAX);
}
for (int i = 0; i < a.size(); i++) {
std::cout << a[i] << " ";
}
std::cout << "\n";
// Note this swap is now in place, previously it returned a new vector
MergeSort(a.begin(), a.end());
for (int i = 0; i < a.size(); i++) {
std::cout << a[i] << " ";
}
std::cout << "\n";
}
template<typename T>
void Merge(T arr1Begin, T arr1End, T arr2Begin, T arr2End) {
T i1 = arr1Begin;
T i2 = arr2Begin;
int i = 0;
std::vector<int> res(arr2End - arr1Begin, 0);
while (i1 < arr1End && i2 < arr2End) {
if (*(i1) < *(i2)) {
res[i++] = *(i1);
std::advance(i1, 1);
}
else {
res[i++] = *(i2);
std::advance(i2, 1);
}
}
while (i1 < arr1End) {
res[i++] = *(i1);
std::advance(i1, 1);
}
while (i2 < arr2End) {
res[i++] = *(i2);
std::advance(i2, 1);
}
std::copy(res.begin(), res.end(), arr1Begin);
}
template<typename T>
void MergeSort(T arrBegin, T arrEnd) {
int size = arrEnd - arrBegin;
if (size < 2) {
return;
}
if (size == 2) {
if (*arrBegin > *(arrEnd - 1)) {
std::swap(*arrBegin, *(arrEnd - 1));
}
return;
}
MergeSort(arrBegin, arrBegin + size / 2);
MergeSort(arrBegin + size / 2, arrEnd);
return Merge(
arrBegin, arrBegin + size / 2,
arrBegin + size / 2, arrEnd
);
}
My questions this time:
- I'm dereferencing a lot of iterators in order to swap and assign values, is there a better way to be doing this?
- Is this implementation better than my last implementation? I'm looking for answers that think about stability, efficiency in terms of speed/memory, etc. (Possibly an obvious question)
- Also, I know nothing about templates, is there anything more to it than adding the line
template<typename T>
then line before the function and then usingT
within it? - I can't figure out how to get rid of
std::vector<int> res(arr2End - arr1Begin, 0);
. Obviously this goes against the templating, how should I store the results while I'm doing the merge? (Edit: since looking back at the previous accepted answer, I figure I should create a new vector outside the original call toMergeSort
(insidemain
) and pass the.begin()
of that array as the destination, rather than defining a new array inside eachMerge()
call and usingstd::copy
. Is this correct?)
Edit: Thanks to @ratchetfreak for pointing out a bug whereby I was overwriting elements of arr1
without storing the overwritten element elsewhere beforehand (essentially losing it).