อัลกอริทึมการเรียงลำดับฮีป (พร้อม) Code in Python และ C++)

⚡ สรุปอย่างชาญฉลาด

อัลกอริทึมการเรียงลำดับแบบฮีป (Heap Sort) เรียงลำดับอาร์เรย์โดยการสร้างฮีปไบนารีและทำซ้ำไปเรื่อยๆtracโดยการนำค่ารากของมันไปใส่ในส่วนที่เรียงลำดับแล้ว แหล่งข้อมูลนี้อธิบายเกี่ยวกับ heapify, max และ min heap, รหัสเทียม และขั้นตอนทั้งหมด Python และ C++ การนำไปใช้งานพร้อมการวิเคราะห์ความซับซ้อนของเวลา

  • 🌳 แนวคิดหลัก: การเรียงลำดับแบบฮีปจะสร้างฮีปไบนารีที่สมบูรณ์ จากนั้นจึงทำการเรียงลำดับซ้ำๆtracts คือรากเพื่อสร้างลำดับที่เรียงแล้ว
  • 🔺 ฮีปฟาย: การดำเนินการ heapify จะคืนค่าคุณสมบัติของฮีปโดยการสลับping พ่อแม่กับลูกที่ตัวใหญ่กว่า
  • 🗃️ การจัดเก็บข้อมูลแบบอาร์เรย์: โครงสร้างข้อมูลแบบฮีป (Heap) ถูกจัดเก็บในรูปแบบอาร์เรย์ โดยที่โหนดที่ดัชนี i จะมีโหนดลูกอยู่ที่ดัชนี 2i+1 และ 2i+2
  • ⏱️ ซับซ้อน: การเรียงลำดับฮีปใช้เวลา O(n log n) ในทุกกรณี และใช้พื้นที่เพิ่มเติม O(1)
  • ???? Code ให้: การทำงาน Python และ C++ โปรแกรมสาธิตการเพิ่มจำนวนฮีป การสร้างฮีป และการเรียงลำดับแบบสมบูรณ์

อัลกอริทึมการเรียงลำดับฮีปคืออะไร

ฮีปซอร์ต (Heap Sort) เป็นหนึ่งในอัลกอริทึมการเรียงลำดับที่ได้รับความนิยมและรวดเร็ว มันสร้างขึ้นบนโครงสร้างข้อมูลต้นไม้ไบนารีแบบสมบูรณ์ เราจะค้นหาองค์ประกอบที่ใหญ่ที่สุดและวางไว้ที่ด้านบนสุดของฮีปสูงสุด โดยจะวางไว้ที่โหนดแม่ของต้นไม้ไบนารี

สมมติว่าได้รับอาร์เรย์ ข้อมูล = [10,5, 7, 9, 4, 11, 45, 17, 60].

ในอาเรย์ ถ้าดัชนี i-th (i=0,1,2,3 …) เป็นโหนดหลัก ดังนั้น (2i+1) และ (2i+2) จะเป็นลูกด้านซ้ายและขวา การสร้างแผนผังไบนารีที่สมบูรณ์ด้วยอาเรย์นี้จะมีลักษณะดังนี้:

อัลกอริธึมการเรียงลำดับกอง

เราจะดำเนินการ heapify ตั้งแต่ต้นจนจบอาร์เรย์ ในตอนแรก หากเราแปลงอาร์เรย์เป็นทรี อาร์เรย์จะมีลักษณะดังภาพด้านบน เราจะเห็นว่าอาร์เรย์ไม่ได้รักษาคุณสมบัติของ heap ใดๆ (min-heap หรือ max heap) เราจะได้อาร์เรย์ที่เรียงลำดับแล้วโดยดำเนินการ heapify สำหรับโหนดทั้งหมด

การประยุกต์ใช้การเรียงลำดับฮีป

ต่อไปนี้เป็นการใช้งานอัลกอริทึมการเรียงลำดับฮีป:

  • การสร้าง "คิวลำดับความสำคัญ" จำเป็นต้องมีการเรียงลำดับฮีป เนื่องจากฮีปเรียงลำดับจะเก็บองค์ประกอบไว้หลังจากการแทรกแต่ละครั้ง
  • โครงสร้างข้อมูลฮีปมีประสิทธิภาพในการค้นหาเคth องค์ประกอบที่ใหญ่ที่สุดในอาร์เรย์ที่กำหนด
  • Linux Kernel ใช้การเรียงลำดับฮีปเป็นค่าเริ่มต้น อัลกอริทึมการเรียงลำดับ เนื่องจากมีความซับซ้อนของพื้นที่ O (1)

สร้างการเรียงลำดับฮีปด้วยตัวอย่าง

ที่นี่เราจะสร้าง max heap จากไบนารีทรีที่สมบูรณ์ดังต่อไปนี้

สร้างการเรียงลำดับฮีปด้วยตัวอย่าง

โหนดย่อยคือ 17, 60, 4, 11 และ 45 โหนดย่อยเหล่านี้ไม่มีโหนดย่อย นั่นคือเหตุผลว่าทำไมจึงเป็นโหนดย่อย ดังนั้นเราจะเริ่มวิธี heapify จากโหนดหลัก นี่คือขั้นตอน:

ขั้นตอน 1) เลือกแผนผังย่อยซ้ายสุด หากโหนดย่อยมีขนาดใหญ่กว่า ให้สลับโหนดหลักกับโหนดย่อย

ที่นี่โหนดหลักคือ 9 และโหนดย่อยคือ 17 และ 60 เนื่องจาก 60 เป็นโหนดที่ใหญ่ที่สุด 60 และ 9 จะถูกสลับเพื่อรักษา กองสูงสุด.

สร้างการเรียงลำดับฮีปด้วยตัวอย่าง

ขั้นตอน 2) ตอนนี้ ทรีย่อยซ้ายสุดถูกฮีปแล้ว โหนดพาเรนต์ถัดไปคือ 7 พาเรนต์นี้มีโหนดย่อยสองโหนด และโหนดที่ใหญ่ที่สุดคือ 45 ดังนั้น 45 และ 7 จะถูกสลับ

สร้างการเรียงลำดับฮีปด้วยตัวอย่าง

สร้างการเรียงลำดับฮีปด้วยตัวอย่าง

ขั้นตอน 3) โหนด 60 และ 4 มีโหนดหลัก 5 เนื่องจาก “5” มีขนาดเล็กกว่าโหนดย่อย 60 จึงจะถูกสลับ

สร้างการเรียงลำดับฮีปด้วยตัวอย่าง

สร้างการเรียงลำดับฮีปด้วยตัวอย่าง

ขั้นตอน 4) ตอนนี้โหนด 5 มีโหนดลูก 17,9 นี่ไม่ได้รักษาคุณสมบัติฮีปสูงสุด ดังนั้น 5 จะถูกแทนที่ด้วย 17

สร้างการเรียงลำดับฮีปด้วยตัวอย่าง

ขั้นตอน 5) โหนด 10 จะถูกสลับด้วย 60 จากนั้นจึงสลับด้วย 17 กระบวนการจะมีลักษณะดังต่อไปนี้

สร้างการเรียงลำดับฮีปด้วยตัวอย่าง

สร้างการเรียงลำดับฮีปด้วยตัวอย่าง

ขั้นตอน 6) จนถึงขั้นตอนที่ 5 เราได้สร้างฮีปสูงสุดแล้ว ทุกโหนดหลักมีขนาดใหญ่กว่าโหนดย่อย โหนดรูทมีค่าสูงสุด (60)

หมายเหตุ ในการสร้างอาร์เรย์ที่เรียงลำดับ เราจำเป็นต้องแทนที่โหนดที่มีค่าสูงสุดด้วยตัวตายตัวแทน

กระบวนการนี้เรียกว่า “extracทีสูงสุด- เนื่องจาก 60 เป็นโหนดสูงสุด เราจะกำหนดตำแหน่งให้เป็นดัชนีที่ 0 และสร้างฮีปโดยไม่มีโหนด 60

สร้างการเรียงลำดับฮีปด้วยตัวอย่าง

สร้างการเรียงลำดับฮีปด้วยตัวอย่าง

ขั้นตอน 7) เนื่องจาก 60 ถูกลบออกไปแล้ว ค่าสูงสุดถัดไปคือ 45 เราจะทำกระบวนการ “ตัวอย่าง”tract Max” อีกครั้งจากโหนด 45

คราวนี้เราจะได้รับ 45 และแทนที่โหนดรูทด้วยตัวตายตัวแทน 17

เราจำเป็นต้องดำเนินการ”Extract แม็กซ์” จนกว่าองค์ประกอบทั้งหมดจะถูกจัดเรียง

หลังจากทำตามขั้นตอนเหล่านี้จนกระทั่งเราเสร็จสิ้นtracสำหรับค่าสูงสุดทั้งหมด เราจะได้อาร์เรย์ดังต่อไปนี้

สร้างการเรียงลำดับฮีปด้วยตัวอย่าง

ไบนารีฮีปคืออะไร?

Binary Heap เป็นแบบที่สมบูรณ์ ต้นไม้ไบนารี โครงสร้างข้อมูล. ในโครงสร้างต้นไม้ชนิดนี้ โหนดพาเรนต์จะมีขนาดใหญ่กว่าหรือเล็กกว่าโหนดย่อย หากโหนดพาเรนต์มีขนาดเล็ก ฮีปจะเรียกว่า “มินฮีป” และหากโหนดพาเรนต์มีขนาดใหญ่กว่า ฮีปจะเรียกว่า “แม็กซ์ฮีป”

นี่คือตัวอย่างของฮีปขั้นต่ำและฮีปสูงสุด

มินฮีปและแม็กซ์ฮีป
มินฮีปและแม็กซ์ฮีป

ในรูปด้านบน หากคุณสังเกตเห็น “Min Heap” โหนดหลักจะมีขนาดเล็กกว่าโหนดลูกเสมอ ที่หัวต้นไม้ เราสามารถหาค่าที่น้อยที่สุดได้ 10

ในทำนองเดียวกัน สำหรับ "Max Heap" โหนดหลักจะมีขนาดใหญ่กว่าโหนดลูกเสมอ องค์ประกอบสูงสุดอยู่ที่โหนดส่วนหัวสำหรับ "Max Heap"

Heapify คืออะไร?

“Heapify” คือหลักการของฮีปที่รับรองตำแหน่งของโหนด ใน Heapify ฮีปสูงสุดจะรักษาความสัมพันธ์กับโหนดหลักและโหนดย่อยเสมอ และโหนดหลักจะมีขนาดใหญ่กว่าโหนดย่อย

ตัวอย่างเช่น หากมีการเพิ่มโหนดใหม่ เราจำเป็นต้องปรับรูปร่างของฮีปใหม่ อย่างไรก็ตาม เราอาจต้องเปลี่ยนแปลงหรือสลับโหนด หรือจัดเรียงอาร์เรย์ใหม่ กระบวนการปรับรูปร่างนี้ping กองข้อมูลแบบนี้เรียกว่า “heapify”

นี่คือตัวอย่างวิธีการทำงานของ heapify:

การเพิ่มโหนดใหม่และ Heapify
การเพิ่มโหนดใหม่และฮีพฟิฟาย

นี่คือขั้นตอนสำหรับ heapify:

ขั้นตอน 1) เพิ่มโหนด 65 เป็นลูกที่ถูกต้องของโหนด 60

ขั้นตอน 2) ตรวจสอบว่าโหนดที่เพิ่มใหม่มีค่ามากกว่าโหนดหลักหรือไม่

ขั้นตอน 3) เนื่องจากมันมากกว่าโหนดพาเรนต์ เราจึงสลับโหนดย่อยที่ถูกต้องกับโหนดพาเรนต์

วิธีการสร้างฮีป

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

สมมติว่าอาร์เรย์มีองค์ประกอบทั้งหมด n รายการ หากดัชนี “i” เป็นโหนดหลัก โหนดด้านซ้ายจะอยู่ที่ดัชนี (2i+1)และโหนดที่ถูกต้องจะอยู่ที่ดัชนี (2i+2)- เราสมมติว่าดัชนีอาร์เรย์เริ่มต้นจาก 0

ด้วยวิธีนี้ เราจะเก็บค่า max heap ลงในรูปแบบอาร์เรย์ดังต่อไปนี้:

การแสดงตามอาร์เรย์ของ Max Heap
การแสดงค่าฮีปสูงสุดตามอาร์เรย์

อัลกอริธึม heapify จะรักษาคุณสมบัติของ heap ไว้ หากโหนดหลักไม่มีค่าสุดขั้ว (น้อยกว่าหรือมากกว่า) โหนดหลักจะสลับกับโหนดย่อยที่มีค่าสุดขั้วมากที่สุด

ต่อไปนี้เป็นขั้นตอนในการสร้าง heap สูงสุด:

ขั้นตอน 1) เริ่มจากโหนดใบ

ขั้นตอน 2) ค้นหาค่าสูงสุดระหว่างผู้ปกครองและลูก

ขั้นตอน 3) สลับโหนดหากโหนดลูกมีค่ามากกว่าโหนดหลัก

ขั้นตอน 4) ขึ้นไปหนึ่งระดับ

ขั้นตอน 5) ทำตามขั้นตอนที่ 2,3,4 จนกระทั่งถึงดัชนี 0 หรือเรียงลำดับทั้งแผนผัง

นี่คือรหัสเทียมสำหรับ heapify แบบเรียกซ้ำ (heap สูงสุด):

def heapify():
  input→ array, size, i
  largest = i
  left = 2*i + 1
  right = 2*i + 2
if left<n and array[largest ] < array[left]:
  largest = left
if right<n and array[largest ] < array[right]:
  largest = right
If largest not equals i:
  swap(array[i],array[largest])
  heapify(array,n,largest)

ชื่อเล่น Code สำหรับการเรียงลำดับแบบฮีป

นี่คือรหัสหลอกสำหรับอัลกอริทึมการเรียงลำดับฮีป:

Heapify(numbers as an array, n as integer, i as integer):
  largest = i
  left = 2i+1
  right= 2i+2
if(left<=n) and (numbers[i]<numbers[left])
  largest=left
if(right<=n) and (numbers[i]<numbers[right])
  largest=right
if(largest  != i)
  swap(numbers[i], numbers[largest])
  Heapify(numbers,n,largest)
HeapSort(numbers as an array):
  n= numbers.size()
for i in range n/2 to 1
  Heapify(numbers,n,i)
for i in range n to 2
  Swap numbers[i] with numbers[1]
  Heapify(numbers,i,0)

ตัวอย่างการเรียงลำดับแบบฮีป (Heap Sort) Code in C++

#include <iostream>
using namespace std;
void display(int arr[], int n)
{
    for (int i = 0; i < n; i++)
    {
        cout << arr[i] << "\t";
    }
    cout << endl;
}
void heapify(int numbers[], int n, int i)
{
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    if (left < n && numbers[left] < numbers[largest])
    {
        largest = left;
    }
    if (right < n && numbers[right] < numbers[largest])
    {
        largest = right;
    }
    if (largest != i)
    {
	//uncomment the following line to see details in output
        //cout<<"Swapping "<< numbers[i]<< " and "<<numbers[largest]<<endl;
        swap(numbers[i], numbers[largest]);
        heapify(numbers, n, largest);
    }
}
void heapSort(int numbers[], int n)
{
    for (int i = n/2 - 1; i >= 0; i--)
    {
        heapify(numbers, n, i);
//uncomment the following line to see details in output
 //cout<<"Heapify:\t";
  //display(numbers,n);
    }
    for (int i = n - 1; i >= 0; i--)
    {
        swap(numbers[0], numbers[i]);
        heapify(numbers, i, 0);
    }
}
int main()
{
    int numbers[] = { 10,5, 7, 9, 4, 11, 45, 17, 60};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    cout<<"Initial Array:\t";
    display(numbers,size);
    heapSort(numbers, size);
    cout<<"Sorted Array (descending order):\t";
    display(numbers, size);
}

Output:

Initial Array:  10      5       7       9       4       11      45      17      60
Sorted Array (descending order):  60      45      17      11      10      9       7       5       4

ตัวอย่างการเรียงลำดับแบบฮีป (Heap Sort) Code in Python

def display(arr):
    for i in range(len(arr)):
    print(arr[i], end = "\t")
print()
def heapify(numbers, n, i):
    largest = i
left = 2 * i + 1
right = 2 * i + 2
if left < n and numbers[left] < numbers[largest]:
    largest = left
if right < n and numbers[right] < numbers[largest]:
    largest = right
if largest != i:
    numbers[i], numbers[largest] = numbers[largest], numbers[i]
heapify(numbers, n, largest)
def heapSort(items, n):
    for i in range(n //2,-1,-1):
        heapify(items, n, i) for i in range(n - 1, -1, -1):
        items[0], items[i] = items[i], items[0] heapify(items, i, 0) numbers = [10, 5, 7, 9, 4, 11, 45, 17, 60] print("Initial List:\t", end = "") display(numbers) print("After HeapSort:\t", end = "") heapSort(numbers, len(numbers)) display(numbers)

Output:

Initial List:   10      5       7       9       4       11      45      17      60
After HeapSort: 60      45      17      11      10      9       7       5       4

การวิเคราะห์ความซับซ้อนของเวลาและพื้นที่ของการเรียงลำดับแบบกอง

มีความซับซ้อนของเวลาและความซับซ้อนของพื้นที่ที่เราสามารถวิเคราะห์ได้สำหรับการเรียงลำดับแบบฮีป สำหรับความซับซ้อนของเวลา เรามีกรณีดังต่อไปนี้:

  1. เคสที่ดีที่สุด
  2. กรณีเฉลี่ย
  3. กรณีที่เลวร้ายที่สุด

ฮีปถูกนำไปใช้บนแผนผังไบนารีที่สมบูรณ์ ดังนั้นที่ระดับล่างสุดของไบนารีทรี จะมีจำนวนโหนดสูงสุด หากระดับล่างสุดไม่มีโหนด ระดับด้านบนก็จะมี n/2 โหนด

การวิเคราะห์ความซับซ้อนของเวลาและพื้นที่

ในตัวอย่างนี้ ระดับ 3 มีสี่รายการ ระดับ 2 มีสองรายการ และระดับ 1 มีหนึ่งรายการ หากมีทั้งหมด n จำนวนรายการ ความสูงหรือระดับรวมจะเป็น เข้าสู่ระบบ2(น). ดังนั้น การแทรกองค์ประกอบเดียวอาจใช้เวลาสูงสุดในการวนซ้ำ Log(n)

เมื่อเราต้องการดึงค่าสูงสุดจากฮีป เราเพียงแค่ดึงโหนดราก จากนั้นรันฮีปฟิวซ์อีกครั้ง ฮีปฟิวซ์แต่ละตัวจะดึงค่าสูงสุดจากโหนดราก เข้าสู่ระบบ2(n) เวลา เช่นtracการหาค่าสูงสุดใช้เวลา O(1)

ความซับซ้อนของเวลาที่ดีที่สุดสำหรับอัลกอริทึมการเรียงลำดับฮีป

เมื่อองค์ประกอบทั้งหมดได้รับการจัดเรียงในอาร์เรย์แล้ว จะต้องใช้เวลา O(n) ในการสร้างฮีป เพราะหากเรียงลำดับรายการแล้วการแทรกรายการจะใช้เวลาคงที่นั่นคือ O(1)

ดังนั้น จะต้องใช้เวลา O(n) ในการสร้าง max-heap หรือ min-heap ในกรณีที่ดีที่สุด

ความซับซ้อนของเวลาเคสโดยเฉลี่ยสำหรับอัลกอริทึมการเรียงลำดับฮีป

การแทรกรายการหรือตัวอย่างtracการหาค่าสูงสุดใช้เวลา O(log(n)) ดังนั้น ความซับซ้อนของเวลาโดยเฉลี่ยสำหรับอัลกอริทึมการเรียงลำดับฮีปคือ O(n บันทึก(n))

ความซับซ้อนของเวลาในกรณีที่แย่ที่สุดสำหรับอัลกอริทึมการเรียงลำดับแบบฮีป

คล้ายกับกรณีทั่วไป ในสถานการณ์เลวร้ายที่สุด เราอาจดำเนินการ heapify n ครั้ง แต่ละครั้งจะใช้เวลา O(log(n)) ดังนั้น ความซับซ้อนของเวลาในกรณีเลวร้ายที่สุดจะเป็นดังนี้ O(n บันทึก(n))

ความซับซ้อนของพื้นที่สำหรับอัลกอริทึมการเรียงลำดับแบบฮีป

การเรียงลำดับแบบฮีปเป็นอัลกอริทึมที่ออกแบบขึ้นเอง ซึ่งหมายความว่าไม่จำเป็นต้องใช้หน่วยความจำพิเศษหรือหน่วยความจำชั่วคราวเพื่อดำเนินการงาน หากเราดูการใช้งานจริง เราจะสังเกตเห็นว่าเราใช้ swap () เพื่อดำเนินการแลกเปลี่ยนโหนด ไม่จำเป็นต้องใช้รายการหรืออาร์เรย์อื่น ดังนั้นความซับซ้อนของพื้นที่จึงเท่ากับ O(1)

คำถามที่พบบ่อย

การเรียงลำดับแบบฮีปไม่ใช่ขั้นตอนวิธีเรียงลำดับที่เสถียร เนื่องจากการสร้างและขยายtracการเรียงลำดับจากกองข้อมูลสามารถจัดเรียงลำดับองค์ประกอบที่เท่ากันใหม่ได้ หากการรักษาลำดับเดิมของคีย์ที่เท่ากันมีความสำคัญ อัลกอริทึมที่เสถียรอย่างเช่น merge sort จะเป็นตัวเลือกที่ดีกว่า

การเรียงลำดับแบบฮีป (Heap sort) รับประกันเวลา O(n log n) ในทุกกรณี และใช้พื้นที่เพิ่ม O(1) การเรียงลำดับแบบควิก (Quicksort) มักจะเร็วกว่าในทางปฏิบัติ แต่ประสิทธิภาพอาจลดลงเหลือ O(n²) ในกรณีที่ใช้จุดหมุนที่ไม่ดี การเรียงลำดับแบบฮีปแลกความเร็วบางส่วนกับความน่าเชื่อถือในกรณีที่เลวร้ายที่สุด

ทั้ง heap sort และ merge sort ทำงานในเวลา O(n log n) Heap sort เรียงลำดับในตำแหน่งเดิมโดยใช้พื้นที่เพิ่ม O(1) แต่ไม่เสถียร ส่วน merge sort เสถียรแต่ต้องการพื้นที่เพิ่ม O(n) สำหรับการรวมข้อมูล

ผู้สอน AI สามารถสร้างภาพเคลื่อนไหวของกระบวนการ heapify แสดงวิธีการสร้าง max heap และอื่นๆ ได้ trace แต่ละ extracขั้นตอน t-max ตัวช่วยแบบภาพและโต้ตอบนี้ช่วยให้ผู้เริ่มต้นเข้าใจวิธีการเรียงลำดับอาร์เรย์ด้วยการเรียงลำดับแบบฮีปได้ง่ายขึ้น

ใช่แล้ว ผู้ช่วยเขียนโค้ด AI สามารถแปลการใช้งานการเรียงลำดับแบบฮีป (heap sort) ระหว่างภาษาต่างๆ ได้ เช่น C++, Pythonและ Java ในขณะที่คีping ตรรกะยังคงเหมือนเดิม คุณควรคอมไพล์และทดสอบโค้ดที่แปลงแล้วเพื่อยืนยันว่าผลลัพธ์ถูกต้อง

สรุปโพสต์นี้ด้วย: