Merge sort เป็นเทคนิคการจัดเรียงตามเทคนิคการแบ่งและพิชิต ความซับซ้อนของเวลากรณีที่เลวร้ายที่สุดคือ Ο(n log n) แต่สิ่งนี้มาพร้อมกับค่าใช้จ่ายเพิ่มเติมในแง่ของพื้นที่เนื่องจากอัลกอริธึมนี้ใช้หน่วยความจำ O(n) พิเศษ
ทีนี้มาดูว่าเราจะใช้อัลกอริทึมนี้อย่างไร เราจะสร้าง 2 ฟังก์ชัน mergeSort และ merge
ผสาน − ฟังก์ชันนี้รับ 2 อาร์กิวเมนต์ ซึ่งเป็น 2 อาร์เรย์บางส่วน จากนั้นจะรวมเป็นหนึ่งเดียวโดยการแทรกองค์ประกอบในลำดับที่ถูกต้อง
ผสานการเรียงลำดับ − ฟังก์ชันนี้เรียก mergeSort ซ้ำๆ ที่ครึ่งซ้ายและขวาของอาร์เรย์ จากนั้นใช้การผสานเพื่อรวมส่วนต่างๆ ของอาร์เรย์เหล่านี้เข้าด้วยกัน สิ่งนี้จะชัดเจนขึ้นมากเมื่อเราดูที่การนำไปใช้
มาเริ่มกันที่ฟังก์ชันผสานและดำดิ่งลงไปที่โค้ด −
ตัวอย่าง
function merge(left, right) { let mergedArr = []; let i = 0; let j = 0; // Try merging till we reach end of either one of the arrays. while (i < left.length && j < right.length) { if (compare(left[i], right[j])) { mergedArr.push(left[i]); i++; } else { mergedArr.push(right[j]); j++; } } // If left was 1, 2, 3, 5 and right was 4, 6, 7, 9 // the for loop above would stop once it reaches the end of // The left array leaving 3 elements in the right array // In order to add the remaining elements from either array, // We need to add elements from i to end in left and j to end // in the right array to the merged array. return mergedArr.concat(left.slice(i)).concat(right.slice(j)); }
ฟังก์ชันนี้จะนำอาร์เรย์ที่เรียงลำดับแล้ว 2 อันมารวมกันเป็นเวลา O(n) เพื่อให้ยังคงเรียงลำดับเป็นอาร์เรย์ที่ใหญ่กว่า อ้างถึงความคิดเห็นของรหัสสำหรับคำอธิบายเชิงลึกของวิธีการ คุณสามารถทดสอบสิ่งนี้โดยใช้ −
ตัวอย่าง
let a1 = [1, 2, 3, 5] let a2 = [4, 6, 8, 9] console.log(merge(a1, a2))
ผลลัพธ์
[1, 2, 3, 4, 5, 8, 9]
ตอนนี้เราจะใช้ฟังก์ชันนี้ในฟังก์ชัน mergesort เพื่อจัดเรียงอาร์เรย์ทั้งหมด
เราจะสร้างฟังก์ชันภายในสำหรับ mergeSort ซึ่งเราจะห่อด้วยฟังก์ชันภายนอกเพื่อให้ตัวเปรียบเทียบของเราพร้อมใช้งาน เพื่อให้ฟังก์ชันของเราสามารถขยายได้ มาดูฟังก์ชันภายในกัน −
ตัวอย่าง
function mergeSortInner(arr) { if (arr.length < 2) { return arr; } let mid = Math.floor(arr.length / 2); // Create an array from 0 to mid - 1 elements let left = arr.slice(0, mid); // Create an array from mid to the last element let right = arr.slice(mid); // Sort the left half, sort the right half, // merge the sorted arrays and return return merge(mergeSortInner(left), mergeSortInner(right)); }
ฟังก์ชันนี้แยกอาร์เรย์ออกเป็นสองส่วน จัดเรียงทีละรายการ จากนั้นส่งคืนอาร์เรย์ที่ผสานกลับ
ให้เราดูโค้ดที่สมบูรณ์พร้อมการทดสอบ -
ตัวอย่าง
function mergeSort(arr, compare = (a, b) => a < b) { function merge(left, right) { let mergedArr = []; let i = 0; let j = 0; // Try merging till we reach end of either one of the arrays. while (i < left.length && j < right.length) { if (compare(left[i], right[j])) { mergedArr.push(left[i]); i++; } else { mergedArr.push(right[j]); j++; } } // If left was 1, 2, 3, 5 and right was 4, 6, 7, 9 // the for loop above would stop once it reaches the end of // The left array leaving 3 elements in the right array // In order to add the remaining elements from either array, // We need to add elements from i to end in left and j to end // in the right array to the merged array. return mergedArr.concat(left.slice(i)).concat(right.slice(j)); } function mergeSortInner(arr) { if (arr.length < 2) { return arr; } let mid = Math.floor(arr.length / 2); let left = arr.slice(0, mid); let right = arr.slice(mid); return merge(mergeSortInner(left), mergeSortInner(right)); } // Call inner mergesort to sort and return the array for us. return mergeSortInner(arr); } You can test this using: let arr = [5, 8, 9, 12, -8, 31, 2]; // Sort Array elements in increasing order arr = mergeSort(arr); console.log(arr); // Sort Array elements in decreasing order arr = mergeSort(arr, (a, b) => a > b); console.log(arr); arr = [ { name: "Harry", age: 20 }, { name: "Jacob", age: 25 }, { name: "Mary", age: 12 } ]; // Sort Array elements in increasing order alphabetically by names arr = mergeSort(arr, (a, b) => a.name < b.name); console.log(arr); // Sort Array elements in decreasing order of ages arr = mergeSort(arr, (a, b) => a.age < b.age); console.log(arr);
ผลลัพธ์
[ -8, 2, 5, 8, 9, 12, 31 ] [ 31, 12, 9, 8, 5, 2, -8 ] [ { name: 'Harry', age: 20 }, { name: 'Jacob', age: 25 }, { name: 'Mary', age: 12 } ] [ { name: 'Mary', age: 12 }, { name: 'Harry', age: 20 }, { name: 'Jacob', age: 25 } ]
การเรียงลำดับอย่างรวดเร็ว
การเรียงลำดับอย่างรวดเร็วเป็นอัลกอริธึมการเรียงลำดับที่มีประสิทธิภาพสูง และขึ้นอยู่กับการแบ่งพาร์ติชั่นของอาร์เรย์ของข้อมูลออกเป็นอาร์เรย์ที่เล็กกว่า อาร์เรย์ขนาดใหญ่แบ่งออกเป็นสองอาร์เรย์ โดยที่อาร์เรย์หนึ่งเก็บค่าที่น้อยกว่าค่าที่ระบุ เช่น pivot โดยอิงตามพาร์ติชั่นที่สร้างขึ้น และอาร์เรย์อื่นจะเก็บค่าที่มากกว่าค่าเดือย
การเรียงลำดับอย่างรวดเร็วแบ่งพาร์ติชั่นอาร์เรย์แล้วเรียกตัวเองซ้ำสองครั้งเพื่อเรียงลำดับอาร์เรย์ย่อยที่เป็นผลลัพธ์ทั้งสอง อัลกอริธึมนี้ค่อนข้างมีประสิทธิภาพสำหรับชุดข้อมูลขนาดใหญ่ เนื่องจากความซับซ้อนของค่าเฉลี่ยและกรณีที่เลวร้ายที่สุดคือ Ο(n2) โดยที่ n คือจำนวนรายการ
กระบวนการแบ่งพาร์ติชั่น
การแสดงภาพเคลื่อนไหวต่อไปนี้จะอธิบายวิธีค้นหาค่า pivot ในอาร์เรย์https://www.tutorialspoint.com/data_structures_algorithms/images/quick_sort_partition_animation.gif
ค่าเดือยแบ่งรายการออกเป็นสองส่วน และแบบเรียกซ้ำ เราจะพบจุดหมุนสำหรับรายการย่อยแต่ละรายการจนกว่ารายการทั้งหมดจะมีเพียงองค์ประกอบเดียว
สิ่งที่เราทำในการแบ่งพาร์ติชั่นคือเราเลือกองค์ประกอบแรกจากอาร์เรย์ (องค์ประกอบแบบสุ่มในการเรียงลำดับอย่างรวดเร็วแบบสุ่ม) จากนั้นเปรียบเทียบส่วนที่เหลือของอาร์เรย์กับองค์ประกอบนี้ จากนั้นเราจะย้ายองค์ประกอบทั้งหมดที่น้อยกว่าองค์ประกอบนี้ไปทางด้านซ้ายของสิ่งที่เราเรียกว่าดัชนี pivot และค่าที่มากกว่านี้ไปทางด้านขวาของดัชนี pivot ดังนั้นเมื่อเราถึงจุดสิ้นสุด องค์ประกอบ pivot (องค์ประกอบแรก) จะถูกวางในตำแหน่งที่ถูกต้อง
ทั้งนี้เนื่องจากองค์ประกอบทั้งหมดที่มากกว่าทางด้านขวาและน้อยกว่าทางด้านซ้าย ทำให้องค์ประกอบนี้อยู่ในตำแหน่งที่ถูกต้อง เราขยายกระบวนการแบ่งพาร์ติชั่นนี้เพื่อช่วยเราในการเรียงลำดับโดยการเรียกพาร์ติชั่นซ้ำๆ บนอาร์เรย์ย่อยด้านซ้ายและขวา
เราสามารถใช้ฟังก์ชันพาร์ทิชันได้ดังนี้ -
ตัวอย่าง
function partition(arr, low, high, compare) { let pivotIndex = low + 1; for (let i = pivotIndex; i < high; i++) { if (compare(arr[i], arr[low])) { // Swap the number less than the pivot swap(arr, i, pivotIndex); pivotIndex += 1; } } // Place the pivot to its correct position swap(arr, pivotIndex - 1, low); // Return pivot's position return pivotIndex - 1; } function swap(arr, i, j) { let temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } You can test the partition function using: let arr = [5, 1, 10, 8, 9, 3, 2, 45, -6]; console.log(partition(arr, 0, arr.length, (l, r) => l < r)); console.log(arr);
ผลลัพธ์
4 [ -6, 1, 3, 2, 5, 10, 8, 45, 9 ]
โปรดทราบว่าองค์ประกอบทางซ้ายของ 5 มีค่าน้อยกว่า 5 และองค์ประกอบทางขวามากกว่า 5 เรายังเห็นว่าดัชนีของ 5 คือ 4
ตอนนี้เรามาดูกันว่า Quick Sort ทำงานอย่างไร -
หากเรามีหน้าต่างที่มากกว่า 1 องค์ประกอบ เราจะเรียกพาร์ติชั่นในอาร์เรย์จากต่ำไปสูง นำดัชนีที่ส่งคืนมาและใช้เพื่อเรียก quicksort ในครึ่งซ้ายและขวาของอาร์เรย์
ตัวอย่าง
function QuickSort(arr, low, high, compare = (l, r) => l < r) { if (high - low > 0) { // Partition the array let mid = partition(arr, low, high, compare); // Recursively sort the left half QuickSort(arr, low, mid, compare); // Recursively sort the right half QuickSort(arr, mid + 1, high, compare); } } You can test this using: let arr = [5, 1, 10, 8, 9, 3, 2, 45, -6]; QuickSort(arr, 0, arr.length, (l, r) => l < r); console.log(arr);
ผลลัพธ์
[ -6, 1, 2, 3, 5, 8, 9, 10, 45 ]