Implementasi algoritma pengurutan yang stabil dan cepat dalam javascript

104

Saya mencari untuk mengurutkan array sekitar 200-300 objek, mengurutkan pada kunci tertentu dan urutan tertentu (asc / desc). Urutan hasil harus konsisten dan stabil.

Apa algoritme terbaik untuk digunakan, dan dapatkah Anda memberikan contoh implementasinya dalam javascript?

Terima kasih!

William Casarin
sumber
6
Karena setidaknya jenis array Chrome tampaknya tidak stabil, mengandalkan jenis array bawaan bukanlah pilihan untuk Anda.
Nosredna
Untuk meringkas: Saya menggunakan semacam penggabungan yang digulung tangan karena ketidakkonsistenan stabilitas Array.sort antara browser modern (terutama chrome tidak menerapkan jenis stabil pada saat komentar ini). Terima kasih semuanya atas bantuan Anda!
William Casarin
Apa yang kami maksud dengan urutan "stabil"?
mowwwalker
2
@mowwwalker Pengurutan stabil adalah pengurutan di mana semua item dengan nilai pengurutan yang sama dibiarkan dalam urutan yang sama seperti di koleksi asli. en.wikipedia.org/wiki/Sorting_algorithm#Stability
Kornelije Petak
Untuk menjawab "apa algoritme terbaik untuk digunakan", kami perlu mengetahui apakah ada struktur yang mendasari data Anda. Banyak dari jawaban di bawah ini hanya berbicara tentang menggunakan merge sort, atau quick sort, pada kenyataannya itu tergantung pada datanya. Bukan masalah sederhana untuk hanya menjawab, saya tidak akan mengatakannya. Google beberapa algoritma penyortiran dan baca tentang mereka untuk melihat apa yang saya maksud. TimSort dan Radix Sort adalah dua contoh bagus yang saya sarankan untuk dibaca.
akan

Jawaban:

114

Dimungkinkan untuk mendapatkan pengurutan yang stabil dari fungsi pengurutan yang tidak stabil.

Sebelum menyortir, Anda mendapatkan posisi semua elemen. Dalam kondisi pengurutan Anda, jika kedua elemen sama, maka Anda mengurutkan berdasarkan posisinya.

Tada! Anda punya jenis yang stabil.

Saya telah menulis artikel tentang itu di blog saya jika Anda ingin tahu lebih banyak tentang teknik ini dan cara menerapkannya: http://blog.vjeux.com/2010/javascript/javascript-sorting-table.html

Vjeux
sumber
32
Bisakah Anda memberikan jawabannya di sini daripada memposting dan menautkan ke blog Anda! Terima kasih
Mohammad Kermani
4
Tautan ke solusi diperbolehkan, tetapi harap pastikan jawaban Anda berguna tanpanya: tambahkan konteks di sekitar tautan sehingga sesama pengguna Anda akan tahu apa itu dan mengapa itu ada, lalu kutip bagian paling relevan dari halaman Anda ' menautkan ulang jika halaman target tidak tersedia. Jawaban yang tidak lebih dari sebuah tautan dapat dihapus.
Samuel Liew
@Vjeux karena ini akan menjadi populer, apakah Anda keberatan menempelkan kode yang relevan ke jawaban ini? Ini akan sangat membantu! Terima kasih!
William Casarin
34

Karena Anda mencari sesuatu yang stabil, jenis penggabungan harus dilakukan.

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

Kode dapat ditemukan di situs web di atas:

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

EDIT:

Menurut posting ini , sepertinya Array.Sort dalam beberapa implementasi menggunakan semacam gabungan.

kemiller2002
sumber
++ Merge sort adalah favorit saya. Sederhana dan stabil tanpa kasus terburuk yang buruk.
Mike Dunlavey
Tautan ke situs web sedang tidak
aktif
menemukan situs web baru sebagai contoh.
kemiller2002
7
Catatan: Array#shiftmungkin bekerja dalam waktu O (n) dan begitu juga mergedi O (n * n).
4esn0k
22

Versi yang lebih pendek dari hal yang sama menggunakan fitur ES2017 seperti fungsi panah dan penghancuran:

Fungsi

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)

Ini menerima array input dan membandingkan fungsi:

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

Ini juga mengembalikan array baru daripada membuat semacam di tempat seperti fungsi Array.sort () bawaan .

Uji

Jika kita mengambil inputlarik berikut , awalnya diurutkan berdasarkan 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 }
]

Kemudian urutkan dengan heightmenggunakan stableSort:

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

Hasil dalam:

// 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 }
]

Namun mengurutkan inputlarik yang sama menggunakan Array.sort()bawaan (di Chrome / NodeJS):

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

Pengembalian:

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

Sumber daya

Memperbarui

Array.prototype.sort sekarang stabil di V8 v7.0 / Chrome 70!

Sebelumnya, V8 menggunakan QuickSort yang tidak stabil untuk array dengan lebih dari 10 elemen. Sekarang, kami menggunakan algoritma TimSort yang stabil.

sumber

simo
sumber
1
The stableSortFungsi merupakan solusi benar-benar hebat!
lhermann
16

Saya tahu bahwa pertanyaan ini telah dijawab untuk beberapa waktu, tetapi saya kebetulan memiliki implementasi penyortiran gabungan yang stabil untuk Array dan jQuery di clipboard saya, jadi saya akan membagikannya dengan harapan bahwa beberapa pencari di masa mendatang mungkin akan menganggapnya berguna.

Ini memungkinkan Anda untuk menentukan fungsi perbandingan Anda sendiri seperti biasanya Array.sort implementasi .

Penerapan

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

Contoh Penggunaan

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"];
Justin Force
sumber
4
Perhatikan bahwa ini bertentangan dengan jenis asli, yang berfungsi di tempatnya, yang berarti ini tidak bisa begitu saja dihentikan.
Eric
3
Mungkin stabil, tetapi metode ini sekitar 20 kali lebih lambat dari native array.sort, lihat pengujian di sini untuk string dan integer -> jsfiddle.net/QC64j
davidkonrad
2
Tentu saja ini lebih lambat dari jenis asli — ini bukan asli. Itu tidak mungkin. Itu juga benar bahwa itu tidak melakukan penyortiran di tempat. Itu juga tidak mungkin (kasus terbaik Anda membuat salinan lalu menimpa aslinya). Selain itu, mengembalikan salinan yang diurutkan lebih merupakan JavaScript-y meskipun JavaScript memiliki perilaku pengurutan asli sendiri. Fungsi ini juga dipanggil mergeSortdan tidak sort, jadi tidak dimaksudkan sebagai pengganti pengganti. Terkadang Anda hanya memerlukan penyortiran gabungan yang stabil, misalnya saat mengurutkan tabel berdasarkan kolom.
Justin Force
1
Salah, Urutan Asli Node ditulis dalam javascript. Sangat mungkin untuk algoritme yang diprogram dalam javascript untuk mengungguli jenis aslinya. Saya membangun algoritme penyortiran sepenuhnya dalam javascript (sejenis jenis gabungan adaptif) yang Kremes / krim / Kreams The native quicksort in node. Maksud dari komentar ini adalah untuk menunjukkan bahwa native tidak menjadi masalah dalam kasus javascript karena algoritma pengurutan ditulis dalam javascript dan bukan dalam beberapa bahasa yang lebih tinggi seperti c ++. Buktinya ada di sini: github.com/nodejs/node/blob/master/deps/v8/src/js/array.js
ahitt6345
2
Bagi siapa saja yang menginginkan solusi drop-in di tempat yang jauh lebih cepat daripada implementasi ini, lihat jawaban saya .
Patrick Roberts
10

Anda dapat menggunakan polyfill berikut untuk mengimplementasikan pengurutan stabil apa pun penerapan aslinya, berdasarkan pernyataan yang dibuat dalam jawaban ini :

// 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) + '%')

Menjalankan tes kinerja yang diterapkan di atas, stableSort()tampaknya berjalan pada sekitar 57% kecepatansort() di Chrome versi 59-61.

Penggunaan .bind(compareFunction)pada fungsi anonim yang dibungkus dalam stableSort()meningkatkan kinerja relatif tersebut dari sekitar 38% dengan menghindari referensi cakupan yang tidak diperlukan kecompareFunction pada setiap panggilan dengan menetapkannya ke konteks sebagai gantinya.

Memperbarui

Mengubah operator terner menjadi korsleting logis, yang cenderung berkinerja lebih baik rata-rata (tampaknya membuat perbedaan efisiensi 2-3%).

Patrick Roberts
sumber
5

Berikut ini mengurutkan array yang disediakan, dengan menerapkan fungsi bandingkan yang disediakan, mengembalikan perbandingan indeks asli saat fungsi bandingkan mengembalikan 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;
}

Contoh di bawah ini mengurutkan larik nama berdasarkan nama belakang, dengan mempertahankan urutan nama belakang yang sama:

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

Philip Bijker
sumber
Tidak stabil untuk tipe primitif atau elemen duplikat dalam larik. jQuery.expando.split( "" ).sort( ( a, b ) => 0 ).join( "" ) === jQuery.expando
martian
3

Berikut implementasi yang stabil. Ini berfungsi dengan menggunakan pengurutan asli, tetapi dalam kasus di mana elemen dibandingkan sama, Anda memutuskan hubungan menggunakan posisi indeks asli.

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

tes non-menyeluruh

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

jawaban lain menyinggung ini, tetapi tidak memposting codez.

Tapi, itu tidak cepat menurut patokan saya . Saya memodifikasi impl jenis gabungan untuk menerima fungsi komparator kustom, dan itu jauh lebih cepat.

kambing
sumber
Apakah tolok ukur Anda benar? Tampaknya, "stableSort" Anda tidak mengubah larik masukan, jenis lain - lakukan, dan karena Anda tidak membuat ulang "arr" selama "penyiapan", jenis lain telah mengurutkan larik ....
4esn0k
@ 4esn0k apakah Anda salah membaca? Saya mengatakan func stableSort saya TIDAK cepat.
kambing
@gota, ah, maafkan aku
4esn0k
3

Anda juga dapat menggunakan Timsort. Ini adalah algoritme yang sangat rumit (400+ baris, karenanya tidak ada kode sumber di sini), jadi lihat deskripsi Wikipedia atau gunakan salah satu implementasi JavaScript yang ada:

Implementasi GPL 3 . Dikemas sebagai Array.prototype.timsort. Tampaknya merupakan penulisan ulang kode Java yang tepat.

Implementasi domain publik Dimaksudkan sebagai tutorial, kode contoh hanya menunjukkan penggunaannya dengan bilangan bulat.

Timsort adalah hibrida yang sangat optimal dari mergesort dan shuffle sort dan merupakan algoritma pengurutan default di Python dan Java (1.7+). Ini adalah algoritme yang rumit, karena menggunakan algoritme berbeda untuk banyak kasus khusus. Tetapi sebagai hasilnya, ini sangat cepat dalam berbagai keadaan.

David Leppik
sumber
1

Sebuah mergeSort sederhana dari 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));
demosthenes
sumber
0

Saya harus mengurutkan array multidimensi dengan kolom arbitrer, dan kemudian dengan kolom lain. Saya menggunakan fungsi ini untuk mengurutkan:

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

Perhatikan bahwa ary.sort tidak pernah mengembalikan nol, yang mana beberapa implementasi fungsi "sort" membuat keputusan yang mungkin tidak tepat.

Ini juga sangat cepat.

alfadog67
sumber
0

Inilah cara Anda memperluas objek Array default JS dengan metode prototipe yang menggunakan MERGE SORT . Metode ini memungkinkan pengurutan pada kunci tertentu (parameter pertama) dan urutan tertentu ('asc' / 'desc' sebagai parameter kedua)

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

Anda dapat mengujinya sendiri dengan melepaskan cuplikan di atas ke konsol browser Anda, lalu mencoba:

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]

Atau urutkan berdasarkan bidang tertentu dalam larik objek:

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
sumber
0

Jadi saya membutuhkan jenis yang stabil untuk aplikasi React + Redux saya, dan jawaban Vjeux di sini membantu saya. Namun, solusi (generik) saya tampaknya berbeda dari yang lain yang saya lihat sejauh ini, jadi saya membagikannya jika ada orang lain yang memiliki kasus penggunaan yang cocok:

  • Saya benar-benar hanya ingin memiliki sesuatu yang mirip dengan sort() API, di mana saya dapat mengirimkan fungsi komparator.
  • Terkadang saya bisa mengurutkan di tempat, dan terkadang data saya tidak dapat diubah (karena Redux) dan saya memerlukan salinan yang diurutkan. Jadi saya membutuhkan fungsi penyortiran yang stabil untuk setiap kasus penggunaan.
  • ES2015.

Solusi saya adalah membuat array yang diketik indices, kemudian menggunakan fungsi perbandingan untuk mengurutkan ini indeks berdasarkan array yang akan diurutkan. Kemudian kita dapat menggunakan indicessortir untuk mengurutkan larik asli atau membuat salinan terurut dalam sekali jalan. Jika itu membingungkan, pikirkan seperti ini: di mana Anda biasanya meneruskan fungsi perbandingan seperti:

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

Anda sekarang menggunakan:

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

Kecepatan itu bagus; itu pada dasarnya adalah algoritma pengurutan built-in, ditambah dua lintasan linier pada akhirnya, dan satu lapisan tambahan dari overhead tipuan penunjuk.

Senang menerima umpan balik tentang pendekatan ini. Inilah implementasi lengkap saya:

/**
 * - `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;
}
Pekerjaan
sumber
0

Saya tahu ini sudah banyak dijawab. Saya hanya ingin melanjutkan posting implementasi TS cepat untuk siapa saja yang mendarat di sini mencari itu.

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

Metode ini tidak lagi berjalan di tempat, seperti yang dilakukan jenis asli. Saya juga ingin menunjukkan bahwa ini bukan yang paling efisien. Ia menambahkan dua loop dari urutan O (n). meskipun sort sendiri kemungkinan besar adalah O (n log (n)) jadi kurang dari itu.

Beberapa solusi yang disebutkan lebih berkinerja, berpikir ini mungkin kode yang lebih sedikit, juga menggunakan internal Array.prototype.sort.

(Untuk solusi Javascript, cukup hapus semua jenis)

Mathias
sumber
0

Menurut blog dev v8 dan caniuse.com Array.sort sudah stabil seperti yang dipersyaratkan oleh spek di browser modern, jadi Anda tidak perlu menggulung solusi Anda sendiri. Satu-satunya pengecualian yang bisa saya lihat adalah Edge, yang akan segera beralih ke chromium dan mendukungnya juga.

akaltar
sumber
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]);

Appani Kaushik
sumber
-1

Counting Sort lebih cepat daripada merge sort (ini bekerja dalam waktu O (n)) dan dimaksudkan untuk digunakan pada integer.

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
}

Contoh:

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
Jericho West
sumber
12
Beberapa hal yang perlu diperhatikan: 1. Urutan penghitungan hanya berfungsi dengan baik dalam ruang bilangan padat. (Coba urutkan array [1, 2e9, 1e9]) 2. Jangan menulis untuk loop seperti while loop. 3. Jangan menambahkan sesuatu secara acak ke namespace Matematika. 4. Anda mungkin ingin mempertimbangkan untuk berteman dengan titik koma.
Domi
Juga, jika array memiliki nilai duplikat, itu akan berjalan selamanya. Misalnya, array [3, 1, 3]hash ke [undefined, 1, undefined, 3]. Kami mendapatkan dua nilai yang tidak ditentukan, sementara algoritme mengharapkan ada tiga di antaranya.
MKPS