繁体   English   中英

JavaScript 快速排序

[英]JavaScript quicksort

我一直在寻找 web 一段时间,我想知道是否存在通常使用的快速排序的“稳定”事实上的实现? 我可以自己写,但为什么要重新发明轮子……

快速排序(递归)

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

  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]

您可以使用装饰-排序-未装饰模式轻松“稳定”不稳定的排序

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

这个想法是将索引添加为最后一个排序项,这样现在没有两个元素是“相同的”,如果其他所有元素都相同,则原始索引将成为区分因素。

功能等价物

庆祝功能性 Javascript,这似乎是最重要

目前,特别是考虑到 ES6+ 美妙的语法糖添加。 箭头函数和解构 我提出了一个非常简洁、简短的功能等价于快速排序函数。 我没有测试它的性能,也没有将它与内置的快速排序功能进行比较,但它可能会帮助那些正在努力理解快速排序的实际用途的人。 鉴于其声明性特性是很容易看到正在发生的事情是反对它是如何工作的。

这是一个没有评论的 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)

如果还不清楚发生了什么,评论应该提供足够的信息。 实际代码很短,没有注释,您可能已经注意到我不喜欢分号。 :)

在这个博客http://www.nczonline.net/blog/2012/11/27/computer-science-in-javascript-quicksort/中指出 Array.sort 是在快速排序或合并排序中实现的。

Quicksort 通常被认为是高效且快速的,因此 V8 使用它作为 Array.prototype.sort() 对超过 23 个项目的数组的实现。 对于少于 23 个项目,V8 使用插入排序[2]。 归并排序是快速排序的竞争对手,因为它也高效、快速,但还有一个额外的好处是稳定。 这就是 Mozilla 和 Safari 使用它来实现 Array.prototype.sort() 的原因。

并且在使用 Array.sort 时,您应该在 Chrome 中返回 -1 0 1 而不是 true 或 false。

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]

快速排序 (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 )。

 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))]
    : []

该算法的运行速度几乎与 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);
        }
    }
}

快速排序时间(毫秒):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();

我真的想过这个问题。 所以首先我找到了正常的搜索模式并写了。

let QuickSort = (arr, low, high) => {
    if (low < high) {
        p = Partition(arr, low, high);
        QuickSort(arr, low, p - 1);
        QuickSort(arr, p + 1, high);
    }
    return arr.A;
}

let Partition = (arr, low, high) => {
    let pivot = arr.A[high];
    let i = low;
    for (let j = low; j <= high; j++) {
        if (arr.A[j] < pivot) {
            [arr.A[i], arr.A[j]] = [arr.A[j], arr.A[i]];
            i++;
        }
    }
    [arr.A[i], arr.A[high]] = [arr.A[high], arr.A[i]];
    return i;
}

let arr = { A/* POINTER */: [33, 22, 88, 23, 45, 0, 44, 11] };
let res = QuickSort(arr, 0, arr.A.length - 1);
console.log(res);

结果是[0, 11, 22, 23, 33, 44, 45, 88]但它不稳定; 所以我检查了其他答案,@6502 的想法对我来说很有趣,“两个项目不必相同”才能区分。 好吧,我有一个解决方案,但它不是最佳的。 我们可以将项目的索引保存在一个单独的数组中。 在这个想法中,内存消耗将几乎翻倍

arr.A => 数字数组

arr.I => 与 A 的每一项相关的索引

influencer => 这应该是一个非常非常小的数字; 我想用它作为一个因素来区分相似的项目。

所以我们可以像这样更改分区:

let Partition = (arr, low, high) => {
    let pivot = arr.A[high];
    let index = arr.I[high];
    let i = low;
    for (let j = low; j <= high; j++) {
        if (arr.A[j] + (arr.I[j] * influencer) < pivot + (index * influencer)) {
            [arr.A[i], arr.A[j]] = [arr.A[j], arr.A[i]];
            [arr.I[i], arr.I[j]] = [arr.I[j], arr.I[i]];
            i++;
        }
    }
    [arr.A[i], arr.A[high]] = [arr.A[high], arr.A[i]];
    [arr.I[i], arr.I[high]] = [arr.I[high], arr.I[i]];
    return i;
}

let influencer = 0.0000001;

let arr = {
    I/* INDEXES */: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
    A/* POINTER */: [33, 22, 88, 33, 23, 45, 33, 89, 44, 11]
};
let res = QuickSort(arr, 0, arr.A.length - 1);
console.log(res);

结果:

I: [19, 11, 14, 10, 13, 16, 18, 15, 12, 17],
A: [11, 22, 23, 33, 33, 33, 44, 45, 88, 89]

就是这个 !!!

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

瘦身版:

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() 快

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

最快的实现

const quickSort = array =>
  (function qsort(arr, start, end) {
    if (start >= end) return arr;
    let swapPos = start;

    for (let i = start; i <= end; i++) {
      if (arr[i] <= arr[end]) {
        [arr[swapPos], arr[i]] = [arr[i], arr[swapPos]];
        swapPos++;
      }
    }
    qsort(arr, start, --swapPos - 1);
    qsort(arr, swapPos + 1, end);

    return arr;
  })(Array.from(array), 0, array.length - 1);

更紧凑且易于理解的快速排序实现

const quicksort = arr =>
  arr.length <= 1
    ? arr
    : [
        ...quicksort(arr.slice(1).filter((el) => el < arr[0])),
        arr[0],
        ...quicksort(arr.slice(1).filter((el) => el >= arr[0])),
      ];

试试我的解决方案

const quickSort = (arr) => {

    // base case
    if(arr.length < 2) return arr;

    // recurisve case

    // pick a random pivot
    let pivotIndex = Math.floor(Math.random() * arr.length);
    let pivot = arr[pivotIndex];
    let left = [];
    let right = [];

    // make  array of the elements less than pivot and greater than it
    for(let i = 0; i < arr.length; i++) {
        if(i === pivotIndex) {
            continue;
        }

        if(arr[i] < pivot) {
            left.push(arr[i])
        } else {
            right.push(arr[i])
        }
    }
    
    // call the recursive case again
    return quickSort(left).concat([pivot], quickSort(right));
} 

当测试这个

quickSort([7, 5, 3, 2, 8, 1, 5])   // returns [[1, 2, 3, 5, 5, 7, 8]]

使用 ES6 进行快速排序,过滤和展开操作。

我们建立一个基本情况,即数组中的 0 或 1 个元素已经排序。 然后我们建立一个归纳案例,如果快速排序适用于 0 或 1 个元素,它可以适用于大小为 2 的数组。然后我们分而治之,直到递归调用我们的 function 直到我们到达调用堆栈中的基本案例以获得我们的期望的结果。

O(n log n)

const quick_sort = array => {
  if (array.length < 2) return array; // base case: arrays with 0 or 1 elements are already "sorted"
  const pivot = array[0]; // recursive case;
  const slicedArr = array.slice(1); 
  const left = slicedArr.filter(val => val <= pivot); // sub array of all elements less than pivot
  const right = slicedArr.filter(val => val > pivot); // sub array of all elements greater than pivot
  return [...quick_sort(left), pivot, ...quick_sort(right)];
 }

这个非变异功能 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;
};

作为奖励,它支持可选的比较功能,该功能可以对每个属性/属性的对象数组进行排序,并且在处理较大的值/对象时不会变慢。

首先快速排序原始数组键,然后返回原始数组的排序副本。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM