merge-sort.hpp 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. #pragma once
  2. #include <algorithm>
  3. #include <nall/utility.hpp>
  4. //class: merge sort
  5. //average: O(n log n)
  6. //worst: O(n log n)
  7. //memory: O(n)
  8. //stack: O(log n)
  9. //stable?: yes
  10. //note: merge sort was chosen over quick sort, because:
  11. //* it is a stable sort
  12. //* it lacks O(n^2) worst-case overhead
  13. //* it usually runs faster than quick sort anyway
  14. //note: insertion sort is generally more performant than selection sort
  15. #define NALL_MERGE_SORT_INSERTION
  16. //#define NALL_MERGE_SORT_SELECTION
  17. namespace nall {
  18. template<typename T, typename Comparator> auto sort(T list[], uint size, const Comparator& lessthan) -> void {
  19. if(size <= 1) return; //nothing to sort
  20. //sort smaller blocks using an O(n^2) algorithm (which for small sizes, increases performance)
  21. if(size < 64) {
  22. //insertion sort requires a copy (via move construction)
  23. #if defined(NALL_MERGE_SORT_INSERTION)
  24. for(int i = 1, j; i < size; i++) {
  25. T copy(move(list[i]));
  26. for(j = i - 1; j >= 0; j--) {
  27. if(!lessthan(copy, list[j])) break;
  28. list[j + 1] = move(list[j]);
  29. }
  30. list[j + 1] = move(copy);
  31. }
  32. //selection sort requires a swap
  33. #elif defined(NALL_MERGE_SORT_SELECTION)
  34. for(uint i = 0; i < size; i++) {
  35. uint min = i;
  36. for(uint j = i + 1; j < size; j++) {
  37. if(lessthan(list[j], list[min])) min = j;
  38. }
  39. if(min != i) swap(list[i], list[min]);
  40. }
  41. #endif
  42. return;
  43. }
  44. //split list in half and recursively sort both
  45. uint middle = size / 2;
  46. sort(list, middle, lessthan);
  47. sort(list + middle, size - middle, lessthan);
  48. //left and right are sorted here; perform merge sort
  49. //use placement new to avoid needing T to be default-constructable
  50. auto buffer = memory::allocate<T>(size);
  51. uint offset = 0, left = 0, right = middle;
  52. while(left < middle && right < size) {
  53. if(!lessthan(list[right], list[left])) {
  54. new(buffer + offset++) T(move(list[left++]));
  55. } else {
  56. new(buffer + offset++) T(move(list[right++]));
  57. }
  58. }
  59. while(left < middle) new(buffer + offset++) T(move(list[left++]));
  60. while(right < size ) new(buffer + offset++) T(move(list[right++]));
  61. for(uint i = 0; i < size; i++) {
  62. list[i] = move(buffer[i]);
  63. buffer[i].~T();
  64. }
  65. memory::free(buffer);
  66. }
  67. template<typename T> auto sort(T list[], uint size) -> void {
  68. return sort(list, size, [](const T& l, const T& r) { return l < r; });
  69. }
  70. }