私はしばらくWebを見回していて、一般的に使用されているクイックソートの「安定した」事実上の実装があるかどうか疑問に思っていますか?私は自分で書くことができますが、なぜ車輪を再発明するのですか...
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つの要素が「同じ」にならないように最後の並べ替え条件としてインデックスを追加することです。他のすべてが同じである場合、元のインデックスが識別要素になります。
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]
_
クイックソート(再帰)
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);
もののように見える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セミコロンのファンであることにお気づきかもしれません。 :)
このブログでは 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]
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;
}
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))]
: []
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
を代わりに使用できます)このアルゴリズムは、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;
}
さらに別のクイックソートのデモ。特定の理由なしに配列の中央をピボットとして使用します。
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();
スリムバージョン:
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()よりも高速であるようです
これだよ !!!
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));
この非変異機能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;
};
おまけとして、プロパティ/プロパティごとのオブジェクトの配列のソートを可能にするオプションの比較関数をサポートし、より大きな値/オブジェクトを処理する場合に遅くなることはありません。
最初に元の配列キーをすばやくソートし、次に元の配列のソートされたコピーを返します。
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);