การใช้อัลกอริทึมการเรียงลำดับที่เสถียรอย่างรวดเร็วในจาวาสคริปต์


104

ฉันต้องการจัดเรียงอาร์เรย์ของวัตถุประมาณ 200-300 รายการโดยเรียงตามคีย์เฉพาะและลำดับที่กำหนด (asc / desc) ลำดับของผลลัพธ์ต้องสอดคล้องและคงที่

อัลกอริทึมที่ดีที่สุดที่จะใช้คืออะไรและคุณสามารถให้ตัวอย่างของการใช้งานในจาวาสคริปต์ได้หรือไม่

ขอบคุณ!


6
เนื่องจากอย่างน้อยการจัดเรียงอาร์เรย์ของ Chrome ดูเหมือนจะไม่เสถียรการใช้การจัดเรียงอาร์เรย์ในตัวจึงไม่ใช่ตัวเลือกสำหรับคุณ
Nosredna

สรุป: ฉันใช้การเรียงลำดับการผสานแบบม้วนด้วยมือเนื่องจากความไม่สอดคล้องกันของความเสถียรของ Array.sort ระหว่างเบราว์เซอร์สมัยใหม่ ขอบคุณทุกคนสำหรับความช่วยเหลือ!
William Casarin

การเรียงลำดับ "เสถียร" หมายถึงอะไร?
mowwwalker

2
@mowwwalker Stable sort คือการจัดเรียงที่รายการทั้งหมดที่มีค่าการเรียงลำดับเดียวกันจะอยู่ในลำดับเดียวกันกับในคอลเลกชันดั้งเดิม en.wikipedia.org/wiki/Sorting_algorithm#Stability
Kornelije Petak

ในการตอบว่า "อัลกอริทึมที่ดีที่สุดที่จะใช้คืออะไร" เราจำเป็นต้องทราบว่ามีโครงสร้างพื้นฐานของข้อมูลของคุณหรือไม่ คำตอบด้านล่างจำนวนมากพูดถึงการใช้การเรียงลำดับการผสานหรือการจัดเรียงอย่างรวดเร็วในความเป็นจริงมันขึ้นอยู่กับข้อมูล ไม่ใช่ปัญหาง่ายๆที่จะตอบว่าฉันจะไม่พูด Google อัลกอริทึมการเรียงลำดับสองสามอย่างและอ่านเกี่ยวกับพวกเขาเพื่อดูว่าฉันหมายถึงอะไร TimSortและRadix Sortเป็นสองตัวอย่างที่ดีที่ฉันขอแนะนำให้อ่าน
จะ

คำตอบ:


114

เป็นไปได้ที่จะได้รับการจัดเรียงที่มั่นคงจากฟังก์ชันการเรียงลำดับที่ไม่คงที่

ก่อนจัดเรียงคุณจะได้รับตำแหน่งขององค์ประกอบทั้งหมด ในเงื่อนไขการจัดเรียงของคุณหากทั้งสององค์ประกอบเท่ากันคุณจะจัดเรียงตามตำแหน่ง

ธาดา! คุณมีการจัดเรียงที่มั่นคง

ฉันได้เขียนบทความเกี่ยวกับเรื่องนี้ในบล็อกของฉันหากคุณต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับเทคนิคนี้และวิธีการใช้งาน: http://blog.vjeux.com/2010/javascript/javascript-sorting-table.html


32
โปรดให้คำตอบแทนการโพสต์และลิงก์ไปยังบล็อกของคุณได้ไหม! ขอบคุณ
Mohammad Kermani

4
ยินดีต้อนรับลิงก์ไปยังโซลูชัน แต่โปรดตรวจสอบให้แน่ใจว่าคำตอบของคุณมีประโยชน์หากไม่มีมัน: เพิ่มบริบทรอบ ๆ ลิงก์เพื่อให้ผู้ใช้เพื่อนของคุณมีความคิดว่ามันคืออะไรและทำไมจึงอยู่ที่นั่นจากนั้นจึงอ้างถึงส่วนที่เกี่ยวข้องที่สุดของเพจคุณ เชื่อมโยงใหม่ในกรณีที่หน้าเป้าหมายไม่พร้อมใช้งาน คำตอบที่มากกว่าลิงก์เพียงเล็กน้อยอาจถูกลบ
Samuel Liew

@Vjeux เนื่องจากสิ่งนี้กำลังเป็นที่นิยมคุณคิดจะวางรหัสที่เกี่ยวข้องลงในคำตอบนี้หรือไม่? มันจะช่วยได้มาก! ขอบคุณ!
William Casarin

34

เนื่องจากคุณกำลังมองหาสิ่งที่มั่นคงการเรียงลำดับการผสานควรทำ

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

รหัสสามารถพบได้ที่เว็บไซต์ด้านบน:

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

แก้ไข:

ตามโพสต์นี้ดูเหมือนว่า Array การจัดเรียงในการใช้งานบางอย่างใช้การจัดเรียงแบบผสาน


++ Merge sort เป็นรายการโปรดของฉัน มันง่ายและมั่นคงโดยไม่มีกรณีเลวร้ายที่สุด
Mike Dunlavey

ลิงก์ไปยังเว็บไซต์ไม่ทำงาน :(
ahitt6345

พบเว็บไซต์ใหม่สำหรับตัวอย่าง
kemiller2002

7
หมายเหตุ: Array#shiftอาจทำงานในเวลา O (n) และของคุณmergeใน O (n * n)
4esn0k

22

เวอร์ชันที่ค่อนข้างสั้นกว่าของสิ่งเดียวกันโดยใช้คุณสมบัติ ES2017 เช่นฟังก์ชันลูกศรและการทำลายโครงสร้าง:

ฟังก์ชัน

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)

ยอมรับอาร์เรย์อินพุตและเปรียบเทียบฟังก์ชัน:

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

นอกจากนี้ยังส่งคืนอาร์เรย์ใหม่แทนการจัดเรียงแบบแทนที่เช่นฟังก์ชันArray.sort () ในตัว

ทดสอบ

ถ้าเราใช้inputอาร์เรย์ต่อไปนี้โดยเริ่มต้นโดย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 }
]

จากนั้นจัดเรียงโดยheightใช้stableSort:

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

ผลลัพธ์ใน:

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

อย่างไรก็ตามการจัดเรียงinputอาร์เรย์เดียวกันโดยใช้บิวท์อินArray.sort()(ใน Chrome / NodeJS):

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

ผลตอบแทน:

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

ทรัพยากร

อัปเดต

Array.prototype.sort ตอนนี้เสถียรแล้วใน V8 v7.0 / Chrome 70!

ก่อนหน้านี้ V8 ใช้ QuickSort ที่ไม่เสถียรสำหรับอาร์เรย์ที่มีองค์ประกอบมากกว่า 10 รายการ ตอนนี้เราใช้อัลกอริทึม TimSort ที่เสถียร

แหล่งที่มา


1
stableSortฟังก์ชั่นเป็นทางออกที่ดีจริงๆ!
lhermann

16

ฉันรู้ว่าคำถามนี้ได้รับคำตอบมาระยะหนึ่งแล้ว แต่ฉันบังเอิญมีการใช้งานการเรียงลำดับการผสานที่เสถียรที่ดีสำหรับ Array และ jQuery ในคลิปบอร์ดของฉันดังนั้นฉันจะแบ่งปันด้วยความหวังว่าผู้ค้นหาในอนาคตจะพบว่ามีประโยชน์

ช่วยให้คุณระบุฟังก์ชันการเปรียบเทียบของคุณเองเหมือนกับการArray.sortใช้งานปกติ

การนำไปใช้

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

ตัวอย่างการใช้งาน

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
หมายเหตุ: นี่คือที่ขัดแย้งกับการจัดเรียงพื้นเมืองซึ่งทำงานในสถานที่ที่มีความหมายนี้ก็ไม่สามารถจะลดลงใน.
เอริค

3
อาจจะเสถียร แต่วิธีนี้ช้ากว่าเนทีฟประมาณ20 เท่าarray.sortดูการทดสอบที่นี่สำหรับทั้งสตริงและจำนวนเต็ม -> jsfiddle.net/QC64j
davidkonrad

2
แน่นอนว่ามันช้ากว่าการจัดเรียงแบบเนทีฟ - ไม่ใช่เนทีฟ นั่นเป็นไปไม่ได้ ยังเป็นความจริงที่ว่ามันไม่ได้จัดเรียงในสถานที่ นั่นเป็นไปไม่ได้เช่นกัน (กรณีที่ดีที่สุดคือคุณสร้างสำเนาแล้วเขียนทับต้นฉบับ) นอกจากนี้การส่งคืนสำเนาที่เรียงลำดับแล้วยังเป็น JavaScript-y มากกว่าแม้จะมีพฤติกรรมการเรียงลำดับเนทีฟของ JavaScript เองก็ตาม เรียกอีกอย่างว่าฟังก์ชันนี้mergeSortไม่ใช่sortดังนั้นจึงไม่ได้มีไว้เพื่อทดแทน บางครั้งคุณเพียงแค่ต้องการการเรียงลำดับการผสานที่มั่นคงเช่นเมื่อจัดเรียงตารางตามคอลัมน์
Justin Force

1
ไม่ถูกต้อง Node's Native sort เขียนด้วย javascript เป็นไปได้ทั้งหมดสำหรับอัลกอริทึมที่ตั้งโปรแกรมในจาวาสคริปต์เพื่อเร่งความเร็วในการจัดเรียงเนทีฟ ฉันสร้างอัลกอริทึมการเรียงลำดับทั้งหมดใน javascript (ประเภทของการจัดเรียงการผสานแบบปรับตัว) ที่ Kremes / creams / Kreams Quicksort ดั้งเดิมในโหนด ประเด็นของข้อคิดเห็นนี้คือการแสดงให้เห็นว่าเนทีฟไม่สำคัญในกรณีของจาวาสคริปต์เนื่องจากอัลกอริทึมการเรียงลำดับเขียนด้วยจาวาสคริปต์และไม่ใช่ภาษาที่สูงกว่าเช่น c ++ หลักฐานอยู่ที่นี่: github.com/nodejs/node/blob/master/deps/v8/src/js/array.js
ahitt6345

2
สำหรับคนที่ต้องการในสถานที่หล่นในการแก้ปัญหาที่เร็วกว่าการดำเนินงานนี้, ตรวจสอบคำตอบของฉัน
Patrick Roberts

10

คุณสามารถใช้โพลีฟิลล์ต่อไปนี้เพื่อใช้การจัดเรียงที่เสถียรโดยไม่คำนึงถึงการนำไปใช้งานแบบเนทีฟตามการยืนยันในคำตอบนี้ :

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

การทดสอบประสิทธิภาพที่ดำเนินการข้างต้นstableSort()ดูเหมือนว่าจะทำงานที่ความเร็วประมาณ 57% ของsort()Chrome เวอร์ชัน 59-61

การใช้.bind(compareFunction)ฟังก์ชันที่ไม่ระบุชื่อที่ห่อหุ้มไว้ภายในstableSort()ช่วยเพิ่มประสิทธิภาพการทำงานสัมพัทธ์จากประมาณ 38% โดยหลีกเลี่ยงการอ้างอิงตามขอบเขตที่ไม่จำเป็นสำหรับการcompareFunctionโทรแต่ละครั้งโดยกำหนดให้กับบริบทแทน

อัปเดต

เปลี่ยนตัวดำเนินการ ternary เป็นการลัดวงจรเชิงตรรกะซึ่งมีแนวโน้มที่จะทำงานได้ดีขึ้นโดยเฉลี่ย (ดูเหมือนจะทำให้ประสิทธิภาพแตกต่างกัน 2-3%)


5

ต่อไปนี้จะจัดเรียงอาร์เรย์ที่ให้มาโดยใช้ฟังก์ชันการเปรียบเทียบที่ให้มาโดยส่งกลับการเปรียบเทียบดัชนีเดิมเมื่อฟังก์ชันเปรียบเทียบส่งคืน 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;
}

ตัวอย่างด้านล่างจัดเรียงอาร์เรย์ของชื่อตามนามสกุลโดยคงลำดับของนามสกุลที่เท่ากัน:

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


ไม่คงที่สำหรับประเภทดั้งเดิมหรือองค์ประกอบที่ซ้ำกันในอาร์เรย์ jQuery.expando.split( "" ).sort( ( a, b ) => 0 ).join( "" ) === jQuery.expando
ดาวอังคาร

3

นี่คือการใช้งานที่เสถียร ทำงานโดยใช้การจัดเรียงแบบเนทีฟ แต่ในกรณีที่องค์ประกอบเปรียบเทียบว่าเท่ากันคุณจะทำลายความสัมพันธ์โดยใช้ตำแหน่งดัชนีเดิม

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

การทดสอบที่ไม่ละเอียดถี่ถ้วน

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

คำตอบอื่นพาดพิงถึงเรื่องนี้ แต่ไม่ได้โพสต์ codez

แต่มันไม่ได้อย่างรวดเร็วตามที่ฉันมาตรฐาน ฉันแก้ไขการเรียงลำดับการผสานเพื่อยอมรับฟังก์ชันตัวเปรียบเทียบที่กำหนดเองและมันเร็วกว่ามาก


เกณฑ์มาตรฐานของคุณถูกต้องหรือไม่? ดูเหมือนว่า "stableSort" ของคุณจะไม่แก้ไขอาร์เรย์อินพุตประเภทอื่น ๆ - ทำและในขณะที่คุณไม่ได้สร้าง "arr" ขึ้นมาใหม่ในระหว่าง "การตั้งค่า" ประเภทอื่น ๆ จะเรียงลำดับอาร์เรย์ที่เรียงไว้แล้ว ....
4esn0k

@ 4esn0k อ่านผิดรึเปล่า ฉันบอกว่า stableSort func ของฉันไม่เร็ว
แพะ

@gota อายกโทษให้ฉัน
4esn0k

3

คุณยังสามารถใช้ Timsort นี่เป็นอัลกอริทึมที่ซับซ้อนมาก (400+ บรรทัดจึงไม่มีซอร์สโค้ดที่นี่) ดังนั้นโปรดดูคำอธิบายของ Wikipediaหรือใช้หนึ่งในการใช้งาน JavaScript ที่มีอยู่:

การใช้ GPL 3การดำเนินการ บรรจุเป็น Array.prototype.timsort ดูเหมือนว่าเป็นการเขียนโค้ด Java ซ้ำทุกประการ

การใช้งานโดเมนสาธารณะหมายถึงการสอนโค้ดตัวอย่างจะแสดงเฉพาะการใช้งานกับจำนวนเต็ม

Timsort เป็นไฮบริดที่ดีที่สุดของการผสานและการเรียงลำดับแบบสุ่มและเป็นอัลกอริธึมการเรียงลำดับเริ่มต้นใน Python และใน Java (1.7+) เป็นอัลกอริทึมที่ซับซ้อนเนื่องจากใช้อัลกอริทึมที่แตกต่างกันสำหรับกรณีพิเศษต่างๆ แต่ผลที่ตามมามันเร็วมากภายใต้สถานการณ์ที่หลากหลาย


1

การผสานแบบง่าย ๆ เรียงลำดับจากhttp://www.stoimen.com/blog/2010/07/02/friday-algorithms-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

ฉันต้องจัดเรียงอาร์เรย์หลายมิติตามคอลัมน์โดยพลการแล้วตามด้วยอีกคอลัมน์หนึ่ง ฉันใช้ฟังก์ชันนี้เพื่อจัดเรียง:

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 ไม่เคยส่งกลับค่าศูนย์ซึ่งเป็นที่ที่การใช้งานฟังก์ชัน "sort" บางอย่างทำให้การตัดสินใจอาจไม่ถูกต้อง

นี่ค่อนข้างเร็วเกินไป


0

นี่คือวิธีการที่คุณสามารถขยาย JS วัตถุอาร์เรย์เริ่มต้นด้วยวิธีการต้นแบบการใช้เรียงลำดับผสาน วิธีนี้ช่วยให้สามารถจัดเรียงคีย์เฉพาะ (พารามิเตอร์แรก) และลำดับที่กำหนด ('asc' / 'desc' เป็นพารามิเตอร์ที่สอง)

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

คุณสามารถทดสอบด้วยตัวเองได้โดยวางข้อมูลโค้ดด้านบนลงในคอนโซลของเบราว์เซอร์จากนั้นลอง:

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]

หรือเรียงลำดับตามฟิลด์เฉพาะในอาร์เรย์ของวัตถุ:

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

ดังนั้นฉันจึงต้องการการจัดเรียงที่มั่นคงสำหรับแอป React + Redux ของฉันและคำตอบของ Vjeux ที่นี่ช่วยฉันได้ อย่างไรก็ตามโซลูชัน (ทั่วไป) ของฉันดูเหมือนจะแตกต่างจากวิธีอื่น ๆ ที่ฉันเห็นที่นี่ดังนั้นฉันจึงแบ่งปันในกรณีที่ใครก็ตามมีกรณีการใช้งานที่ตรงกัน:

  • ฉันแค่อยากมีอะไรที่คล้ายกับไฟล์ sort() API ซึ่งฉันสามารถส่งผ่านฟังก์ชันเปรียบเทียบได้
  • บางครั้งฉันก็ทำได้จัดเรียงในสถานที่และบางครั้งข้อมูลของฉันไม่เปลี่ยนรูป (เนื่องจาก Redux) และฉันต้องการสำเนาที่เรียงลำดับแทน ดังนั้นฉันจึงต้องการฟังก์ชันการเรียงลำดับที่เสถียรสำหรับการใช้งานแต่ละกรณี
  • ES2015.

วิธีแก้ปัญหาของฉันคือการสร้างอาร์เรย์ที่พิมพ์จากindicesนั้นใช้ฟังก์ชันเปรียบเทียบเพื่อจัดเรียงดัชนีเหล่านี้ตามอาร์เรย์ที่จะจัดเรียง จากนั้นเราสามารถใช้การเรียงลำดับindicesเพื่อจัดเรียงอาร์เรย์เดิมหรือสร้างสำเนาที่เรียงลำดับในครั้งเดียว หากเกิดความสับสนให้คิดในลักษณะนี้ซึ่งโดยปกติคุณจะส่งผ่านฟังก์ชันเปรียบเทียบเช่น:

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

ตอนนี้คุณใช้:

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

ความเร็วเป็นสิ่งที่ดี โดยพื้นฐานแล้วอัลกอริธึมการเรียงลำดับในตัวคือบวกสองเส้นตรงในตอนท้ายและอีกหนึ่งชั้นของค่าใช้จ่ายชี้ทิศทาง

ยินดีรับคำติชมเกี่ยวกับแนวทางนี้ นี่คือการใช้งานเต็มรูปแบบของฉัน:

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

ฉันรู้ว่ามีคำตอบมากมาย ฉันแค่อยากจะโพสต์การใช้ TS ฉบับย่อสำหรับทุกคนที่มาที่นี่เพื่อค้นหาสิ่งนั้น

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

เมธอดไม่ทำงานในตำแหน่งอีกต่อไปเช่นเดียวกับการจัดเรียงแบบเนทีฟ ฉันยังต้องการชี้ให้เห็นว่ามันไม่ได้มีประสิทธิภาพมากที่สุด เพิ่มสองลูปของคำสั่ง O (n) แม้ว่าการเรียงลำดับจะเป็นไปได้มากที่สุด O (n log (n)) ดังนั้นจึงน้อยกว่านั้น

โซลูชันบางอย่างที่กล่าวถึงมีประสิทธิภาพมากกว่าคิดว่าอาจใช้รหัสน้อยกว่าและใช้ภายในด้วย Array.prototype.sortด้วย

(สำหรับโซลูชัน Javascript ให้ลบทุกประเภทออก)


0

ตามที่บล็อก v8 devและcaniuse.com Array.sortมีความเสถียรแล้วตามข้อกำหนดในเบราว์เซอร์สมัยใหม่ดังนั้นคุณไม่จำเป็นต้องม้วนโซลูชันของคุณเอง ข้อยกเว้นเดียวที่ฉันเห็นคือ 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

การเรียงลำดับการนับเร็วกว่าการเรียงลำดับการรวม (ดำเนินการในเวลา O (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
}

ตัวอย่าง:

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
สิ่งที่จะกล่าวถึง: 1. การเรียงลำดับการนับใช้ได้ดีในช่องว่างที่มีจำนวนหนาแน่นเท่านั้น (ลองเรียงลำดับอาร์เรย์ [1, 2e9, 1e9]) 2. อย่าเขียนสำหรับลูปเหมือนในขณะที่ลูป 3. อย่าสุ่มเพิ่มสิ่งต่างๆในเนมสเปซคณิตศาสตร์ 4. คุณอาจต้องการหาเพื่อนด้วยเครื่องหมายอัฒภาค
Domi

นอกจากนี้ในกรณีที่อาร์เรย์มีค่าที่ซ้ำกันอาร์เรย์จะทำงานตลอดไป ยกตัวอย่างเช่นarray [3, 1, 3]hashes [undefined, 1, undefined, 3]ไป เราได้รับค่าที่ไม่ได้กำหนดสองค่าในขณะที่อัลกอริทึมคาดว่าจะมีสามค่า
MKPS
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.