web-dev-qa-db-ja.com

JavaScriptクイックソート

私はしばらくWebを見回していて、一般的に使用されているクイックソートの「安定した」事実上の実装があるかどうか疑問に思っていますか?私は自分で書くことができますが、なぜ車輪を再発明するのですか...

18
flavour404

Decorate-sort-undecorateパターンを使用して、不安定なソートを簡単に「安定化」できます

function stableSort(v, f)
{
    if (f === undefined) {
        f = function(a, b) {
            a = ""+a; b = ""+b;
            return a < b ? -1 : (a > b ? 1 : 0);
        }
    }
    var dv = [];
    for (var i=0; i<v.length; i++) {
        dv[i] = [v[i], i];
    }
    dv.sort(function(a, b){
              return f(a[0], b[0]) || (a[1] - b[1]);
            });
    for (var i=0; i<v.length; i++) {
        v[i] = dv[i][0];
    }
}

アイデアは、2つの要素が「同じ」にならないように最後の並べ替え条件としてインデックスを追加することです。他のすべてが同じである場合、元のインデックスが識別要素になります。

13
6502
  1. オブジェクトを配列に入れます。
  2. Array.sort() を呼び出します。とても速いです。

    _var array = [3,7,2,8,2,782,7,29,1,3,0,34];
    array.sort();
    console.log(array); // prints [0, 1, 2, 2, 29, 3, 3, 34, 7, 7, 782, 8]
    _

なぜそれが辞書式順序で印刷されるのですか?これがデフォルトでarray.sort()が機能する方法です。 コンパレータ機能を提供しない場合。これを修正しましょう。

_    var array = [3,7,2,8,2,782,7,29,1,3,0,34];
    array.sort(function (a, b)
    {
        return a-b;
    });
    console.log(array); // prints [0, 1, 2, 2, 3, 3, 7, 7, 8, 29, 34, 782]
_
16
Matt Ball

クイックソート(再帰)

function quicksort(array) {
  if (array.length <= 1) {
    return array;
  }

  var pivot = array[0];
  
  var left = []; 
  var right = [];

  for (var i = 1; i < array.length; i++) {
    array[i] < pivot ? left.Push(array[i]) : right.Push(array[i]);
  }

  return quicksort(left).concat(pivot, quicksort(right));
};

var unsorted = [23, 45, 16, 37, 3, 99, 22];
var sorted = quicksort(unsorted);

console.log('Sorted array', sorted);
15

同等の機能

もののように見えるFunctional Javascriptを祝って

現時点では、特にES6 +の素晴らしい構文糖の追加を考えると。アロー関数とデストラクチャリング私は、クイックソート関数の非常にクリーンで短い機能的同等物を提案します。パフォーマンスをテストしたり、組み込みのクイックソート機能と比較したりはしていませんが、クイックソートの実際の使い方を理解するのに苦労している人には役立つかもしれません。その宣言的な性質を考えると、非常に簡単に見ることができますwhatが反対に起こっているhow動作します。

これはコメントのないJSBinバージョンです https://jsbin.com/zenajud/edit?js,console

function quickSortF(arr) {
    // Base case
    if (!arr.length) return []

    // This is a ES6 addition, it uses destructuring to pull out the 
    // first value and the rest, similar to how other functional languages
    // such as Haskell, Scala do it. You can then use the variables as 
    // normal below
    const [head, ...tail] = arr,
          // here we are using the arrow functions, and taking full 
          // advantage of the concise syntax, the verbose version of
          // function(e) => { return e < head } is the same thing
          // so we end up with the partition part, two arrays,
          // one smaller than the pivot and one bigger than the 
          // pivot, in this case is the head variable
          left = tail.filter( e => e < head),
          right = tail.filter( e => e >= head)

       // this is the conquer bit of divide-and-conquer
       // recursively run through each left and right array
       // until we hit the if condition which returns an empty
       // array. These results are all connected using concat,
       // and we get our sorted array.
       return quickSortF(left).concat(head, quickSortF(right))           

}

const q7 = quickSortF([11,8,14,3,6,2,7]) 
//[2, 3, 6, 7, 8, 11, 14]
const q8 =  quickSortF([11,8,14,3,6,2,1, 7])
//[1, 2, 3, 6, 7, 8, 11, 14]
const q9 = quickSortF([16,11,9,7,6,5,3, 2])
//[2, 3, 5, 6, 7, 9, 11, 16]

console.log(q7,q8,q9)

何が起こっているのかすでに明確でない場合、コメントは十分に提供する必要があります。実際のコードはコメントなしで非常に短く、私がnotセミコロンのファンであることにお気づきかもしれません。 :)

9
Faktor 10

このブログでは http://www.nczonline.net/blog/2012/11/27/computer-science-in-javascript-quicksort/ Array.sortがクイックソートで実装されていることを指摘していますまたは、ソートを内部的にマージします。

Quicksortは一般に効率的で高速であると考えられているため、23項目を超える配列のArray.prototype.sort()の実装としてV8で使用されます。 23項目未満の場合、V8は挿入sort [2]を使用します。マージソートは、効率的かつ高速であるためクイックソートの競合相手ですが、安定しているという利点もあります。これが、MozillaとSafariがArray.prototype.sort()の実装にそれを使用する理由です。

また、Array.sortを使用する場合、Chromeではtrueまたはfalseの代わりに-1 0 1を返す必要があります。

arr.sort(function(a,b){
  return a<b;
});
// maybe--> [21, 0, 3, 11, 4, 5, 6, 7, 8, 9, 10, 1, 2, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22]
arr.sort(function(a,b){
  return a > b ? -1 : a < b ? 1 : 0;
});
// --> [22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
7
jinwei
var array = [8, 2, 5, 7, 4, 3, 12, 6, 19, 11, 10, 13, 9];
quickSort(array, 0, array.length -1);
document.write(array);


function  quickSort(arr, left, right)
{
        var i = left;
        var j = right;
        var tmp;
        pivotidx = (left + right) / 2; 
        var pivot = parseInt(arr[pivotidx.toFixed()]);  
        /* partition */
        while (i <= j)
        {
                while (parseInt(arr[i]) < pivot)
                i++;
                while (parseInt(arr[j]) > pivot)
                        j--;
                if (i <= j)
                {
                        tmp = arr[i];
                        arr[i] = arr[j];
                        arr[j] = tmp;
                        i++;
                        j--;
                }
        }

        /* recursion */
        if (left < j)
                quickSort(arr, left, j);
        if (i < right)
                quickSort(arr, i, right);
        return arr;
}
4
Andriy

ES6レストを使用して、以下を展開します。

smaller = (a, list) => list.filter(x => x <= a)
larger = (a, list) => list.filter(x => x > a)
qsort = ([x, ...list]) => (!isNaN(x))
    ? [...qsort(smaller(x, list)), x, ...qsort(larger(x, list))]
    : []
3
GChamon

クイックソート(ES6)

function quickSort(arr) {
    if (arr.length < 2) {
        return arr;
    }
    const pivot = arr[Math.floor(Math.random() * arr.length)];

    let left = [];
    let right = [];
    let equal = [];

    for (let val of arr) {
        if (val < pivot) {
            left.Push(val);
        } else if (val > pivot) {
            right.Push(val);
        } else {
            equal.Push(val);
        }
    }
    return [
        ...quickSort(left),
        ...equal,
        ...quickSort(right)
    ];
}

少数の注記:

  • ランダムなピボットは、データがソートされている場合でもアルゴリズムを効率的に保ちます。
  • 使用するのと同じくらいArray.filterを使用する代わりにfor ofループ、ここでの回答の一部と同様に、時間の複雑さが増します(Array.reduceを代わりに使用できます)
2
Lior Elrom

このアルゴリズムは、ChromeのArray.prototype.sortのデフォルト実装とほぼ同じ速度で機能します。

function quickSort(t){
    _quickSort(t,0,t.length-1,0,t.length-1);
}

function _quickSort(t, s, e, sp, ep){   
    if( s>=e )  return;
    while( sp<ep && t[sp]<t[e] ) sp++;  
    if( sp==e )
        _quickSort(t,s,e-1,s,e-1);  
    else{
        while(t[ep]>=t[e] && sp<ep ) ep--;      
        if( sp==ep ){
            var temp = t[sp];
            t[sp] = t[e];
            t[e] = temp;
            if( s!=sp ){
                _quickSort(t,s,sp-1,s,sp-1);
            }
            _quickSort(t,sp+1,e,sp+1,e);            
        }else{
            var temp = t[sp];
            t[sp] = t[ep];
            t[ep] = temp;
            _quickSort(t,s,e,sp+1,ep);
        }
    }
}

quickSort時間(ミリ秒):738
javaScriptSort時間(ミリ秒):603

var m = randTxT(5000,500,-1000,1000);
VS(m);

function VS(M){
    var t;
    t = Date.now();
    for(var i=0;i<M.length;i++){
        quickSort(M[i].slice());
    }console.log("quickSort time (ms): "+(Date.now()-t));

    t = Date.now();
    for(var i=0;i<M.length;i++){
        M[i].slice().sort(compare);
    }console.log("javaScriptSort time (ms): "+(Date.now()-t));
}

function compare(a, b) {
    if( a<b )
        return -1;
    if( a==b )
        return 0;
    return 1;
}

function randT(n,min,max){
    var res = [], i=0;
    while( i<n ){
        res.Push( Math.floor(Math.random()*(max-min+1)+min) );
        i++;
    }
    return res; 
}
function randTxT(n,m,min,max){
    var res = [], i=0;
    while( i<n ){
        res.Push( randT(m,min,max) );
        i++;
    }
    return res; 
}
1
user3870075

さらに別のクイックソートのデモ。特定の理由なしに配列の中央をピボットとして使用します。

const QuickSort = function (A, start, end) {
    // 
    if (start >= end) {
        return;
    }
    // return index of the pivot
    var pIndex = Partition(A, start, end);
    // partition left side
    QuickSort(A, start, pIndex - 1);
    // partition right side
    QuickSort(A, pIndex + 1, end);
}

const Partition = function (A, start, end) {
    if (A.length > 1 == false) {
        return 0;
    }
    let pivotIndex = Math.ceil((start + end) / 2);
    let pivotValue = A[pivotIndex];
    for (var i = 0; i < A.length; i++) {
        var leftValue = A[i];
        // 
        if (i < pivotIndex) {
            if (leftValue > pivotValue) {
                A[pivotIndex] = leftValue;
                A[i] = pivotValue;
                pivotIndex = i;
            }
        }
        else if (i > pivotIndex) {
            if (leftValue < pivotValue) {
                A[pivotIndex] = leftValue;
                A[i] = pivotValue;
                pivotIndex = i;
            }
        }
    }
    return pivotIndex;

}

const QuickSortTest = function () {
    const arrTest = [3, 5, 6, 22, 7, 1, 8, 9];
    QuickSort(arrTest, 0, arrTest.length - 1);
    console.log("arrTest", arrTest);
}
// 
QuickSortTest();
1
Teoman shipahi

スリムバージョン:

function swap(arr,a,b){
    let temp = arr[a]
    arr[a] = arr[b]
    arr[b] = temp
    return 1
}

function qS(arr, first, last){
    if(first > last) return

    let p = first
    for(let i = p; i < last; i++)
        if(arr[i] < arr[last]) 
            p += swap(arr, i, p)

    swap(arr, p, last)

    qS(arr, first, p - 1)
    qS(arr, p + 1, last)
}

ランダムな値の配列でテストされ、常にArray.sort()よりも高速であるようです

0
Matteo Marongiu

これだよ !!!

function typeCheck(a, b){
  if(typeof a === typeof b){
    return true;
  }else{
    return false;
  }
}

function qSort(arr){
  if(arr.length === 0){
    return [];
  }

  var leftArr = [];
  var rightArr = [];
  var pivot = arr[0];

  for(var i = 1; i < arr.length; i++){
    if(typeCheck(arr[i], parseInt(0))){
      if(arr[i] < pivot){
        leftArr.Push(arr[i]);
      }else { rightArr.Push(arr[i]) } 
    }else{
      throw new Error("All must be integers");
    }
  }

  return qSort(leftArr).concat(pivot, qSort(rightArr));

}

var test = [];

for(var i = 0; i < 10; i++){
  test[i] = Math.floor(Math.random() * 100 + 2);
}

console.log(test);
console.log(qSort(test));
0
adib

この非変異機能QuickSortはどうですか?

const quicksort = (arr, comp, iArr = arr) => {
  if (arr.length < 2) {
    return arr;
  }
  const isInitial = arr.length === iArr.length;
  const arrIndexes = isInitial ? Object.keys(arr) : arr;
  const compF = typeof comp === 'function'
  ? comp : (left, right) => left < right ? -1 : right < left ? 1 : 0;
  const [pivotIndex, ...indexesSansPivot] = arrIndexes;
  const indexSortReducer = isLeftOfPivot => [
    (acc, index) => isLeftOfPivot === (compF(iArr[index], iArr[pivotIndex]) === -1)
    ? acc.concat(index) : acc,
    []
  ];
  const ret = quicksort(indexesSansPivot.reduce(...indexSortReducer(true)), compF, iArr)
  .concat(pivotIndex)
  .concat(quicksort(indexesSansPivot.reduce(...indexSortReducer(false)), compF, iArr));
  return isInitial ? ret.reduce((acc, index) => acc.concat([arr[index]]), []) : ret;
};

おまけとして、プロパティ/プロパティごとのオブジェクトの配列のソートを可能にするオプションの比較関数をサポートし、より大きな値/オブジェクトを処理する場合に遅くなることはありません。

最初に元の配列キーをすばやくソートし、次に元の配列のソートされたコピーを返します。

0
MHelander
quickSort = (array, left, right) => {
    if (left >= right) {
        return;
    }
    const pivot = array[Math.trunc((left + right) / 2)];
    const index = partition(array, left, right, pivot);
    quickSort(array, left, index - 1);
    quickSort(array, index, right);
}

partition = (array, left, right, pivot) => {
    while (left <= right) {
        while (array[left] < pivot) {
            left++;
        }
        while (array[right] > pivot) {
            right--;
        }
        if (left <= right) {
            swap(array, left, right);
            left++;
            right--;
        }
    }
    return left;
}

swap = (array, left, right) => {
    let temp = array[left];
    array[left] = array[right];
    array[right] = temp;
}
let array = [1, 5, 2, 3, 5, 766, 64, 7678, 21, 567];
quickSort(array, 0, array.length - 1);
console.log('final Array: ', array);
0
skduhariya