JavaScript'te hızlı ve kararlı sıralama algoritması uygulaması


104

Yaklaşık 200-300 nesneden oluşan bir diziyi belirli bir anahtara ve belirli bir sıraya göre (artan / azalan) sıralamayı arıyorum. Sonuçların sırası tutarlı ve istikrarlı olmalıdır.

Kullanılacak en iyi algoritma nedir ve bunun javascript'te uygulanmasının bir örneğini verebilir misiniz?

Teşekkürler!


6
En azından Chrome'un dizi sıralaması kararlı görünmediğinden, yerleşik dizi sıralamasına güvenmek sizin için bir seçenek değildir.
Nosredna

Özetlemek gerekirse: Modern tarayıcılar arasındaki Array.sort kararlılığı tutarsızlıkları nedeniyle elle çevrilmiş birleştirme sıralaması kullandım (esas olarak bu yorumun yapıldığı sırada Chrome kararlı bir sıralama uygulamıyordu). Yardımlarınız için herkese teşekkürler!
William Casarin

"Kararlı" sıralama ile neyi kastediyoruz?
mowwwalker

2
@mowwwalker Kararlı sıralama, aynı sıralama değerine sahip tüm öğelerin orijinal koleksiyondakiyle aynı sırada bırakıldığı bir sıralamadır. en.wikipedia.org/wiki/Sorting_algorithm#Stability
Kornelije Petak

"Kullanılacak en iyi algoritma nedir" yanıtını vermek için, verilerinizin altında yatan herhangi bir yapı olup olmadığını bilmemiz gerekir. Aşağıdaki yanıtların çoğu, sadece birleştirme sıralaması veya hızlı sıralamayı kullanmaktan bahsediyor, gerçekte verilere bağlı. Söylemeyeceğim sadece cevap vermek basit bir problem değil. Google'da birkaç sıralama algoritması bulun ve ne demek istediğimi görmek için onlar hakkında okuyun. TimSort ve Radix Sort , okumayı tavsiye ettiğim iki iyi örnek.
will

Yanıtlar:


114

Kararlı olmayan bir sıralama işlevinden kararlı bir sıralama elde etmek mümkündür.

Sıralamadan önce tüm öğelerin konumunu alırsınız. Sıralama koşulunuzda, her iki öğe de eşitse, o zaman konuma göre sıralarsınız.

Tada! Dengeli bir türün var.

Bu teknik ve nasıl uygulanacağı hakkında daha fazla bilgi edinmek istiyorsanız bloguma bununla ilgili bir makale yazdım: http://blog.vjeux.com/2010/javascript/javascript-sorting-table.html


33
Lütfen blogunuza gönderip link vermek yerine cevabı buraya yazabilir misiniz? Teşekkürler
Mohammad Kermani

4
Bir çözüme bağlantı memnuniyetle karşılanır, ancak lütfen cevabınızın o olmadan yararlı olduğundan emin olun: Bağlantının etrafına bağlam ekleyin, böylece diğer kullanıcılarınız bunun ne olduğu ve neden orada olduğu konusunda fikir sahibi olur, ardından sayfanın en alakalı bölümünü alıntı yapın ' hedef sayfanın mevcut olmaması durumunda yeniden bağlanma. Bir bağlantıdan biraz daha fazla olan cevaplar silinebilir.
Samuel Liew

@Vjeux bu popüler olmaya başladığına göre, ilgili kodu bu cevaba yapıştırabilir misiniz? Çok yardımcı olur! Teşekkürler!
William Casarin

34

Kararlı bir şey aradığınız için, birleştirme sıralaması yapmalıdır.

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

Kod, yukarıdaki web sitesinde bulunabilir:

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

DÜZENLE:

Buna göre yazı , bir birleştirme tür kullanan bazı uygulamalarda Array.Sort benziyor.


++ Birleştirme sıralaması benim favorim. En kötü durumlar olmadan basit ve kararlıdır.
Mike Dunlavey

Web sitesinin bağlantısı kapalı :(
ahitt6345

örnek için yeni bir web sitesi buldu.
kemiller2002

7
Not: Array#shiftO (n) zamanında çalışabilir ve dolayısıyla sizin mergeO (n * n) olarak çalışabilir .
4esn0k

22

Ok işlevleri ve yıkıcı gibi ES2017 özelliklerini kullanan aynı şeyin biraz daha kısa versiyonu:

Fonksiyon

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)

Giriş dizisini ve karşılaştırma işlevini kabul eder:

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

Ayrıca yerleşik Array.sort () işlevi gibi yerinde sıralama yapmak yerine yeni dizi döndürür .

Ölçek

Aşağıdaki inputdiziyi alırsak , başlangıçta şu şekilde sıralanır 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 }
]

Ardından şunu heightkullanarak sıralayın stableSort:

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

Sonuçlar:

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

Ancak aynı inputdiziyi yerleşik kullanarak sıralamak Array.sort()(Chrome / NodeJS'de):

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

İadeler:

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

Kaynaklar

Güncelleme

Array.prototype.sort artık V8 v7.0 / Chrome 70'te kararlı!

Önceden, V8 10'dan fazla eleman içeren diziler için kararsız bir QuickSort kullanıyordu. Şimdi, kararlı TimSort algoritmasını kullanıyoruz.

kaynak


1
stableSortFonksiyon gerçekten harika bir çözümdür!
lhermann

16

Bu sorunun bir süredir yanıtlandığını biliyorum, ancak panomda Array ve jQuery için iyi bir kararlı birleştirme sıralama uygulamasına sahibim, bu nedenle gelecekteki bazı araştırmacıların yararlı bulabileceği umuduyla bunu paylaşacağım.

Normal Array.sortuygulamadaki gibi kendi karşılaştırma işlevinizi belirlemenizi sağlar .

Uygulama

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

Örnek Kullanım

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
Not Bu işleri yerli tür, ile oran içinde bu anlam, bir yerde sadece düştü edilemez.
Eric

3
Belki istikrarlı, ancak bu yöntem hakkında 20 kez daha yavaş yerli daha array.sort> -, dizeleri ve tamsayılar ikisi için buraya testi görmek jsfiddle.net/QC64j
davidkonrad

2
Tabii ki yerel sıralamadan daha yavaştır - yerel değil. Bu imkansız. Yerinde bir sıralama yapmadığı da doğrudur. Bu da imkansızdır (en iyi durumda bir kopya oluşturup ardından orijinalin üzerine yazın). Ayrıca, sıralı bir kopya döndürmek, JavaScript'in kendi yerel sıralama davranışına rağmen daha JavaScript-y'dir. İşlev aynı zamanda çağrılır mergeSortve değildir sort, bu nedenle değiştirme işleminde düşüş olması amaçlanmamıştır. Bazen, örneğin tabloları sütuna göre sıralarken, yalnızca kararlı bir birleştirme sıralamasına ihtiyaç duyarsınız.
Justin Force

1
Yanlış, Node'un Yerel sıralaması javascript ile yazılmıştır. Javascript ile programlanmış bir algoritmanın yerel sıralamayı aşması tamamen mümkündür. Kremes / creams / Kreams'in düğümdeki yerel hızlı sıralaması olan tamamen javascript'te (bir tür uyarlamalı birleştirme sıralaması) bir sıralama algoritması oluşturdum. Bu yorumun amacı, javascript durumunda yerelin önemli olmadığını göstermektir çünkü sıralama algoritması c ++ gibi daha yüksek bir dilde değil javascript ile yazılmıştır. Kanıt burada: github.com/nodejs/node/blob/master/deps/v8/src/js/array.js
ahitt6345

2
Yerinde, bu uygulamadan çok daha hızlı bir çözüm isteyenler için cevabıma göz atın .
Patrick Roberts

10

Bu yanıtta yapılan iddiaya göre, yerel uygulamadan bağımsız olarak kararlı bir sıralama uygulamak için aşağıdaki çoklu dolguyu kullanabilirsiniz :

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

Yukarıda uygulanan performans testlerini stableSort()çalıştırmanın, sort()Chrome 59-61 sürümünün hızının yaklaşık% 57'sinde çalıştığı görülmektedir .

İçerideki .bind(compareFunction)sarmalanmış anonim işlevi kullanmak , bunun yerine içeriğe atayarak her çağrıda stableSort()gereksiz kapsamlı bir referanstan kaçınarak bu göreceli performansı yaklaşık% 38'den artırdı compareFunction.

Güncelleme

Üçlü operatör, ortalama olarak daha iyi performans gösterme eğiliminde olan mantıksal kısa devreye değiştirildi (verimlilikte% 2-3 fark yaratıyor gibi görünüyor).


5

Aşağıdakiler, sağlanan karşılaştırma işlevini uygulayarak, karşılaştırma işlevi 0 döndürdüğünde orijinal dizin karşılaştırmasını döndürerek sağlanan diziyi sıralar:

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

Aşağıdaki örnek, bir isim dizisini soyadına göre sıralar ve eşit soyadlarının sırasını korur:

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


İlkel türler veya dizideki yinelenen öğeler için kararlı değildir. jQuery.expando.split( "" ).sort( ( a, b ) => 0 ).join( "" ) === jQuery.expando
Marslı

3

İşte kararlı bir uygulama. Yerel sıralamayı kullanarak çalışır, ancak öğelerin eşit olarak karşılaştırıldığı durumlarda, orijinal dizin konumunu kullanarak bağları koparırsınız.

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

kapsamlı olmayan bir test

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

başka bir cevap buna ima etti, ancak kod göndermedi.

ama benim ölçütüme göre hızlı değil . Özel bir karşılaştırma işlevini kabul etmek için bir birleştirme sıralama uygulamasını değiştirdim ve çok daha hızlıydı.


Kıyaslamanız doğru mu? Görünüşe göre, "stabilSort", giriş dizisini değiştirmiyor, diğer türler - yapın ve "kurulum" sırasında "arr" yı yeniden oluşturmadığınız için, diğer türler zaten sıralanmış dizileri sıralar ....
4esn0k

@ 4esn0k yanlış mı okudunuz? StabilSort işlevimin hızlı OLMADIĞINI söyledim.
keçi

@gota, ah, pardon me
4esn0k

3

Timsort'u da kullanabilirsiniz. Bu gerçekten karmaşık bir algoritmadır (400+ satır, dolayısıyla burada kaynak kodu yoktur), bu nedenle Wikipedia'nın açıklamasına bakın veya mevcut JavaScript uygulamalarından birini kullanın:

GPL 3 uygulaması . Array.prototype.timsort olarak paketlenmiştir. Java kodunun tam olarak yeniden yazılması gibi görünüyor.

Genel etki alanı uygulaması Öğretici anlamına gelir, örnek kod yalnızca tamsayılarla kullanımını gösterir.

Timsort, son derece optimize edilmiş bir birleştirme ve karışık sıralama hibritidir ve Python ve Java'da (1.7+) varsayılan sıralama algoritmasıdır. Birçok özel durum için farklı algoritmalar kullandığı için karmaşık bir algoritmadır. Ancak sonuç olarak çok çeşitli koşullar altında son derece hızlıdır.


1

Http://www.stoimen.com/blog/2010/07/02/friday-algorithms-javascript-merge-sort/ adresinden basit bir birleştirmeSortu

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

Çok boyutlu dizileri gelişigüzel bir sütuna göre ve sonra başka bir sütuna göre sıralamak zorundayım. Bu işlevi sıralamak için kullanıyorum:

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'un hiçbir zaman sıfır döndürmediğine dikkat edin, burada "sort" işlevinin bazı uygulamaları doğru olmayabilecek kararlar verir.

Bu da oldukça hızlı.


0

MERGE SORT kullanarak bir prototip yöntemiyle JS varsayılan Array nesnesini nasıl genişletebileceğiniz aşağıda açıklanmıştır . Bu yöntem, belirli bir anahtara (ilk parametre) ve belirli bir sıraya (ikinci parametre olarak 'artan' / 'azalan') göre sıralama sağlar.

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

Yukarıdaki snippet'i tarayıcı konsolunuza bırakıp ardından şunu deneyerek bunu kendiniz test edebilirsiniz:

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]

Veya bir nesne dizisindeki belirli bir alana göre sıralayın:

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

0

Bu yüzden React + Redux uygulamam için kararlı bir türe ihtiyacım vardı ve Vjeux'un buradaki cevabı bana yardımcı oldu. Ancak, benim (genel) çözümüm şu ana kadar burada gördüğüm diğer çözümlerden farklı görünüyor, bu nedenle başka birinin eşleşen bir kullanım durumu olması durumunda onu paylaşıyorum:

  • Gerçekten sadece sort()bir karşılaştırma işlevini geçebileceğim API'ye benzer bir şeye sahip olmak istiyorum .
  • Bazen yapabilirsiniz yerinde sıralamak ve (Redux çünkü) ve ben bunun yerine sıralanmış bir kopyasına gerek bazen verilerim sabittir. Bu nedenle, her kullanım durumu için kararlı bir sıralama işlevine ihtiyacım var.
  • ES2015.

Benim çözümüm, tiplenmiş bir dizi oluşturmak indices, ardından bu dizinleri sıralanacak diziye göre sıralamak için bir karşılaştırma işlevi kullanmak . Daha sonra sıralı indicesdiziyi ya orijinal diziyi sıralamak için kullanabiliriz ya da tek geçişte sıralı bir kopya oluşturabiliriz. Bu kafa karıştırıcıysa, şu şekilde düşünün: normalde burada aşağıdaki gibi bir karşılaştırma işlevini geçersiniz:

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

Şimdi bunun yerine şunu kullanacaksınız:

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

Hız iyidir; temelde yerleşik sıralama algoritması, artı sonunda iki doğrusal geçiş ve bir ekstra işaretçi yönlendirme yükü katmanıdır.

Bu yaklaşımla ilgili geri bildirim almaktan mutluluk duyarız. İşte benim tam uygulamam:

/**
 * - `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

Bunun çok cevaplandığını biliyorum. Ben sadece buraya gelip bunu arayan herkes için hızlı bir TS uygulaması gönderisine devam etmek istedim.

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

Yöntem, yerel sıralamanın yaptığı gibi artık yerinde çalışmamaktadır. En verimli olmadığını da belirtmek isterim. O (n) sırasının iki döngüsünü ekler. sıralamanın kendisi büyük olasılıkla O (n log (n)) olduğundan daha azdır.

Bahsedilen çözümlerden bazıları daha performanslıdır, bunun daha az kod olabileceğini düşündü, yine dahili kullanıyor Array.prototype.sort.

(Bir Javascript çözümü için tüm türleri kaldırmanız yeterlidir)


0

Göre v8 dev blogunda ve caniuse.com Array.sort kendi çözümünü rulo gerek kalmaz, modern tarayıcılarda spec gereği zaten kararlıdır. Görebildiğim tek istisna, yakında kroma geçecek ve onu da destekleyecek olan 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

Sayma Sıralaması, birleştirme sıralamasından daha hızlıdır (O (n) zamanında gerçekleştirilir) ve tam sayılarda kullanılması amaçlanmıştır.

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
}

Misal:

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
Söylenecek birkaç şey: 1. Sayma sıralaması yalnızca yoğun bir sayı alanında işe yarar. ([1, 2e9, 1e9] dizisini sıralamayı deneyin) 2. Döngüler için olduğu gibi döngüler için yazmayın. 3. Math ad alanına rastgele şeyler eklemeyin. 4. Noktalı virgülle arkadaş olmayı düşünebilirsiniz.
Domi

Ayrıca, dizinin yinelenen değerlere sahip olması durumunda sonsuza kadar çalışacaktır. Örneğin, array [3, 1, 3]için karma [undefined, 1, undefined, 3]. Algoritma bunlardan üç tane olmasını beklerken, tanımlanmamış iki değer elde ederiz.
MKPS
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.