Triển khai thuật toán sắp xếp ổn định nhanh chóng trong javascript


103

Tôi đang tìm cách sắp xếp một mảng khoảng 200-300 đối tượng, sắp xếp theo một khóa cụ thể và một thứ tự nhất định (asc / desc). Thứ tự kết quả phải nhất quán và ổn định.

Thuật toán tốt nhất để sử dụng là gì và bạn có thể cung cấp một ví dụ về việc triển khai nó trong javascript không?

Cảm ơn!


5
Vì ít nhất thì sắp xếp mảng của Chrome có vẻ không ổn định nên việc dựa vào sắp xếp mảng tích hợp không phải là một lựa chọn dành cho bạn.
Nosredna 15/09/09

Tóm lại: Tôi đã sử dụng sắp xếp hợp nhất cuộn bằng tay do sự không nhất quán về tính ổn định của Array.sort giữa các trình duyệt hiện đại (chủ yếu là chrome không triển khai sắp xếp ổn định tại thời điểm nhận xét này). Cảm ơn mọi người đã giúp đỡ của bạn!
William Casarin

Ý chúng tôi là gì về loại "ổn định"?
mowwwalker

2
@mowwwalker Sắp xếp ổn định là kiểu sắp xếp trong đó tất cả các mục có cùng giá trị sắp xếp được giữ nguyên theo thứ tự như trong bộ sưu tập ban đầu. vi.wikipedia.org/wiki/Sorting_algorithm#Stability
Kornelije Petak

Để trả lời "thuật toán tốt nhất để sử dụng là gì", chúng tôi cần biết liệu có bất kỳ cấu trúc cơ bản nào đối với dữ liệu của bạn hay không. Rất nhiều câu trả lời dưới đây chỉ nói về việc sử dụng sắp xếp hợp nhất, hoặc sắp xếp nhanh, trong thực tế, nó phụ thuộc vào dữ liệu. Nó không phải là một vấn đề đơn giản để trả lời tôi sẽ không nói. Google một vài thuật toán sắp xếp và đọc về chúng để xem ý tôi là gì. TimSortRadix Sort là hai ví dụ điển hình mà tôi muốn giới thiệu.
sẽ

Câu trả lời:


113

Có thể có được một chức năng sắp xếp ổn định từ một chức năng sắp xếp không ổn định.

Trước khi sắp xếp, bạn nhận được vị trí của tất cả các phần tử. Trong điều kiện sắp xếp của bạn, nếu cả hai phần tử bằng nhau, thì bạn sắp xếp theo vị trí.

Tada! Bạn đã có một sự sắp xếp ổn định.

Tôi đã viết một bài về nó trên blog của mình nếu bạn muốn biết thêm về kỹ thuật này và cách thực hiện nó: http://blog.vjeux.com/2010/javascript/javascript-sorting-table.html


32
Bạn có thể vui lòng cung cấp câu trả lời ở đây thay vì đăng và liên kết đến blog của bạn! Cảm ơn
Mohammad Kermani

4
Liên kết đến một giải pháp được hoan nghênh, nhưng hãy đảm bảo câu trả lời của bạn hữu ích nếu không có nó: thêm ngữ cảnh xung quanh liên kết để những người dùng đồng nghiệp của bạn sẽ biết nó là gì và tại sao nó ở đó, sau đó trích dẫn phần có liên quan nhất của trang bạn ' đang liên kết lại trong trường hợp trang đích không có sẵn. Các câu trả lời ít hơn một liên kết có thể bị xóa.
Samuel Liew

@Vjeux vì điều này đang trở nên phổ biến, bạn có phiền dán mã liên quan vào câu trả lời này không? Nó sẽ giúp đỡ rất nhiều! cảm ơn!
William Casarin

34

Vì bạn đang tìm kiếm thứ gì đó ổn định, nên sắp xếp hợp nhất.

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

Bạn có thể tìm thấy mã tại trang web trên:

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

BIÊN TẬP:

Theo bài đăng này , có vẻ như Array.Sort trong một số triển khai sử dụng sắp xếp hợp nhất.


++ Hợp nhất sắp xếp là yêu thích của tôi. Nó đơn giản và ổn định không có trường hợp xấu nhất nào xảy ra.
Mike Dunlavey 15/09/09

Liên kết đến trang web bị lỗi :(
ahitt6345 29/02/16

tìm thấy một trang web mới cho ví dụ.
kemiller2002 29/02/16

7
Lưu ý: Array#shiftcó thể hoạt động trong thời gian O (n) và của bạn mergetrong O (n * n).
4esn0k

22

Phiên bản tương tự ngắn hơn một chút sử dụng các tính năng của ES2017 như chức năng mũi tên và cấu trúc hủy:

Chức năng

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)

Nó chấp nhận mảng đầu vào và hàm so sánh:

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

Nó cũng trả về mảng mới thay vì sắp xếp tại chỗ như hàm Array.sort () tích hợp sẵn.

Kiểm tra

Nếu chúng ta lấy inputmảng sau , ban đầu được sắp xếp theo 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 }
]

Sau đó, sắp xếp nó bằng heightcách sử dụng stableSort:

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

Kết quả trong:

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

Tuy nhiên, sắp xếp cùng một inputmảng bằng cách sử dụng tích hợp sẵn Array.sort()(trong Chrome / NodeJS):

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

Lợi nhuận:

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

Tài nguyên

Cập nhật

Array.prototype.sort hiện đã ổn định trong V8 v7.0 / Chrome 70!

Trước đây, V8 đã sử dụng QuickSort không ổn định cho các mảng có hơn 10 phần tử. Bây giờ, chúng tôi sử dụng thuật toán TimSort ổn định.

nguồn


1
Các stableSortchức năng là một giải pháp thực sự tuyệt vời!
lhermann

16

Tôi biết rằng câu hỏi này đã được trả lời trong một thời gian, nhưng tôi tình cờ có triển khai sắp xếp hợp nhất ổn định tốt cho Array và jQuery trong khay nhớ tạm thời của mình, vì vậy tôi sẽ chia sẻ nó với hy vọng rằng một số người tìm kiếm trong tương lai có thể thấy nó hữu ích.

Nó cho phép bạn chỉ định chức năng so sánh của riêng mình giống như cách Array.sorttriển khai bình thường .

Thực hiện

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

Ví dụ sử dụng

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
Lưu ý rằng điều này trái ngược với kiểu gốc, hoạt động tại chỗ, có nghĩa là không thể bỏ qua điều này.
Eric

3
Có lẽ ổn định, nhưng phương pháp này là khoảng 20 lần chậm hơn mẹ đẻ array.sort, xem thử nghiệm ở đây cho cả chuỗi và số nguyên -> jsfiddle.net/QC64j
davidkonrad

2
Tất nhiên nó chậm hơn so với sắp xếp gốc — nó không phải là bản địa. Không thể nào. Nó cũng đúng là nó không làm một loại tại chỗ. Điều đó cũng không thể (trường hợp tốt nhất là bạn tạo một bản sao sau đó ghi đè lên bản gốc). Bên cạnh đó, việc trả về một bản sao đã được sắp xếp là JavaScript-y nhiều hơn mặc dù hành vi sắp xếp tự nhiên của JavaScript. Hàm cũng được gọi mergeSortvà không được gọi sort, vì vậy nó không nhằm mục đích thay thế nó. Đôi khi bạn chỉ cần một sắp xếp hợp nhất ổn định, ví dụ: khi sắp xếp các bảng theo cột.
Justin Force

1
Sai, sắp xếp Gốc của Node được viết bằng javascript. Hoàn toàn có thể xảy ra với một thuật toán được lập trình bằng javascript để tăng tốc độ sắp xếp gốc. Tôi đã xây dựng một thuật toán sắp xếp hoàn toàn bằng javascript (một loại sắp xếp hợp nhất thích ứng) mà Kremes / cream / Kreams Quicksort gốc trong nút. Điểm của nhận xét này là chỉ ra rằng bản địa không quan trọng trong trường hợp của javascript vì thuật toán sắp xếp được viết bằng javascript chứ không phải bằng một số ngôn ngữ cao hơn như c ++. Bằng chứng là ở đây: github.com/nodejs/node/blob/master/deps/v8/src/js/array.js
ahitt6345 29/02/16

2
Đối với bất kỳ ai muốn có một giải pháp tại chỗ, nhanh hơn nhiều so với việc triển khai này, hãy xem câu trả lời của tôi .
Patrick Roberts,

10

Bạn có thể sử dụng polyfill sau để triển khai một loại ổn định bất kể triển khai gốc là gì, dựa trên khẳng định được đưa ra trong câu trả lời này :

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

Chạy các bài kiểm tra hiệu suất được triển khai ở trên, stableSort()dường như chạy ở khoảng 57% tốc độ của sort()Chrome phiên bản 59-61.

Việc sử dụng .bind(compareFunction)trên hàm ẩn danh được bao bọc bên trong stableSort()đã tăng hiệu suất tương đối đó từ khoảng 38% bằng cách tránh tham chiếu phạm vi không cần thiết đến compareFunctiontrên mỗi cuộc gọi bằng cách chỉ định nó cho ngữ cảnh.

Cập nhật

Đã thay đổi toán tử bậc ba thành đoản mạch logic, có xu hướng hoạt động trung bình tốt hơn (dường như tạo ra sự khác biệt 2-3% về hiệu suất).


5

Sau đây sắp xếp mảng được cung cấp, bằng cách áp dụng hàm so sánh được cung cấp, trả về so sánh chỉ mục ban đầu khi hàm so sánh trả về 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;
}

Ví dụ dưới đây sắp xếp một mảng tên theo họ, giữ lại thứ tự của các họ bằng nhau:

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


Không ổn định cho các kiểu nguyên thủy hoặc các phần tử trùng lặp trong mảng. jQuery.expando.split( "" ).sort( ( a, b ) => 0 ).join( "" ) === jQuery.expando
Martian

3

Đây là một triển khai ổn định. Nó hoạt động bằng cách sử dụng sắp xếp gốc, nhưng trong trường hợp các phần tử so sánh bằng nhau, bạn phá vỡ mối quan hệ bằng cách sử dụng vị trí chỉ mục ban đầu.

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

một bài kiểm tra không kỹ lưỡng

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

một câu trả lời khác ám chỉ đến điều này, nhưng không đăng teh codez.

nhưng, nó không nhanh theo điểm chuẩn của tôi . Tôi đã sửa đổi một mô hình sắp xếp hợp nhất để chấp nhận một hàm so sánh tùy chỉnh và nó nhanh hơn nhiều.


Điểm chuẩn của bạn có chính xác không? Có vẻ, bạn "stableSort" không thay đổi mảng đầu vào, các loại khác - làm, và như bạn đã không tái tạo "arr" trong "thiết lập", các loại khác loại mảng đã được sắp xếp ....
4esn0k

@ 4esn0k bạn có đọc nhầm không? Tôi đã nói func stableSort của tôi KHÔNG nhanh.

@gota, ah, tha thứ cho tôi
4esn0k

3

Bạn cũng có thể sử dụng Timsort. Đây là một thuật toán thực sự phức tạp (hơn 400 dòng, do đó không có mã nguồn ở đây), vì vậy hãy xem mô tả của Wikipedia hoặc sử dụng một trong các triển khai JavaScript hiện có:

GPL 3 thực hiện . Được đóng gói dưới dạng Array.prototype.timsort. Có vẻ như là một bản viết lại chính xác của mã Java.

Triển khai miền công cộng Có nghĩa là một hướng dẫn, mã mẫu chỉ cho thấy việc sử dụng nó với các số nguyên.

Timsort là sự kết hợp tối ưu hóa cao giữa sắp xếp hợp nhất và sắp xếp xáo trộn và là thuật toán sắp xếp mặc định trong Python và trong Java (1.7+). Đây là một thuật toán phức tạp, vì nó sử dụng các thuật toán khác nhau cho nhiều trường hợp đặc biệt. Nhưng kết quả là nó cực kỳ nhanh trong nhiều trường hợp.


1

Một hợp nhất đơn giản Sắp xếp từ http://www.stoimen.com/blog/2010/07/02/friday-algorithm-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));

0

Tôi phải sắp xếp các mảng nhiều chiều theo một cột tùy ý, và sau đó theo một cột khác. Tôi sử dụng hàm này để sắp xếp:

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

Lưu ý rằng ary.sort không bao giờ trả về số 0, đó là nơi một số triển khai của hàm "sort" đưa ra các quyết định có thể không đúng.

Điều này cũng khá nhanh.


0

Đây là cách bạn có thể mở rộng đối tượng Mảng mặc định JS với một phương thức nguyên mẫu sử dụng MERGE SORT . Phương thức này cho phép sắp xếp theo một khóa cụ thể (tham số đầu tiên) và một thứ tự nhất định ('asc' / 'desc' là tham số thứ hai)

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

Bạn có thể tự mình kiểm tra điều này bằng cách thả đoạn mã trên vào bảng điều khiển của trình duyệt, sau đó thử:

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]

Hoặc đặt hàng dựa trên một trường cụ thể trong một mảng các đối tượng:

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

Vì vậy, tôi cần một loại ổn định cho ứng dụng React + Redux của mình và câu trả lời của Vjeux ở đây đã giúp tôi. Tuy nhiên, giải pháp (chung chung) của tôi có vẻ khác với những giải pháp khác mà tôi thấy ở đây cho đến nay, vì vậy tôi sẽ chia sẻ nó trong trường hợp bất kỳ ai khác có trường hợp sử dụng phù hợp:

  • Tôi thực sự chỉ muốn có một cái gì đó tương tự như sort()API, nơi tôi có thể chuyển một hàm so sánh.
  • Đôi khi tôi có thể sắp xếp tại chỗ và đôi khi dữ liệu của tôi là bất biến (vì Redux) và tôi cần một bản sao được sắp xếp thay thế. Vì vậy, tôi cần một chức năng sắp xếp ổn định cho từng trường hợp sử dụng.
  • ES2015.

Giải pháp của tôi là tạo một mảng đã nhập indices, sau đó sử dụng hàm so sánh để sắp xếp các chỉ số này dựa trên mảng được sắp xếp. Sau đó, chúng ta có thể sử dụng sắp xếp indicesđể sắp xếp mảng ban đầu hoặc tạo một bản sao được sắp xếp trong một lần chuyển. Nếu điều đó khó hiểu, hãy nghĩ theo cách này: nơi bạn thường truyền một hàm so sánh như:

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

Thay vào đó, bạn sử dụng:

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

Tốc độ là tốt; Về cơ bản, đó là thuật toán sắp xếp tích hợp, cộng với hai đường chuyển tuyến tính cuối cùng và một lớp bổ sung của chi phí chuyển hướng con trỏ.

Rất vui khi nhận được phản hồi về cách tiếp cận này. Đây là cách triển khai đầy đủ của tôi về nó:

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

Tôi biết điều này đã được trả lời rất nhiều. Tôi chỉ muốn tiếp tục một bài triển khai TS nhanh chóng cho bất kỳ ai đến đây tìm kiếm điều đó.

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

Phương thức này không còn chạy tại chỗ như kiểu nguyên thủy. Tôi cũng muốn chỉ ra rằng nó không phải là hiệu quả nhất. Nó thêm hai vòng lặp thứ tự O (n). mặc dù bản thân sắp xếp rất có thể là O (n log (n)) vì vậy nó ít hơn thế.

Một số giải pháp được đề cập có hiệu suất cao hơn, được cho là ít mã hơn, cũng sử dụng nội bộ Array.prototype.sort.

(Đối với giải pháp Javascript, chỉ cần loại bỏ tất cả các loại)


0

Theo blog nhà phát triển v8caniuse.com Array.sort đã ổn định theo yêu cầu của thông số kỹ thuật trong các trình duyệt hiện đại, vì vậy bạn không cần phải triển khai giải pháp của riêng mình. Ngoại lệ duy nhất tôi có thể thấy là Edge, sẽ sớm chuyển sang crom và hỗ trợ nó.


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

Sắp xếp đếm nhanh hơn sắp xếp hợp nhất (nó thực hiện trong thời gian O (n)) và nó được thiết kế để sử dụng trên số nguyên.

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
}

Thí dụ:

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
Vài điều cần nói: 1. Sắp xếp đếm chỉ hoạt động tốt trong không gian số dày đặc. (Hãy thử sắp xếp mảng [1, 2e9, 1e9]) 2. Không viết vòng lặp for như vòng lặp while. 3. Không thêm ngẫu nhiên các thứ vào không gian tên Toán học. 4. Bạn có thể muốn cân nhắc kết bạn với dấu chấm phẩy.
Domi

Ngoài ra, trong trường hợp mảng có giá trị trùng lặp, nó sẽ chạy mãi mãi. Ví dụ: array [3, 1, 3]băm thành [undefined, 1, undefined, 3]. Chúng tôi nhận được hai giá trị không phải không xác định, trong khi thuật toán hy vọng có ba giá trị trong số đó.
MKPS
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.