QuickSort.h 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // SPDX-FileCopyrightText: 2022 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. #include <Jolt/Core/InsertionSort.h>
  5. JPH_NAMESPACE_BEGIN
  6. /// Helper function for QuickSort, will move the pivot element to inMiddle.
  7. template <typename Iterator, typename Compare>
  8. inline void QuickSortMedianOfThree(Iterator inFirst, Iterator inMiddle, Iterator inLast, Compare inCompare)
  9. {
  10. // This should be guaranteed because we switch over to insertion sort when there's 32 or less elements
  11. JPH_ASSERT(inFirst != inMiddle && inMiddle != inLast);
  12. if (inCompare(*inMiddle, *inFirst))
  13. swap(*inFirst, *inMiddle);
  14. if (inCompare(*inLast, *inFirst))
  15. swap(*inFirst, *inLast);
  16. if (inCompare(*inLast, *inMiddle))
  17. swap(*inMiddle, *inLast);
  18. }
  19. /// Helper function for QuickSort using the Ninther method, will move the pivot element to inMiddle.
  20. template <typename Iterator, typename Compare>
  21. inline void QuickSortNinther(Iterator inFirst, Iterator inMiddle, Iterator inLast, Compare inCompare)
  22. {
  23. // Divide the range in 8 equal parts (this means there are 9 points)
  24. auto diff = (inLast - inFirst) >> 3;
  25. auto two_diff = diff << 1;
  26. // Median of first 3 points
  27. Iterator mid1 = inFirst + diff;
  28. QuickSortMedianOfThree(inFirst, mid1, inFirst + two_diff, inCompare);
  29. // Median of second 3 points
  30. QuickSortMedianOfThree(inMiddle - diff, inMiddle, inMiddle + diff, inCompare);
  31. // Median of third 3 points
  32. Iterator mid3 = inLast - diff;
  33. QuickSortMedianOfThree(inLast - two_diff, mid3, inLast, inCompare);
  34. // Determine the median of the 3 medians
  35. QuickSortMedianOfThree(mid1, inMiddle, mid3, inCompare);
  36. }
  37. /// Implementation of the quick sort algorithm. The STL version implementation is not consistent across platforms.
  38. template <typename Iterator, typename Compare>
  39. inline void QuickSort(Iterator inBegin, Iterator inEnd, Compare inCompare)
  40. {
  41. // Implementation based on https://en.wikipedia.org/wiki/Quicksort using Hoare's partition scheme
  42. // Loop so that we only need to do 1 recursive call instead of 2.
  43. for (;;)
  44. {
  45. // If there's less than 2 elements we're done
  46. auto num_elements = inEnd - inBegin;
  47. if (num_elements < 2)
  48. return;
  49. // Fall back to insertion sort if there are too few elements
  50. if (num_elements <= 32)
  51. {
  52. InsertionSort(inBegin, inEnd, inCompare);
  53. return;
  54. }
  55. // Determine pivot
  56. Iterator pivot_iterator = inBegin + ((num_elements - 1) >> 1);
  57. QuickSortNinther(inBegin, pivot_iterator, inEnd - 1, inCompare);
  58. auto pivot = *pivot_iterator;
  59. // Left and right iterators
  60. Iterator i = inBegin;
  61. Iterator j = inEnd;
  62. for (;;)
  63. {
  64. // Find the first element that is bigger than the pivot
  65. while (inCompare(*i, pivot))
  66. i++;
  67. // Find the last element that is smaller than the pivot
  68. do
  69. --j;
  70. while (inCompare(pivot, *j));
  71. // If the two iterators crossed, we're done
  72. if (i >= j)
  73. break;
  74. // Swap the elements
  75. swap(*i, *j);
  76. // Note that the first while loop in this function should
  77. // have been do i++ while (...) but since we cannot decrement
  78. // the iterator from inBegin we left that out, so we need to do
  79. // it here.
  80. ++i;
  81. }
  82. // Include the middle element on the left side
  83. j++;
  84. // Check which partition is smaller
  85. if (j - inBegin < inEnd - j)
  86. {
  87. // Left side is smaller, recurse to left first
  88. QuickSort(inBegin, j, inCompare);
  89. // Loop again with the right side to avoid a call
  90. inBegin = j;
  91. }
  92. else
  93. {
  94. // Right side is smaller, recurse to right first
  95. QuickSort(j, inEnd, inCompare);
  96. // Loop again with the left side to avoid a call
  97. inEnd = j;
  98. }
  99. }
  100. }
  101. /// Implementation of quick sort algorithm without comparator.
  102. template <typename Iterator>
  103. inline void QuickSort(Iterator inBegin, Iterator inEnd)
  104. {
  105. std::less<> compare;
  106. QuickSort(inBegin, inEnd, compare);
  107. }
  108. JPH_NAMESPACE_END