I just read and experimented with C++ implementation of the merge sort algorithm from the Wikipedia page. During the experiments I have slightly modified the source code and would like to know how much the algorithm is improved (if it is).
//! \brief Performs a recursive merge sort on the given vector
//! \param vec The vector to be sorted using the merge sort
//! \return The sorted resultant vector after merge sort is
//! complete.
vector<int> merge_sort(vector<int>& vec)
{
// Termination condition: List is completely sorted if it
// only contains a single element.
if(vec.size() == 1)
{
return vec;
}
// Determine the location of the middle element in the vector
std::vector<int>::iterator middle = vec.begin() + (vec.size() / 2);
vector<int> left(vec.begin(), middle);
vector<int> right(middle, vec.end());
// Perform a merge sort on the two smaller vectors
left = merge_sort(left);
right = merge_sort(right);
return merge(vec,left, right);
}
//! \brief Merges two sorted vectors into one sorted vector
//! \param left A sorted vector of integers
//! \param right A sorted vector of integers
//! \return A sorted vector that is the result of merging two sorted
//! vectors.
vector<int> merge(vector<int> &vec,const vector<int>& left, const vector<int>& right)
{
// Fill the resultant vector with sorted results from both vectors
vector<int> result;
unsigned left_it = 0, right_it = 0;
while(left_it < left.size() && right_it < right.size())
{
// If the left value is smaller than the right it goes next
// into the resultant vector
if(left[left_it] < right[right_it])
{
result.push_back(left[left_it]);
left_it++;
}
else
{
result.push_back(right[right_it]);
right_it++;
}
}
// Push the remaining data from both vectors onto the resultant
while(left_it < left.size())
{
result.push_back(left[left_it]);
left_it++;
}
while(right_it < right.size())
{
result.push_back(right[right_it]);
right_it++;
}
//show merge process..
int i;
for(i=0;i<result.size();i++)
{
cout<<result[i]<<" ";
}
// break each line for display purposes..
cout<<"***********"<<endl;
//take a source vector and parse the result to it. then return it.
vec = result;
return vec;
}
The following is my code:
typedef std::vector<int> int_v;
int_v Merge(int_v& vec, int_v& l, int_v& r) {
int_v res;
res.reserve(l.size() + r.size());
unsigned int li = 0;
unsigned int ri = 0;
if (l.size() > 1 && (l[l.size()-1] < r[0])) {
res.insert(res.end(), l.begin(), l.end());
res.insert(res.end(), r.begin(), r.end());
return res;
}
while (li < l.size() && ri < r.size()) {
if (l[li] < r[ri]) {
res.push_back(l[li++]);
} else {
res.push_back(r[ri++]);
}
}
while (li < l.size()) {
res.push_back(l[li++]);
}
while (ri < r.size()) {
res.push_back(r[ri++]);
}
vec = res;
return vec;
}
int_v MergeSort(int_v& v) {
if (1 == v.size()) {
return v;
}
int_v::iterator m = v.begin() + v.size()/2;
int_v l(v.begin(), m);
int_v r(m, v.end());
l = MergeSort(l);
r = MergeSort(r);
return Merge(v, l, r);
}
The modifications are:
res.reserve(l.size() + r.size());
This will help to avoid relocation of the vector each time when there will need to double the size to be able to store the next element.
if (l.size() > 1 && (l[l.size()-1] < r[0])) {
res.insert(res.end(), l.begin(), l.end());
res.insert(res.end(), r.begin(), r.end());
return res;
}
I think this will help to avoid the 'while' loops when just concatenation of two sides is enough.
Running both versions Wikipedia's one and mine for five million randomly generated integers produces the following differences:
Wikipedia's example:
time ./a.out real 0m26.278s user 0m26.205s sys 0m0.064s
Mine:
time ./a.out real 0m22.129s user 0m22.026s sys 0m0.092s