QuickSort.h 3.8 KB

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