การเรียงลำดับ () ของ Javascript ทำงานอย่างไร


95

โค้ดต่อไปนี้จัดเรียงอาร์เรย์นี้อย่างไรให้อยู่ในลำดับตัวเลข?

var array=[25, 8, 7, 41]

array.sort(function(a,b){
  return a - b
})

ฉันรู้ว่าถ้าผลลัพธ์ของการคำนวณเป็น ...

น้อยกว่า 0 : "a" ถูกจัดเรียงให้เป็นดัชนีที่ต่ำกว่า "b"
ศูนย์: "a" และ "b" ถือว่าเท่ากันและไม่มีการเรียงลำดับ
มากกว่า 0: "b" ถูกจัดเรียงให้เป็นดัชนีที่ต่ำกว่า "a"

ฟังก์ชันการเรียกกลับการเรียงลำดับอาร์เรย์ถูกเรียกหลายครั้งในระหว่างการจัดเรียงหรือไม่

ถ้าเป็นเช่นนั้นฉันต้องการทราบว่าตัวเลขสองตัวใดถูกส่งผ่านเข้าสู่ฟังก์ชันในแต่ละครั้ง ฉันคิดว่าครั้งแรกใช้เวลา "25" (a) และ "8" (b) ตามด้วย "7" (a) และ "41" (b) ดังนั้น:

25 (a) - 8 (b) = 17 (มากกว่าศูนย์ดังนั้นจัดเรียง "b" ให้เป็นดัชนีที่ต่ำกว่า "a"): 8, 25

7 (a) - 41 (b) = -34 (น้อยกว่าศูนย์ดังนั้นจัดเรียง "a" ให้เป็นดัชนีที่ต่ำกว่า "b": 7, 41

แล้วตัวเลขทั้งสองชุดมีความสัมพันธ์กันอย่างไร?

โปรดช่วยมือใหม่ที่กำลังดิ้นรน!


5
ฉันหวังว่านี่จะทำให้เข้าใจผิดได้!
cw84

คำตอบ:


51

ฟังก์ชันการเรียกกลับการเรียงลำดับอาร์เรย์ถูกเรียกหลายครั้งในระหว่างการจัดเรียงหรือไม่

ใช่

ถ้าเป็นเช่นนั้นฉันต้องการทราบว่าตัวเลขสองตัวใดถูกส่งผ่านเข้าสู่ฟังก์ชันในแต่ละครั้ง

คุณสามารถค้นหาตัวตนของคุณได้ด้วย:

array.sort((a,b) => {
  console.log(`comparing ${a},${b}`);
  return a > b ? 1
               : a === b ? 0 
                         : -1;
});

แก้ไข

นี่คือผลลัพธ์ที่ฉันได้รับ:

25,8
25,7
8,7
25,41

8
แทนที่จะทำ console.log เพื่อ firebug หรือ html องค์ประกอบ DIV ของ .innerHTML + = "เปรียบเทียบ" + a + "," + b + "\ n";
Joshua

2
โปรดจำไว้ว่านี่เป็นเว็บไซต์ที่เหมือนวิกิและเราสามารถแก้ไขคำตอบอื่น ๆ เพื่อให้ดีขึ้นได้ :)
Pablo Fernandez

7
หมายเหตุสำหรับไวยากรณ์ ES6 ใหม่: array.sort((a,b) => a - b);เป็นไวยากรณ์ที่ถูกต้อง
Sterling Archer

1
ถ้าฟังก์ชัน sort return -ve มันจะสลับและ + แล้วมันจะไม่สลับ ??
Mahi

2
@ShekharReddy คุณยังสามารถใช้ตัวดำเนินการเพื่อเปรียบเทียบได้ ฉันได้อัปเดตคำตอบแล้ว
OscarRyz

44

ตัวแปล JavaScript มีการใช้อัลกอริทึมการจัดเรียงบางประเภทที่ติดตั้งอยู่ภายใน เรียกใช้ฟังก์ชันเปรียบเทียบหลายครั้งในระหว่างการดำเนินการเรียงลำดับ จำนวนครั้งที่เรียกใช้ฟังก์ชันเปรียบเทียบขึ้นอยู่กับอัลกอริทึมเฉพาะข้อมูลที่จะจัดเรียงและลำดับก่อนหน้าการจัดเรียง

อัลกอริทึมการจัดเรียงบางรายการทำงานได้ไม่ดีในรายการที่เรียงลำดับแล้วเนื่องจากทำให้เกิดการเปรียบเทียบมากกว่าในกรณีทั่วไป คนอื่น ๆ รับมือได้ดีกับรายการที่จัดเรียงไว้ล่วงหน้า แต่มีกรณีอื่น ๆ ที่สามารถ "หลอก" ให้ทำงานได้ไม่ดี

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

ล่าม JavaScript เฉพาะของคุณอาจใช้อัลกอริทึมเหล่านั้นอย่างใดอย่างหนึ่งหรืออย่างอื่นทั้งหมด ECMAScriptมาตรฐานไม่ได้ระบุขั้นตอนวิธีสอดคล้องการใช้งานที่ต้องดำเนินการ แม้จะปฏิเสธความต้องการความมั่นคงอย่างชัดเจน


1
FWIW Quicksort พื้นฐานเป็นหนึ่งในอัลกอริทึมที่สามารถ "หลอก" ให้ทำงานได้ไม่ดี ในรูปแบบง่ายๆจะมีประสิทธิภาพ O (N ^ 2) สำหรับรายการที่เรียงลำดับแล้วหรือย้อนกลับทั้งหมด อัลกอริทึม Quicksort ไลบรารีส่วนใหญ่มีการเพิ่มประสิทธิภาพที่ไม่ชัดเจนจำนวนมากซึ่งช่วยหลีกเลี่ยงสถานการณ์ที่เลวร้ายที่สุดเหล่านี้
อดิศักดิ์

3
JavaScriptCore ใช้ทรี AVL ในการจัดเรียงตามความจำเป็นในการทำงานตามกำหนดเมื่อเผชิญกับฟังก์ชันตัวเปรียบเทียบที่แก้ไขอาร์เรย์ที่กำลังจัดเรียง
olliej

มาตรฐาน ECMAScript ตอนนี้ต้องมีความมั่นคง
ggorlen

11

มีการเปรียบเทียบคู่ของค่าทีละคู่ คู่ที่เปรียบเทียบเป็นรายละเอียดการใช้งาน - อย่าคิดว่าจะเหมือนกันในทุกเบราว์เซอร์ การเรียกกลับอาจเป็นอะไรก็ได้ (คุณสามารถจัดเรียงสตริงหรือเลขโรมันหรืออะไรก็ได้ที่คุณสามารถสร้างฟังก์ชันที่ส่งคืน 1,0, -1)

สิ่งหนึ่งที่ควรคำนึงถึงในการจัดเรียงของ JavaScript คือไม่รับประกันว่าจะเสถียร


5

ฟังก์ชันการเรียกกลับการเรียงลำดับอาร์เรย์ถูกเรียกหลายครั้งในระหว่างการจัดเรียงหรือไม่

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


4

ฟังก์ชันการเรียกกลับการเรียงลำดับอาร์เรย์ถูกเรียกหลายครั้งในระหว่างการจัดเรียงหรือไม่

เนื่องจากนี่คือการจัดเรียงการเปรียบเทียบการรับรายการ N, ฟังก์ชั่นการโทรกลับควรมีการเรียกค่าเฉลี่ย (N * Lg N) ครั้งอย่างรวดเร็วเช่นการจัดเรียงQuicksort หากอัลกอริทึมที่ใช้เป็นแบบBubble Sortฟังก์ชันเรียกกลับจะถูกเรียกใช้โดยเฉลี่ย (N * N) ครั้ง

จำนวนขั้นต่ำของการเรียกใช้สำหรับการเรียงลำดับเปรียบเทียบคือ (N-1) และนั่นเป็นเพียงการตรวจพบรายการที่เรียงลำดับแล้วเท่านั้น (เช่นในช่วงแรกของ Bubble Sort หากไม่มีการแลกเปลี่ยนเกิดขึ้น)


3

ความรู้อย่างลึกซึ้ง

ถ้าผลลัพธ์เป็นลบ a จะเรียงลำดับก่อน b

ถ้าผลลัพธ์เป็นบวก b จะเรียงลำดับก่อน a.

หากผลลัพธ์เป็น 0 จะไม่มีการเปลี่ยนแปลงใด ๆ กับลำดับการจัดเรียงของทั้งสองค่า

บันทึก:

รหัสนี้เป็นมุมมองภายในของวิธีการจัดเรียงทีละขั้นตอน

เอาท์พุท:

let arr = [90, 1, 20, 14, 3, 55];
var sortRes = [];
var copy = arr.slice();		//create duplicate array
var inc = 0;	//inc meant increment
copy.sort((a, b) => {
	sortRes[inc] = [ a, b, a-b ];
	inc += 1;
	return a - b;
});
var p = 0;
for (var i = 0; i < inc; i++) {
	copy = arr.slice();
	copy.sort((a, b) => {
		p += 1;
		if (p <= i ) {
			return a - b;
		}
		else{
			return false;
		}
	});
	p = 0;
	console.log(copy +' \t a: '+ sortRes[i][0] +' \tb: '+ sortRes[i][1] +'\tTotal: '+ sortRes[i][2]);
}


0

เรียกใช้รหัสนี้ คุณสามารถเห็นขั้นตอนการเรียงลำดับที่แน่นอนตั้งแต่ต้นจนจบ

var array=[25, 8, 7, 41]
var count = 1;
array.sort( (a,b) => { 
console.log(`${count++}). a: ${a} | b: ${b}`);
return a-b;
});
console.log(array);

คัดลอกและวางลงในคอนโซลและเพิ่งส่งคืนไม่ได้กำหนด
iPzard

0

เพื่อช่วยชี้แจงพฤติกรรมของArray#sortและตัวเปรียบเทียบให้พิจารณาการเรียงลำดับการแทรกที่ไร้เดียงสาที่สอนในหลักสูตรการเขียนโปรแกรมเริ่มต้น:

const sort = arr => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && arr[j-1] > arr[j]; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array);
console.log("" + array);

ละเว้นทางเลือกของการจัดเรียงแทรกเป็นอัลกอริทึมที่มุ่งเน้นที่hardcodedarr[j-1] > arr[j]เปรียบเทียบ: ปัญหานี้มีสองปัญหาที่เกี่ยวข้องกับการสนทนา:

  1. ตัว>ดำเนินการถูกเรียกใช้ในคู่ขององค์ประกอบอาร์เรย์ แต่หลายสิ่งที่คุณอาจต้องการจัดเรียงเช่นวัตถุไม่ตอบสนอง>ด้วยวิธีที่สมเหตุสมผล (เช่นเดียวกันถ้าเราใช้-)
  2. แม้ว่าคุณจะทำงานกับตัวเลข แต่บ่อยครั้งที่คุณต้องการการจัดเรียงอื่น ๆ นอกเหนือจากการเรียงลำดับจากน้อยไปมากที่ได้รับการอบที่นี่

เราสามารถแก้ไขปัญหาเหล่านี้ได้โดยเพิ่มcomparefnอาร์กิวเมนต์ที่คุณคุ้นเคย:

const sort = (arr, comparefn) => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array, (a, b) => a - b);
console.log("" + array);

sort(array, (a, b) => b - a);
console.log("" + array);

const objArray = [{id: "c"}, {id: "a"}, {id: "d"}, {id: "b"}];
sort(objArray, (a, b) => a.id.localeCompare(b.id));
console.log(JSON.stringify(objArray, null, 2));

ตอนนี้กิจวัตรการเรียงลำดับที่ไร้เดียงสาเป็นเรื่องทั่วไป คุณจะเห็นได้อย่างชัดเจนเมื่อมีการเรียกกลับนี้โดยตอบข้อกังวลชุดแรกของคุณ:

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

การรันโค้ดด้านล่างแสดงให้เห็นว่าใช่ฟังก์ชันนี้ถูกเรียกใช้หลายครั้งและคุณสามารถใช้console.logเพื่อดูว่ามีการส่งผ่านหมายเลขใด:

const sort = (arr, comparefn) => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

console.log("on our version:");
const array = [3, 0, 4, 5];
sort(array, (a, b) => console.log(a, b) || (a - b));
console.log("" + array);

console.log("on the builtin:");
console.log("" + 
  [3, 0, 4, 5].sort((a, b) => console.log(a, b) || (a - b))
);

คุณถาม:

แล้วตัวเลขทั้งสองชุดมีความสัมพันธ์กันอย่างไร?

เพื่อให้แม่นยำกับคำศัพท์aและbไม่ใช่ชุดของตัวเลข - เป็นวัตถุในอาร์เรย์ (ในตัวอย่างของคุณคือตัวเลข)

ความจริงก็คือไม่สำคัญว่าจะเรียงลำดับอย่างไรเพราะขึ้นอยู่กับการนำไปใช้งาน หากฉันใช้อัลกอริธึมการเรียงลำดับที่แตกต่างจากการเรียงลำดับการแทรกตัวเปรียบเทียบอาจถูกเรียกใช้กับคู่ตัวเลขที่แตกต่างกัน แต่ในตอนท้ายของการเรียกการเรียงลำดับค่าคงที่ที่มีความสำคัญต่อโปรแกรมเมอร์ JS คืออาร์เรย์ผลลัพธ์จะเรียงตาม ตัวเปรียบเทียบสมมติว่าตัวเปรียบเทียบจะส่งคืนค่าที่เป็นไปตามสัญญาที่คุณระบุไว้ (<0 เมื่อa < b0 เมื่อใดa === bและ> 0 เมื่อใดa > b)

ในทำนองเดียวกันว่าฉันมีอิสระในการเปลี่ยนแปลงการใช้งานการเรียงลำดับของฉันตราบใดที่ฉันไม่ละเมิดข้อกำหนดของฉันการใช้ ECMAScript มีอิสระที่จะเลือกการใช้งานการเรียงลำดับภายในขอบเขตของข้อกำหนดภาษาดังนั้นจึงArray#sortมีแนวโน้มที่จะสร้างการเรียกตัวเปรียบเทียบที่แตกต่าง บนเครื่องยนต์ที่แตกต่างกัน ไม่มีใครเขียนโค้ดที่ตรรกะขึ้นอยู่กับลำดับของการเปรียบเทียบโดยเฉพาะ (และตัวเปรียบเทียบไม่ควรสร้างผลข้างเคียงตั้งแต่แรก)

ตัวอย่างเช่นเครื่องยนต์ V8 (ในขณะที่เขียน) เรียกใช้Timsortเมื่ออาร์เรย์มีขนาดใหญ่กว่าจำนวนองค์ประกอบที่คำนวณไว้ล่วงหน้าและใช้การเรียงลำดับการแทรกไบนารีสำหรับส่วนอาร์เรย์ขนาดเล็ก อย่างไรก็ตามมันเคยใช้Quicksortซึ่งไม่เสถียรและมีแนวโน้มที่จะให้ลำดับอาร์กิวเมนต์ที่แตกต่างกันและการเรียกใช้ตัวเปรียบเทียบ

เนื่องจากการใช้งานการเรียงลำดับที่แตกต่างกันใช้ค่าส่งคืนของฟังก์ชันตัวเปรียบเทียบที่แตกต่างกันสิ่งนี้อาจนำไปสู่พฤติกรรมที่น่าแปลกใจเมื่อตัวเปรียบเทียบไม่ปฏิบัติตามสัญญา ดูหัวข้อนี้สำหรับตัวอย่าง


-2
var array=[25, 8, 7, 41]

array.sort(function(a,b){
  console.log(`a = ${a} , b = ${b}`);
  return a - b
});

เอาท์พุท

  • a = 8, b = 25
  • a = 7, b = 8
  • a = 41, b = 7
  • a = 41, b = 8
  • a = 41, b = 25

ในเบราว์เซอร์ของฉัน (Google Chrome เวอร์ชัน 70.0.3538.77 (รุ่นอย่างเป็นทางการ) (64 บิต)) ในการทำซ้ำครั้งแรกอาร์กิวเมนต์a คือองค์ประกอบที่สองในอาร์เรย์และอาร์กิวเมนต์b คือองค์ประกอบแรกของอาร์เรย์

ถ้าฟังก์ชัน Compare ส่งกลับ

  1. ค่าติดลบกว่า b ถูกย้ายไปข้างหน้าเป็น
  2. ค่าบวกมากกว่า a จะถูกย้ายไปข้างหน้าเป็น b
  3. 0 (ศูนย์) ทั้ง a & b จะยังคงอยู่เหมือนเดิม
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.