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;
}









share|improve this question
























  • 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















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;
}









share|improve this question
























  • 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













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;
}









share|improve this question















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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


















  • 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










2 Answers
2






active

oldest

votes

















up vote
4
down vote



accepted












  • You don't need to pass the vector itself. left and right 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, when size <= 16). Once the recursions are completed, the range is almost sorted, and insertion sort runs in linear time.









share|improve this answer





















  • 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


















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
    }







share|improve this answer























    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
    });


    }
    });














    draft saved

    draft discarded


















    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 and right 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, when size <= 16). Once the recursions are completed, the range is almost sorted, and insertion sort runs in linear time.









    share|improve this answer





















    • 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















    up vote
    4
    down vote



    accepted












    • You don't need to pass the vector itself. left and right 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, when size <= 16). Once the recursions are completed, the range is almost sorted, and insertion sort runs in linear time.









    share|improve this answer





















    • 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













    up vote
    4
    down vote



    accepted







    up vote
    4
    down vote



    accepted








    • You don't need to pass the vector itself. left and right 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, when size <= 16). Once the recursions are completed, the range is almost sorted, and insertion sort runs in linear time.









    share|improve this answer














    • You don't need to pass the vector itself. left and right 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, when size <= 16). Once the recursions are completed, the range is almost sorted, and insertion sort runs in linear time.










    share|improve this answer












    share|improve this answer



    share|improve this answer










    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 for size == 2 serves no purpose while missing the terminal return.
      – 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










    • The special case for size == 2 serves no purpose while missing the terminal return.
      – 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












    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
      }







    share|improve this answer



























      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
        }







      share|improve this answer

























        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
          }







        share|improve this answer














        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
          }








        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited 18 mins ago









        albert

        1071




        1071










        answered Mar 8 at 9:37









        greybeard

        1,3491521




        1,3491521






























            draft saved

            draft discarded




















































            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.




            draft saved


            draft discarded














            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





















































            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







            Popular posts from this blog

            Quarter-circle Tiles

            build a pushdown automaton that recognizes the reverse language of a given pushdown automaton?

            Mont Emei