Quick Sort on C++ vector
up vote
7
down vote
favorite
I've seen other implementations around, but they seem pretty complicated. This seems to work for me, but is there anything I'm missing? Also, any tips on how I can improve the quality of the code is appreciated.
#include <iostream>
#include <vector>
template<typename Iter>
void quickSort(std::vector<typename Iter::value_type>& vec, Iter left,
Iter right) {
auto size = std::distance(left, right);
if (size <= 1) {
return;
}
auto pivot = std::next(right, -1);
if (size == 2 && *pivot < *left) {
std::iter_swap(left, pivot);
}
auto wall = left;
auto curr = left;
while (curr != right) {
if (*curr < *pivot) {
std::iter_swap(wall, curr);
wall++;
}
curr++;
}
std::iter_swap(pivot, wall);
quickSort(vec, left, wall);
quickSort(vec, wall + 1, right);
}
int main() {
std::vector<int> myVec = { 6, 5, 2, 3, 2, 4, 34, 2434, 251, 4, 12, 4, 5,
634, 523, 5, 4, 353, 3, 5, 345, 7, 86786, 8, 7, 9, 1 };
quickSort(myVec, myVec.begin(), myVec.end());
return 0;
}
c++ quick-sort
add a comment |
up vote
7
down vote
favorite
I've seen other implementations around, but they seem pretty complicated. This seems to work for me, but is there anything I'm missing? Also, any tips on how I can improve the quality of the code is appreciated.
#include <iostream>
#include <vector>
template<typename Iter>
void quickSort(std::vector<typename Iter::value_type>& vec, Iter left,
Iter right) {
auto size = std::distance(left, right);
if (size <= 1) {
return;
}
auto pivot = std::next(right, -1);
if (size == 2 && *pivot < *left) {
std::iter_swap(left, pivot);
}
auto wall = left;
auto curr = left;
while (curr != right) {
if (*curr < *pivot) {
std::iter_swap(wall, curr);
wall++;
}
curr++;
}
std::iter_swap(pivot, wall);
quickSort(vec, left, wall);
quickSort(vec, wall + 1, right);
}
int main() {
std::vector<int> myVec = { 6, 5, 2, 3, 2, 4, 34, 2434, 251, 4, 12, 4, 5,
634, 523, 5, 4, 353, 3, 5, 345, 7, 86786, 8, 7, 9, 1 };
quickSort(myVec, myVec.begin(), myVec.end());
return 0;
}
c++ quick-sort
I seem to remember having seen quicksort before, even Lomuto partitioning, and a tag for things done time and again: reinventing-the-wheel.
– greybeard
Mar 8 at 7:45
add a comment |
up vote
7
down vote
favorite
up vote
7
down vote
favorite
I've seen other implementations around, but they seem pretty complicated. This seems to work for me, but is there anything I'm missing? Also, any tips on how I can improve the quality of the code is appreciated.
#include <iostream>
#include <vector>
template<typename Iter>
void quickSort(std::vector<typename Iter::value_type>& vec, Iter left,
Iter right) {
auto size = std::distance(left, right);
if (size <= 1) {
return;
}
auto pivot = std::next(right, -1);
if (size == 2 && *pivot < *left) {
std::iter_swap(left, pivot);
}
auto wall = left;
auto curr = left;
while (curr != right) {
if (*curr < *pivot) {
std::iter_swap(wall, curr);
wall++;
}
curr++;
}
std::iter_swap(pivot, wall);
quickSort(vec, left, wall);
quickSort(vec, wall + 1, right);
}
int main() {
std::vector<int> myVec = { 6, 5, 2, 3, 2, 4, 34, 2434, 251, 4, 12, 4, 5,
634, 523, 5, 4, 353, 3, 5, 345, 7, 86786, 8, 7, 9, 1 };
quickSort(myVec, myVec.begin(), myVec.end());
return 0;
}
c++ quick-sort
I've seen other implementations around, but they seem pretty complicated. This seems to work for me, but is there anything I'm missing? Also, any tips on how I can improve the quality of the code is appreciated.
#include <iostream>
#include <vector>
template<typename Iter>
void quickSort(std::vector<typename Iter::value_type>& vec, Iter left,
Iter right) {
auto size = std::distance(left, right);
if (size <= 1) {
return;
}
auto pivot = std::next(right, -1);
if (size == 2 && *pivot < *left) {
std::iter_swap(left, pivot);
}
auto wall = left;
auto curr = left;
while (curr != right) {
if (*curr < *pivot) {
std::iter_swap(wall, curr);
wall++;
}
curr++;
}
std::iter_swap(pivot, wall);
quickSort(vec, left, wall);
quickSort(vec, wall + 1, right);
}
int main() {
std::vector<int> myVec = { 6, 5, 2, 3, 2, 4, 34, 2434, 251, 4, 12, 4, 5,
634, 523, 5, 4, 353, 3, 5, 345, 7, 86786, 8, 7, 9, 1 };
quickSort(myVec, myVec.begin(), myVec.end());
return 0;
}
c++ quick-sort
c++ quick-sort
edited Mar 8 at 8:20
asked Mar 8 at 3:06
Jarrett Johnson
387
387
I seem to remember having seen quicksort before, even Lomuto partitioning, and a tag for things done time and again: reinventing-the-wheel.
– greybeard
Mar 8 at 7:45
add a comment |
I seem to remember having seen quicksort before, even Lomuto partitioning, and a tag for things done time and again: reinventing-the-wheel.
– greybeard
Mar 8 at 7:45
I seem to remember having seen quicksort before, even Lomuto partitioning, and a tag for things done time and again: reinventing-the-wheel.
– greybeard
Mar 8 at 7:45
I seem to remember having seen quicksort before, even Lomuto partitioning, and a tag for things done time and again: reinventing-the-wheel.
– greybeard
Mar 8 at 7:45
add a comment |
2 Answers
2
active
oldest
votes
up vote
4
down vote
accepted
You don't need to pass the vector itself.
left
andright
iterators provide all the necessary information.
That said, you pass a correct
right
: too often people pass it as.end() - 1
which indeed leads to unnecessary complications.
Partitioning is an important algorithm on its own right and deserves to be factored out.
The lines
if (size == 2 && *pivot < *left) {
std::iter_swap(left, pivot);
}
serve no purpose. The code works fine without them.
The trickiest part is achieving best performance.
The poor choice of pivot may result in quadratic time complexity. In a professional implementation choosing pivot is the most complex part.
In general, C++ is very good in eliminating tail recursion, but in this particular case it may use some help. Specifically, you'd want to recurse into smaller partition, and iterate over the larger one.
Another optimization is a timely switch to insertion sort. Instead of descending all the way to
size <= 1
it is beneficial to stop recursion earlier (say, whensize <= 16
). Once the recursions are completed, the range is almost sorted, and insertion sort runs in linear time.
Thank you for your answer. Really insightful. I had to also change the first if statement to if (size <= 2)return; For some reason, instead of throwing a run-time error, Visual Studio just crashes...
– Jarrett Johnson
Mar 8 at 6:09
The special case forsize == 2
serves no purpose while missing the terminalreturn
.
– greybeard
Mar 8 at 7:50
add a comment |
up vote
2
down vote
Additions to vnp's take:
- code shall be documented. You may find (and justify) (succinct) presentations like yours in print media, where it doesn't easily get separated from the explanations due - with program code, there's copy&paste. A programming language may or may not have a standard for documenting purpose and limits of a construct. I'm not aware of such for "the C-family", I use&recommend doxygen.
- must read: templaterex' How to Implement Classic Sorting Algorithms in Modern C++
- vnp implied you'd not want to recurse into the larger partition -
that is a matter of correctness even more than experiencing (intolerable) run time quadratic in the number of items to sort:
in the worst case, you'd nest one call per item, possibly hitting a limit on stack space or nesting depth. - this worst case occurs if picking
pivot
as shown for (almost) pre-ordered items - a deplorably, even disagreeably frequent use case.
reduce the visual impact of special casing:
if (size < QUICK_LIMIT) {
if (size <= 1) {
return;
auto other = std::next(left);
if (2 == size && *other < *left) {
std::iter_swap(left, other);
return;
}
// handle 2 < size < QUICK_LIMIT
}
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f189103%2fquick-sort-on-c-vector%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
4
down vote
accepted
You don't need to pass the vector itself.
left
andright
iterators provide all the necessary information.
That said, you pass a correct
right
: too often people pass it as.end() - 1
which indeed leads to unnecessary complications.
Partitioning is an important algorithm on its own right and deserves to be factored out.
The lines
if (size == 2 && *pivot < *left) {
std::iter_swap(left, pivot);
}
serve no purpose. The code works fine without them.
The trickiest part is achieving best performance.
The poor choice of pivot may result in quadratic time complexity. In a professional implementation choosing pivot is the most complex part.
In general, C++ is very good in eliminating tail recursion, but in this particular case it may use some help. Specifically, you'd want to recurse into smaller partition, and iterate over the larger one.
Another optimization is a timely switch to insertion sort. Instead of descending all the way to
size <= 1
it is beneficial to stop recursion earlier (say, whensize <= 16
). Once the recursions are completed, the range is almost sorted, and insertion sort runs in linear time.
Thank you for your answer. Really insightful. I had to also change the first if statement to if (size <= 2)return; For some reason, instead of throwing a run-time error, Visual Studio just crashes...
– Jarrett Johnson
Mar 8 at 6:09
The special case forsize == 2
serves no purpose while missing the terminalreturn
.
– greybeard
Mar 8 at 7:50
add a comment |
up vote
4
down vote
accepted
You don't need to pass the vector itself.
left
andright
iterators provide all the necessary information.
That said, you pass a correct
right
: too often people pass it as.end() - 1
which indeed leads to unnecessary complications.
Partitioning is an important algorithm on its own right and deserves to be factored out.
The lines
if (size == 2 && *pivot < *left) {
std::iter_swap(left, pivot);
}
serve no purpose. The code works fine without them.
The trickiest part is achieving best performance.
The poor choice of pivot may result in quadratic time complexity. In a professional implementation choosing pivot is the most complex part.
In general, C++ is very good in eliminating tail recursion, but in this particular case it may use some help. Specifically, you'd want to recurse into smaller partition, and iterate over the larger one.
Another optimization is a timely switch to insertion sort. Instead of descending all the way to
size <= 1
it is beneficial to stop recursion earlier (say, whensize <= 16
). Once the recursions are completed, the range is almost sorted, and insertion sort runs in linear time.
Thank you for your answer. Really insightful. I had to also change the first if statement to if (size <= 2)return; For some reason, instead of throwing a run-time error, Visual Studio just crashes...
– Jarrett Johnson
Mar 8 at 6:09
The special case forsize == 2
serves no purpose while missing the terminalreturn
.
– greybeard
Mar 8 at 7:50
add a comment |
up vote
4
down vote
accepted
up vote
4
down vote
accepted
You don't need to pass the vector itself.
left
andright
iterators provide all the necessary information.
That said, you pass a correct
right
: too often people pass it as.end() - 1
which indeed leads to unnecessary complications.
Partitioning is an important algorithm on its own right and deserves to be factored out.
The lines
if (size == 2 && *pivot < *left) {
std::iter_swap(left, pivot);
}
serve no purpose. The code works fine without them.
The trickiest part is achieving best performance.
The poor choice of pivot may result in quadratic time complexity. In a professional implementation choosing pivot is the most complex part.
In general, C++ is very good in eliminating tail recursion, but in this particular case it may use some help. Specifically, you'd want to recurse into smaller partition, and iterate over the larger one.
Another optimization is a timely switch to insertion sort. Instead of descending all the way to
size <= 1
it is beneficial to stop recursion earlier (say, whensize <= 16
). Once the recursions are completed, the range is almost sorted, and insertion sort runs in linear time.
You don't need to pass the vector itself.
left
andright
iterators provide all the necessary information.
That said, you pass a correct
right
: too often people pass it as.end() - 1
which indeed leads to unnecessary complications.
Partitioning is an important algorithm on its own right and deserves to be factored out.
The lines
if (size == 2 && *pivot < *left) {
std::iter_swap(left, pivot);
}
serve no purpose. The code works fine without them.
The trickiest part is achieving best performance.
The poor choice of pivot may result in quadratic time complexity. In a professional implementation choosing pivot is the most complex part.
In general, C++ is very good in eliminating tail recursion, but in this particular case it may use some help. Specifically, you'd want to recurse into smaller partition, and iterate over the larger one.
Another optimization is a timely switch to insertion sort. Instead of descending all the way to
size <= 1
it is beneficial to stop recursion earlier (say, whensize <= 16
). Once the recursions are completed, the range is almost sorted, and insertion sort runs in linear time.
answered Mar 8 at 5:49
vnp
38.4k13096
38.4k13096
Thank you for your answer. Really insightful. I had to also change the first if statement to if (size <= 2)return; For some reason, instead of throwing a run-time error, Visual Studio just crashes...
– Jarrett Johnson
Mar 8 at 6:09
The special case forsize == 2
serves no purpose while missing the terminalreturn
.
– greybeard
Mar 8 at 7:50
add a comment |
Thank you for your answer. Really insightful. I had to also change the first if statement to if (size <= 2)return; For some reason, instead of throwing a run-time error, Visual Studio just crashes...
– Jarrett Johnson
Mar 8 at 6:09
The special case forsize == 2
serves no purpose while missing the terminalreturn
.
– greybeard
Mar 8 at 7:50
Thank you for your answer. Really insightful. I had to also change the first if statement to if (size <= 2)return; For some reason, instead of throwing a run-time error, Visual Studio just crashes...
– Jarrett Johnson
Mar 8 at 6:09
Thank you for your answer. Really insightful. I had to also change the first if statement to if (size <= 2)return; For some reason, instead of throwing a run-time error, Visual Studio just crashes...
– Jarrett Johnson
Mar 8 at 6:09
The special case for
size == 2
serves no purpose while missing the terminal return
.– greybeard
Mar 8 at 7:50
The special case for
size == 2
serves no purpose while missing the terminal return
.– greybeard
Mar 8 at 7:50
add a comment |
up vote
2
down vote
Additions to vnp's take:
- code shall be documented. You may find (and justify) (succinct) presentations like yours in print media, where it doesn't easily get separated from the explanations due - with program code, there's copy&paste. A programming language may or may not have a standard for documenting purpose and limits of a construct. I'm not aware of such for "the C-family", I use&recommend doxygen.
- must read: templaterex' How to Implement Classic Sorting Algorithms in Modern C++
- vnp implied you'd not want to recurse into the larger partition -
that is a matter of correctness even more than experiencing (intolerable) run time quadratic in the number of items to sort:
in the worst case, you'd nest one call per item, possibly hitting a limit on stack space or nesting depth. - this worst case occurs if picking
pivot
as shown for (almost) pre-ordered items - a deplorably, even disagreeably frequent use case.
reduce the visual impact of special casing:
if (size < QUICK_LIMIT) {
if (size <= 1) {
return;
auto other = std::next(left);
if (2 == size && *other < *left) {
std::iter_swap(left, other);
return;
}
// handle 2 < size < QUICK_LIMIT
}
add a comment |
up vote
2
down vote
Additions to vnp's take:
- code shall be documented. You may find (and justify) (succinct) presentations like yours in print media, where it doesn't easily get separated from the explanations due - with program code, there's copy&paste. A programming language may or may not have a standard for documenting purpose and limits of a construct. I'm not aware of such for "the C-family", I use&recommend doxygen.
- must read: templaterex' How to Implement Classic Sorting Algorithms in Modern C++
- vnp implied you'd not want to recurse into the larger partition -
that is a matter of correctness even more than experiencing (intolerable) run time quadratic in the number of items to sort:
in the worst case, you'd nest one call per item, possibly hitting a limit on stack space or nesting depth. - this worst case occurs if picking
pivot
as shown for (almost) pre-ordered items - a deplorably, even disagreeably frequent use case.
reduce the visual impact of special casing:
if (size < QUICK_LIMIT) {
if (size <= 1) {
return;
auto other = std::next(left);
if (2 == size && *other < *left) {
std::iter_swap(left, other);
return;
}
// handle 2 < size < QUICK_LIMIT
}
add a comment |
up vote
2
down vote
up vote
2
down vote
Additions to vnp's take:
- code shall be documented. You may find (and justify) (succinct) presentations like yours in print media, where it doesn't easily get separated from the explanations due - with program code, there's copy&paste. A programming language may or may not have a standard for documenting purpose and limits of a construct. I'm not aware of such for "the C-family", I use&recommend doxygen.
- must read: templaterex' How to Implement Classic Sorting Algorithms in Modern C++
- vnp implied you'd not want to recurse into the larger partition -
that is a matter of correctness even more than experiencing (intolerable) run time quadratic in the number of items to sort:
in the worst case, you'd nest one call per item, possibly hitting a limit on stack space or nesting depth. - this worst case occurs if picking
pivot
as shown for (almost) pre-ordered items - a deplorably, even disagreeably frequent use case.
reduce the visual impact of special casing:
if (size < QUICK_LIMIT) {
if (size <= 1) {
return;
auto other = std::next(left);
if (2 == size && *other < *left) {
std::iter_swap(left, other);
return;
}
// handle 2 < size < QUICK_LIMIT
}
Additions to vnp's take:
- code shall be documented. You may find (and justify) (succinct) presentations like yours in print media, where it doesn't easily get separated from the explanations due - with program code, there's copy&paste. A programming language may or may not have a standard for documenting purpose and limits of a construct. I'm not aware of such for "the C-family", I use&recommend doxygen.
- must read: templaterex' How to Implement Classic Sorting Algorithms in Modern C++
- vnp implied you'd not want to recurse into the larger partition -
that is a matter of correctness even more than experiencing (intolerable) run time quadratic in the number of items to sort:
in the worst case, you'd nest one call per item, possibly hitting a limit on stack space or nesting depth. - this worst case occurs if picking
pivot
as shown for (almost) pre-ordered items - a deplorably, even disagreeably frequent use case.
reduce the visual impact of special casing:
if (size < QUICK_LIMIT) {
if (size <= 1) {
return;
auto other = std::next(left);
if (2 == size && *other < *left) {
std::iter_swap(left, other);
return;
}
// handle 2 < size < QUICK_LIMIT
}
edited 18 mins ago
albert
1071
1071
answered Mar 8 at 9:37
greybeard
1,3491521
1,3491521
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f189103%2fquick-sort-on-c-vector%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I seem to remember having seen quicksort before, even Lomuto partitioning, and a tag for things done time and again: reinventing-the-wheel.
– greybeard
Mar 8 at 7:45