Реализация алгоритма быстрой стабильной сортировки в javascript

104

Я хочу отсортировать массив из примерно 200-300 объектов, сортируя по определенному ключу и заданному порядку (asc / desc). Порядок результатов должен быть последовательным и стабильным.

Какой алгоритм лучше всего использовать, и не могли бы вы привести пример его реализации в javascript?

Спасибо!

Уильям Касарин
источник
6
Поскольку, по крайней мере, сортировка массивов Chrome не кажется стабильной, полагаться на встроенную сортировку массивов для вас не вариант.
Nosredna
Подводя итог: я использовал ручную сортировку слиянием из-за несоответствий стабильности Array.sort между современными браузерами (в основном, хром не реализует стабильную сортировку во время этого комментария). Спасибо всем за вашу помощь!
Уильям Касарин,
Что мы подразумеваем под «стабильной» сортировкой?
mowwwalker
2
@mowwwalker Стабильная сортировка - это сортировка, при которой все элементы с одинаковым значением сортировки оставляются в том же порядке, что и в исходной коллекции. en.wikipedia.org/wiki/Sorting_algorithm#Stability
Kornelije Petak
Чтобы ответить, «какой алгоритм лучше всего использовать», нам нужно знать, есть ли у ваших данных какая-либо основная структура. Многие из приведенных ниже ответов просто говорят об использовании сортировки слиянием или быстрой сортировки, на самом деле это зависит от данных. Я бы не сказал, что это непростая проблема. Погуглите несколько алгоритмов сортировки и прочтите о них, чтобы понять, что я имею в виду. TimSort и Radix Sort - два хороших примера, о которых я бы порекомендовал прочитать.
будет

Ответы:

114

Можно получить стабильную сортировку из нестабильной функции сортировки.

Перед сортировкой вы получите расположение всех элементов. В вашем условии сортировки, если оба элемента равны, вы сортируете по позиции.

Тада! У вас стабильный сорт.

Я написал статью об этом в своем блоге, если вы хотите узнать больше об этой технике и о том, как ее реализовать: http://blog.vjeux.com/2010/javascript/javascript-sorting-table.html

Vjeux
источник
32
Не могли бы вы дать ответ здесь вместо того, чтобы публиковать и ссылаться на свой блог! Спасибо
Мохаммад Кермани
4
Ссылка на решение приветствуется, но, пожалуйста, убедитесь, что ваш ответ полезен и без нее: добавьте контекст вокруг ссылки, чтобы ваши друзья-пользователи имели некоторое представление о том, что это такое и почему оно есть, а затем процитируйте наиболее релевантную часть страницы, которую вы ' повторная ссылка на, если целевая страница недоступна. Ответы, которые представляют собой не более чем ссылку, могут быть удалены.
Сэмюэл Лью
@Vjeux, поскольку это становится популярным, не могли бы вы вставить соответствующий код в этот ответ? Это бы очень помогло! Спасибо!
Уильям Касарин
34

Поскольку вы ищете что-то стабильное, подойдет сортировка слиянием.

http://www.stoimen.com/blog/2010/07/02/friday-algorithms-javascript-merge-sort/

Код можно найти на указанном выше веб-сайте:

function mergeSort(arr)
{
    if (arr.length < 2)
        return arr;

    var middle = parseInt(arr.length / 2);
    var left   = arr.slice(0, middle);
    var right  = arr.slice(middle, arr.length);

    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right)
{
    var result = [];

    while (left.length && right.length) {
        if (left[0] <= right[0]) {
            result.push(left.shift());
        } else {
            result.push(right.shift());
        }
    }

    while (left.length)
        result.push(left.shift());

    while (right.length)
        result.push(right.shift());

    return result;
}

РЕДАКТИРОВАТЬ:

Согласно этому сообщению , похоже, что Array.Sort в некоторых реализациях использует сортировку слиянием.

kemiller2002
источник
++ Сортировка слиянием - моя любимая. Это просто и стабильно, без наихудших случаев.
Майк Данлэви,
Ссылка на сайт не работает :(
ahitt6345
нашел новый сайт для примера.
kemiller2002
7
Примечание: Array#shiftможет работать за O (n) раз, а значит, mergeза O (n * n).
4esn0k
22

Несколько более короткая версия того же самого с использованием функций ES2017, таких как стрелочные функции и деструктуризация:

Функция

var stableSort = (arr, compare) => arr
  .map((item, index) => ({item, index}))
  .sort((a, b) => compare(a.item, b.item) || a.index - b.index)
  .map(({item}) => item)

Он принимает входной массив и функцию сравнения:

stableSort([5,6,3,2,1], (a, b) => a - b)

Он также возвращает новый массив вместо того, чтобы выполнять сортировку на месте, как встроенная функция Array.sort () .

Тест

Если взять следующий inputмассив, изначально отсортированный по weight:

// sorted by weight
var input = [
  { height: 100, weight: 80 },
  { height: 90, weight: 90 },
  { height: 70, weight: 95 },
  { height: 100, weight: 100 },
  { height: 80, weight: 110 },
  { height: 110, weight: 115 },
  { height: 100, weight: 120 },
  { height: 70, weight: 125 },
  { height: 70, weight: 130 },
  { height: 100, weight: 135 },
  { height: 75, weight: 140 },
  { height: 70, weight: 140 }
]

Затем отсортируйте его, heightиспользуя stableSort:

stableSort(input, (a, b) => a.height - b.height)

Результаты в:

// Items with the same height are still sorted by weight 
// which means they preserved their relative order.
var stable = [
  { height: 70, weight: 95 },
  { height: 70, weight: 125 },
  { height: 70, weight: 130 },
  { height: 70, weight: 140 },
  { height: 75, weight: 140 },
  { height: 80, weight: 110 },
  { height: 90, weight: 90 },
  { height: 100, weight: 80 },
  { height: 100, weight: 100 },
  { height: 100, weight: 120 },
  { height: 100, weight: 135 },
  { height: 110, weight: 115 }
]

Однако сортировка того же inputмассива с использованием встроенного Array.sort()(в Chrome / NodeJS):

input.sort((a, b) => a.height - b.height)

Возврат:

var unstable = [
  { height: 70, weight: 140 },
  { height: 70, weight: 95 },
  { height: 70, weight: 125 },
  { height: 70, weight: 130 },
  { height: 75, weight: 140 },
  { height: 80, weight: 110 },
  { height: 90, weight: 90 },
  { height: 100, weight: 100 },
  { height: 100, weight: 80 },
  { height: 100, weight: 135 },
  { height: 100, weight: 120 },
  { height: 110, weight: 115 }
]

Ресурсы

Обновить

Array.prototype.sort теперь стабильна в V8 v7.0 / Chrome 70!

Раньше V8 использовал нестабильный QuickSort для массивов с более чем 10 элементами. Теперь мы используем стабильный алгоритм TimSort.

источник

симо
источник
1
stableSortФункция действительно отличное решение!
lhermann
16

Я знаю, что в течение некоторого времени на этот вопрос был дан ответ, но у меня есть хорошая стабильная реализация сортировки слиянием для Array и jQuery в моем буфере обмена, поэтому я поделюсь ею в надежде, что некоторые будущие поисковики могут найти ее полезной.

Это позволяет вам указать вашу собственную функцию сравнения, как в обычной Array.sortреализации.

Реализация

// Add stable merge sort to Array and jQuery prototypes
// Note: We wrap it in a closure so it doesn't pollute the global
//       namespace, but we don't put it in $(document).ready, since it's
//       not dependent on the DOM
(function() {

  // expose to Array and jQuery
  Array.prototype.mergeSort = jQuery.fn.mergeSort = mergeSort;

  function mergeSort(compare) {

    var length = this.length,
        middle = Math.floor(length / 2);

    if (!compare) {
      compare = function(left, right) {
        if (left < right)
          return -1;
        if (left == right)
          return 0;
        else
          return 1;
      };
    }

    if (length < 2)
      return this;

    return merge(
      this.slice(0, middle).mergeSort(compare),
      this.slice(middle, length).mergeSort(compare),
      compare
    );
  }

  function merge(left, right, compare) {

    var result = [];

    while (left.length > 0 || right.length > 0) {
      if (left.length > 0 && right.length > 0) {
        if (compare(left[0], right[0]) <= 0) {
          result.push(left[0]);
          left = left.slice(1);
        }
        else {
          result.push(right[0]);
          right = right.slice(1);
        }
      }
      else if (left.length > 0) {
        result.push(left[0]);
        left = left.slice(1);
      }
      else if (right.length > 0) {
        result.push(right[0]);
        right = right.slice(1);
      }
    }
    return result;
  }
})();

Пример использования

var sorted = [
  'Finger',
  'Sandwich',
  'sandwich',
  '5 pork rinds',
  'a guy named Steve',
  'some noodles',
  'mops and brooms',
  'Potato Chip Brand® chips'
].mergeSort(function(left, right) {
  lval = left.toLowerCase();
  rval = right.toLowerCase();

  console.log(lval, rval);
  if (lval < rval)
    return -1;
  else if (lval == rval)
    return 0;
  else
    return 1;
});

sorted == ["5 pork rinds", "a guy named Steve", "Finger", "mops and brooms", "Potato Chip Brand® chips", "Sandwich", "sandwich", "some noodles"];
Джастин Форс
источник
4
Обратите внимание, что это противоречит встроенной сортировке, которая работает на месте, а это означает, что ее нельзя просто отбросить.
Эрик
3
Может быть, стабильный, но этот метод примерно в 20 раз медленнее, чем нативный array.sort, см. Здесь тест как для строк, так и для целых чисел -> jsfiddle.net/QC64j
davidkonrad
2
Конечно, это медленнее, чем родная сортировка - она ​​не родная. Это невозможно. Также верно то, что он не выполняет сортировку по месту. Это тоже невозможно (в лучшем случае вы создаете копию, а затем перезаписываете оригинал). Кроме того, возврат отсортированной копии - это больше JavaScript-y, несмотря на собственное поведение сортировки JavaScript. Функция также вызывается mergeSortи не используется sort, поэтому она не предназначена для замены. Иногда вам просто нужна стабильная сортировка слиянием, например, при сортировке таблиц по столбцам.
Джастин Форс
1
Неправильно, родная сортировка Node написана на javascript. Вполне возможно, что алгоритм, запрограммированный на javascript, опередит собственную сортировку. Я построил алгоритм сортировки полностью на javascript (тип адаптивной сортировки слиянием), который Kremes / creams / Kreams The native quicksort in node. Смысл этого комментария - показать, что native не имеет значения в случае javascript, потому что алгоритм сортировки написан на javascript, а не на каком-либо более высоком языке, таком как c ++. Доказательство здесь: github.com/nodejs/node/blob/master/deps/v8/src/js/array.js
ahitt6345
2
Для тех, кто хочет оперативное решение, которое намного быстрее, чем эта реализация, ознакомьтесь с моим ответом .
Патрик Робертс
10

Вы можете использовать следующий полифилл для реализации стабильной сортировки независимо от собственной реализации на основе утверждения, сделанного в этом ответе :

// ECMAScript 5 polyfill
Object.defineProperty(Array.prototype, 'stableSort', {
  configurable: true,
  writable: true,
  value: function stableSort (compareFunction) {
    'use strict'

    var length = this.length
    var entries = Array(length)
    var index

    // wrap values with initial indices
    for (index = 0; index < length; index++) {
      entries[index] = [index, this[index]]
    }

    // sort with fallback based on initial indices
    entries.sort(function (a, b) {
      var comparison = Number(this(a[1], b[1]))
      return comparison || a[0] - b[0]
    }.bind(compareFunction))

    // re-map original array to stable sorted values
    for (index = 0; index < length; index++) {
      this[index] = entries[index][1]
    }
    
    return this
  }
})

// usage
const array = Array(500000).fill().map(() => Number(Math.random().toFixed(4)))

const alwaysEqual = () => 0
const isUnmoved = (value, index) => value === array[index]

// not guaranteed to be stable
console.log('sort() stable?', array
  .slice()
  .sort(alwaysEqual)
  .every(isUnmoved)
)
// guaranteed to be stable
console.log('stableSort() stable?', array
  .slice()
  .stableSort(alwaysEqual)
  .every(isUnmoved)
)

// performance using realistic scenario with unsorted big data
function time(arrayCopy, algorithm, compare) {
  var start
  var stop
  
  start = performance.now()
  algorithm.call(arrayCopy, compare)
  stop = performance.now()
  
  return stop - start
}

const ascending = (a, b) => a - b

const msSort = time(array.slice(), Array.prototype.sort, ascending)
const msStableSort = time(array.slice(), Array.prototype.stableSort, ascending)

console.log('sort()', msSort.toFixed(3), 'ms')
console.log('stableSort()', msStableSort.toFixed(3), 'ms')
console.log('sort() / stableSort()', (100 * msSort / msStableSort).toFixed(3) + '%')

Выполненные выше тесты производительности stableSort()работают примерно на 57% от скорости в sort()Chrome версии 59-61.

Использование .bind(compareFunction)обернутой анонимной функции внутри stableSort()повысило относительную производительность примерно с 38% за счет исключения ненужных ссылок на область видимости compareFunctionпри каждом вызове, назначив ее вместо этого контексту.

Обновить

Изменен тернарный оператор на логическое короткое замыкание, которое в среднем работает лучше (похоже, разница в эффективности составляет 2-3%).

Патрик Робертс
источник
5

Следующий код сортирует предоставленный массив, применяя предоставленную функцию сравнения, возвращая исходное сравнение индексов, когда функция сравнения возвращает 0:

function stableSort(arr, compare) {
    var original = arr.slice(0);

    arr.sort(function(a, b){
        var result = compare(a, b);
        return result === 0 ? original.indexOf(a) - original.indexOf(b) : result;
    });

    return arr;
}

В приведенном ниже примере массив имен сортируется по фамилии, сохраняя порядок равных фамилий:

var names = [
	{ surname: "Williams", firstname: "Mary" },
	{ surname: "Doe", firstname: "Mary" }, 
	{ surname: "Johnson", firstname: "Alan" }, 
	{ surname: "Doe", firstname: "John" }, 
	{ surname: "White", firstname: "John" }, 
	{ surname: "Doe", firstname: "Sam" }
]

function stableSort(arr, compare) {
    var original = arr.slice(0);

    arr.sort(function(a, b){
        var result = compare(a, b);
        return result === 0 ? original.indexOf(a) - original.indexOf(b) : result;
    });
	
    return arr;
}

stableSort(names, function(a, b) { 
	return a.surname > b.surname ? 1 : a.surname < b.surname ? -1 : 0;
})

names.forEach(function(name) {
	console.log(name.surname + ', ' + name.firstname);
});

Филип Бийкер
источник
Не стабильно для примитивных типов или повторяющихся элементов в массиве. jQuery.expando.split( "" ).sort( ( a, b ) => 0 ).join( "" ) === jQuery.expando
марсианин
3

Вот стабильная реализация. Он работает с использованием собственной сортировки, но в случаях, когда элементы сравниваются как равные, вы разрываете связи, используя исходную позицию индекса.

function stableSort(arr, cmpFunc) {
    //wrap the arr elements in wrapper objects, so we can associate them with their origional starting index position
    var arrOfWrapper = arr.map(function(elem, idx){
        return {elem: elem, idx: idx};
    });

    //sort the wrappers, breaking sorting ties by using their elements orig index position
    arrOfWrapper.sort(function(wrapperA, wrapperB){
        var cmpDiff = cmpFunc(wrapperA.elem, wrapperB.elem);
        return cmpDiff === 0 
             ? wrapperA.idx - wrapperB.idx
             : cmpDiff;
    });

    //unwrap and return the elements
    return arrOfWrapper.map(function(wrapper){
        return wrapper.elem;
    });
}

неполный тест

var res = stableSort([{a:1, b:4}, {a:1, b:5}], function(a, b){
    return a.a - b.a;
});
console.log(res);

другой ответ ссылался на это, но не отправлял код.

но, его не быстро , по моему эталону . Я изменил сортировку слиянием impl, чтобы принимать пользовательскую функцию компаратора, и это было намного быстрее.

козел
источник
Ваш тест верен? Кажется, ваш "stableSort" не изменяет входной массив, другие виды - делают, и, поскольку вы не воссоздали "arr" во время "установки", другие виды сортировки уже отсортировали массивы ....
4esn0k
@ 4esn0k Вы неправильно прочитали? Я сказал, что моя функция stableSort НЕ была быстрой.
goat
@gota, ах, простите меня
4esn0k
3

Вы также можете использовать Timsort. Это действительно сложный алгоритм (более 400 строк, поэтому здесь нет исходного кода), поэтому см . Описание Википедии или используйте одну из существующих реализаций JavaScript:

Реализация GPL 3 . Упакован как Array.prototype.timort. Похоже, это точная переработка кода Java.

Реализация в общественном достоянии В качестве учебного пособия пример кода показывает его использование только с целыми числами.

Timsort - это высокооптимизированный гибрид сортировки слиянием и сортировки в случайном порядке и алгоритм сортировки по умолчанию в Python и Java (1.7+). Это сложный алгоритм, поскольку он использует разные алгоритмы для многих частных случаев. Но в результате он очень быстр в самых разных обстоятельствах.

Дэвид Леппик
источник
1

Простой mergeSort из http://www.stoimen.com/blog/2010/07/02/friday-algorithms-javascript-merge-sort/

var a = [34, 203, 3, 746, 200, 984, 198, 764, 9];

function mergeSort(arr)
{
    if (arr.length < 2)
         return arr;

    var middle = parseInt(arr.length / 2);
    var left   = arr.slice(0, middle);
    var right  = arr.slice(middle, arr.length);

    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right)
{
     var result = [];

    while (left.length && right.length) {
         if (left[0] <= right[0]) {
             result.push(left.shift());
         } else {
            result.push(right.shift());
         }
    }

    while (left.length)
        result.push(left.shift());

    while (right.length)
        result.push(right.shift());

    return result;
}

console.log(mergeSort(a));
Демосфен
источник
0

Мне нужно отсортировать многомерные массивы по произвольному столбцу, а затем по другому. Я использую эту функцию для сортировки:

function sortMDArrayByColumn(ary, sortColumn){

    //Adds a sequential number to each row of the array
    //This is the part that adds stability to the sort
    for(var x=0; x<ary.length; x++){ary[x].index = x;}

    ary.sort(function(a,b){
        if(a[sortColumn]>b[sortColumn]){return 1;}
        if(a[sortColumn]<b[sortColumn]){return -1;}
        if(a.index>b.index){
            return 1;
        }
        return -1;
    });
}

Обратите внимание, что ary.sort никогда не возвращает ноль, поэтому некоторые реализации функции "sort" принимают решения, которые могут быть неправильными.

Это тоже чертовски быстро.

alfadog67
источник
0

Вот как можно расширить объект Array JS по умолчанию с помощью метода-прототипа, использующего MERGE SORT . Этот метод позволяет выполнять сортировку по определенному ключу (первый параметр) и заданному порядку («asc» / «desc» в качестве второго параметра)

Array.prototype.mergeSort = function(sortKey, direction){
  var unsortedArray = this;
  if(unsortedArray.length < 2) return unsortedArray;

  var middle = Math.floor(unsortedArray.length/2);
  var leftSubArray = unsortedArray.slice(0,middle).mergeSort(sortKey, direction);
  var rightSubArray = unsortedArray.slice(middle).mergeSort(sortKey, direction);

  var sortedArray = merge(leftSubArray, rightSubArray);
  return sortedArray;

  function merge(left, right) {
    var combined = [];
    while(left.length>0 && right.length>0){
      var leftValue = (sortKey ? left[0][sortKey] : left[0]);
      var rightValue = (sortKey ? right[0][sortKey] : right[0]);
      combined.push((direction === 'desc' ? leftValue > rightValue : leftValue < rightValue) ? left.shift() : right.shift())
    }
    return combined.concat(left.length ? left : right)
  }
}

Вы можете проверить это сами, перетащив приведенный выше фрагмент в консоль браузера, а затем попробовав:

var x = [2,76,23,545,67,-9,12];
x.mergeSort(); //[-9, 2, 12, 23, 67, 76, 545]
x.mergeSort(undefined, 'desc'); //[545, 76, 67, 23, 12, 2, -9]

Или заказать на основе определенного поля в массиве объектов:

var y = [
  {startTime: 100, value: 'cat'},
  {startTime: 5, value: 'dog'},
  {startTime: 23, value: 'fish'},
  {startTime: 288, value: 'pikachu'}
]
y.mergeSort('startTime');
y.mergeSort('startTime', 'desc');
Cumulo Nimbus
источник
0

Поэтому мне нужна была стабильная версия для моего приложения React + Redux, и ответ Vjeux здесь мне помог. Однако мое (общее) решение кажется отличным от других, которые я видел здесь, поэтому я поделюсь им на случай, если у кого-то еще есть подходящий вариант использования:

  • Я просто хочу иметь что-то похожее на sort() API, где я могу передать функцию компаратора.
  • Иногда я могу выполнить сортировку на месте, а иногда мои данные неизменяемы (из-за Redux), и вместо этого мне нужна отсортированная копия. Поэтому мне нужна стабильная функция сортировки для каждого варианта использования.
  • ES2015.

Мое решение - создать типизированный массив indices, а затем использовать функцию сравнения для сортировки этих индексов на основе массива, подлежащего сортировке. Затем мы можем использовать сортировку indicesдля сортировки исходного массива или создания отсортированной копии за один проход. Если это сбивает с толку, подумайте об этом так: где вы обычно передаете функцию сравнения, например:

(a, b) => { 
  /* some way to compare a and b, returning -1, 0, or 1 */ 
};

Вместо этого вы используете:

(i, j) => { 
  let a = arrayToBeSorted[i], b = arrayToBeSorted[j]; 
  /* some way to compare a and b, returning -1 or 1 */
  return i - j; // fallback when a == b
}

Скорость хорошая; в основном это встроенный алгоритм сортировки, плюс два линейных прохода в конце и один дополнительный уровень накладных расходов на косвенное указание.

Рад получить отзывы об этом подходе. Вот моя полная реализация:

/**
 * - `array`: array to be sorted
 * - `comparator`: closure that expects indices `i` and `j`, and then
 *   compares `array[i]` to `array[j]` in some way. To force stability,
 *   end with `i - j` as the last "comparison".
 * 
 * Example:
 * ```
 *  let array = [{n: 1, s: "b"}, {n: 1, s: "a"}, {n:0, s: "a"}];
 *  const comparator = (i, j) => {
 *    const ni = array[i].n, nj = array[j].n;
 *    return ni < nj ? -1 :
 *      ni > nj ? 1 :
 *        i - j;
 *  };
 *  stableSortInPlace(array, comparator);
 *  // ==> [{n:0, s: "a"}, {n:1, s: "b"}, {n:1, s: "a"}]
 * ```
 */
function stableSortInPlace(array, comparator) {
  return sortFromIndices(array, findIndices(array, comparator));
}

function stableSortedCopy(array, comparator){
  let indices = findIndices(array, comparator);
  let sortedArray = [];
  for (let i = 0; i < array.length; i++){
    sortedArray.push(array[indices[i]]);
  }
  return sortedArray;
}

function findIndices(array, comparator){
  // Assumes we don't have to worry about sorting more than 
  // 4 billion elements; if you know the upper bounds of your
  // input you could replace it with a smaller typed array
  let indices = new Uint32Array(array.length);
  for (let i = 0; i < indices.length; i++) {
    indices[i] = i;
  }
  // after sorting, `indices[i]` gives the index from where
  // `array[i]` should take the value from, so to sort
  // move the value at at `array[indices[i]]` to `array[i]`
  return indices.sort(comparator);
}

// If I'm not mistaken this is O(2n) - each value is moved
// only once (not counting the vacancy temporaries), and 
// we also walk through the whole array once more to check
// for each cycle.
function sortFromIndices(array, indices) {
  // there might be multiple cycles, so we must
  // walk through the whole array to check.
  for (let k = 0; k < array.length; k++) {
    // advance until we find a value in
    // the "wrong" position
    if (k !== indices[k]) {
      // create vacancy to use "half-swaps" trick,
      // props to Andrei Alexandrescu
      let v0 = array[k];
      let i = k;
      let j = indices[k];
      while (j !== k) {
        // half-swap next value
        array[i] = array[j];
        // array[i] now contains the value it should have,
        // so we update indices[i] to reflect this
        indices[i] = i;
        // go to next index
        i = j;
        j = indices[j];
      }
      // put original array[k] back in
      // and update indices
      array[i] = v0;
      indices[i] = i;
    }
  }
  return array;
}
Работа
источник
0

Я знаю, что на это было много ответов. Я просто хотел написать быструю реализацию TS для всех, кто пришел сюда в поисках этого.

export function stableSort<T>( array: T[], compareFn: ( a: T, b: T ) => number ): T[] {
    const indices = array.map( ( x: T, i: number ) => ( { element: x, index: i } ) );

    return indices.sort( ( a, b ) => {
        const order = compareFn( a.element, b.element );
        return order === 0 ? a.index - b.index : order;
    } ).map( x => x.element );
}

Этот метод больше не работает на месте, в отличие от собственной сортировки. Также хочу отметить, что он не самый эффективный. Он добавляет две петли порядка O (n). хотя сама сортировка, скорее всего, O (n log (n)), поэтому она меньше этого.

Некоторые из упомянутых решений более производительны, подумал, что это может быть меньше кода, также используя внутренние Array.prototype.sort .

(Для решения Javascript просто удалите все типы)

Матиас
источник
0

Согласно v8 девблоге и caniuse.com Array.sort уже стабильно в соответствии с требованиями спецификации в современных браузерах, так что вам не нужно свернуть собственное решение. Единственное исключение, которое я вижу, - это Edge, который скоро должен перейти на хром и поддерживать его.

акалтар
источник
0

function sort(data){
    var result=[];
    var array = data;
    const array2=data;
    const len=array2.length;
    for(var i=0;i<=len-1;i++){
    var min = Math.min.apply(Math,array)
    result.push(min);
    var index=array.indexOf(min)
    array.splice(index,1);
    }
    return result;
}   
sort([9,8,5,7,9,3,9,243,4,5,6,3,4,2,4,7,4,9,55,66,33,66]);

Аппани Кошик
источник
-1

Сортировка с подсчетом выполняется быстрее, чем сортировка слиянием (она выполняется за время O (n)), и предназначена для использования с целыми числами.

Math.counting_sort = function (m) {
    var i
    var j
    var k
    var step
    var start
    var Output
    var hash
    k = m.length
    Output = new Array ()
    hash = new Array ()
    // start at lowest possible value of m
    start = 0
    step = 1
    // hash all values
    i = 0
    while ( i < k ) {
        var _m = m[i]
        hash [_m] = _m
        i = i + 1
    }
    i = 0
    j = start
    // find all elements within x
    while ( i < k ) {
        while ( j != hash[j] ) {
            j = j + step
        }
        Output [i] = j
        i = i + 1
        j = j + step
    }
    return Output
}

Пример:

var uArray = new Array ()<br/>
var sArray = new Array ()<br/><br/>
uArray = [ 10,1,9,2,8,3,7,4,6,5 ]<br/>
sArray = Math.counting_sort ( uArray ) // returns a sorted array
Иерихон Запад
источник
12
Следует сказать несколько вещей: 1. Счетная сортировка хорошо работает только в плотном числовом пространстве. (Попробуйте отсортировать массив [1, 2e9, 1e9]) 2. Не пишите циклы for как циклы while. 3. Не добавляйте случайным образом объекты в пространство имен Math. 4. Возможно, вы захотите подружиться с точкой с запятой.
Доми
Кроме того, если в массиве есть повторяющиеся значения, он будет работать вечно. Например, array [3, 1, 3]хеши в [undefined, 1, undefined, 3]. Мы получаем два неопределенных значения, в то время как алгоритм ожидает, что их будет три.
MKPS,