I've written a program that counts the number of inversions using a Divide and Conquer algorithm, written in C++11.
#include <iostream>
#include <tuple>
#include <vector>
using IntVec = std::vector<int>;
std::tuple<IntVec, long long> count_inv(IntVec xs);
std::tuple<IntVec, long long> count_split_inv(IntVec xs, IntVec ys);
std::tuple<IntVec, long long> count_inv(IntVec xs) {
int n = xs.size();
if (n < 2) return std::make_tuple(xs, 0);
IntVec::iterator middle = xs.begin() + (n / 2);
IntVec left(xs.begin(), middle);
IntVec right(middle, xs.end());
long long left_inv, right_inv, split_inv;
IntVec as, bs, cs;
std::tie(as, left_inv) = count_inv(left);
std::tie(bs, right_inv) = count_inv(right);
std::tie(cs, split_inv) = count_split_inv(as, bs);
return std::make_tuple(cs, left_inv + right_inv + split_inv);
}
std::tuple<IntVec, long long> count_split_inv(IntVec xs, IntVec ys) {
int n = xs.size() + ys.size();
IntVec zs(n);
long long split_inv = 0;
int i = 0, j = 0, k = 0;
for (; k < n && i < xs.size() && j < ys.size(); ++k) {
if (xs[i] <= ys[j]) {
zs[k] = xs[i];
++i;
} else {
zs[k] = ys[j];
++j;
split_inv += xs.size() - i;
}
}
for (; i < xs.size(); ++k, ++i)
zs[k] = xs[i];
for (; j < ys.size(); ++k, ++j)
zs[k] = ys[j];
return std::make_tuple(zs, split_inv);
}
int main() {
std::vector<int> xs;
int x;
while (std::cin >> x) {
xs.push_back(x);
}
IntVec ys;
long long inv;
std::tie(ys, inv) = count_inv(xs);
std::cout << inv << std::endl;
return 0;
}
I find that the code is very verbose in some areas, such as specifying the type of the tuple. Also, parts of it are confusing. How could I rewrite this code such that it is more concise and clearer?
1 Answer 1
More clear by using extra argument?
If you passed in a count by reference to each function, and each function incremented this count instead of returning a new count in a tuple, the code might look cleaner. For example, this:
IntVec as, bs, cs; std::tie(as, left_inv) = count_inv(left); std::tie(bs, right_inv) = count_inv(right); std::tie(cs, split_inv) = count_split_inv(as, bs); return std::make_tuple(cs, left_inv + right_inv + split_inv);
would become this:
IntVec as = count_inv(left, count);
IntVec bs = count_inv(right, count);
return count_split_inv(as, bs, count);
And in main()
, this:
long long inv;
std::tie(ys, inv) = count_inv(xs);
would become this:
long long inv = 0;
count_inv(xs, inv);
-
\$\begingroup\$ I'm not certain if it's good practice to use pointers in this case. \$\endgroup\$wei2912– wei29122015年11月03日 14:36:46 +00:00Commented Nov 3, 2015 at 14:36
-
\$\begingroup\$ @wei2912 As opposed to creating tuples and returning them? Why are you opposed to using pointers? \$\endgroup\$JS1– JS12015年11月03日 18:10:02 +00:00Commented Nov 3, 2015 at 18:10
-
\$\begingroup\$ It certainly looks cleaner, but in these calls to
count_inv(xs, inv)
, it's not entirely clear that the inv/count variables will be changed as a side-effect. I also agree that returning a tuple is much worse, so I don't have an imediate solution for this. \$\endgroup\$Not a real meerkat– Not a real meerkat2016年01月28日 11:31:20 +00:00Commented Jan 28, 2016 at 11:31
Explore related questions
See similar questions with these tags.
(i,j)
wherei < j && array[i] > array[j]
. At least, this is what would make sense, because this program is using the "mergesort method" of counting the number of inversions in an array. See this link, for example. \$\endgroup\$