מבני נתונים - אוניברסיטת רייכמן

תרגול 4 - Comparison-Based Sorting Algorithms

Recitation 4 · מיון מבוסס השוואות

תוכן עניינים

  1. רקע: HeapSort, MergeSort, רקורסיה
  2. תרגיל 1: ניתוח רקורסיה עם n זוגי/אי-זוגי
  3. HeapSort - Recap
  4. תרגיל 2: HeapSort בסדר יורד
  5. MergeSort - Recap
  6. דף עזר

1. רקע

חזרה: ניתוח רקורסיה
לכל אלגוריתם רקורסיבי: ניסוח \(T(n)\) כנוסחה רקורסיבית, פתרון ע"י הצבה / אינדוקציה / עץ רקורסיה.
HeapSort בקצרה
BuildMaxHeap (\(O(n)\)) ואז \(n-1\) פעמים DeleteMax לסוף המערך. \(\Theta(n \log n)\) worst-case, in-place, לא יציב.
MergeSort בקצרה
Divide-and-Conquer: חלוקה לחצי, מיון רקורסיבי, Merge. \(T(n) = 2T(n/2) + \Theta(n) = \Theta(n \log n)\) בכל המקרים. יציב, \(O(n)\) מקום עזר.

2. תרגיל 1: ניתוח רקורסיה

השאלה
תנו חסם עליון לזמן הריצה של:
func(n)
    if (n = 1 or n = 2)
        return 2n
    if (n mod 2 = 0)
        num = func(n/2)
        return num²
    else
        return 2*func(n-1)

1) נוסחת רקורסיה

לפי המקרים:
  T(n) ≤ T(n/2) + c₃,        אם n זוגי ו-n > 2
  T(n) ≤ T(n-1) + c₄,        אם n אי-זוגי ו-n > 1
  T(1) = c₁,  T(2) = c₂

יהי c = 2·max{c₁, c₂, c₃, c₄}.

הטריק: כש-n אי-זוגי, אחרי קריאה אחת n-1 זוגי, ואז מתחלק ב-2.
שתי קריאות יורדות מ-n ל-⌊(n-1)/2⌋ ≈ n/2.
לכן באופן אחיד:
  T(n) ≤ T(⌊n/2⌋) + c,    T(1), T(2) ≤ c.

2) פתרון בהצבה

T(n) ≤ T(⌊n/2⌋) + c
     ≤ T(⌊n/4⌋) + 2c
     ≤ T(⌊n/8⌋) + 3c
     ...
     ≤ T(⌊n/2ᵏ⌋) + k·c

נעצרים כש-⌊n/2ᵏ⌋ ≤ 2 ⟺ n ≤ 2^(k+1) ⟺ log₂ n − 1 ≤ k.

אחרי ⌈log n⌉ צעדים מגיעים ל-T(2) או T(1):
  T(n) ≤ c + ⌊log₂ n⌋ · c = O(log n).

3) אישור באינדוקציה

נוכיח: T(n) ≤ c · log₂ n לכל n ≥ 2.

בסיס n=2: T(2) ≤ c = c · log₂ 2. ✓

הנחה: לכל k < n, T(k) ≤ c · log₂ k.

צעד (n זוגי): T(n) ≤ T(n/2) + c
              ≤ c·log(n/2) + c    [הנחת אינדוקציה]
              = c·(log n − log 2) + c
              = c·log n − c + c = c·log n. ✓

צעד (n אי-זוגי): T(n) ≤ T((n-1)/2) + c
                 ≤ c·log((n-1)/2) + c   [הנחה]
                 = c·log(n-1) − c + c
                 ≤ c·log(n-1) ≤ c·log n. ✓

⟹ T(n) = O(log n). ∎

3. HeapSort - Recap

פסאודוקוד
HeapSort(A)
  BuildMaxHeap(A)
  n = length(A)
  For s = n downto 2
    x = A[1]
    PercDown(1, A[s], s-1, A)
    A[s] = x

סיבוכיות וכוחות

זמן Worstזמן Avgזמן Bestמקום
\(\Theta(n \log n)\)\(\Theta(n \log n)\)\(\Theta(n)\)\(O(1)\) - in-place
חוזקות וחולשות
מהיר: \(\Theta(n \log n)\) — בלי worst-case של \(O(n^2)\) כמו ב-Bubble Sort.
חסכוני במקום: in-place, \(O(1)\) מקום עזר.
לאט בפועל: בממוצע איטי יותר מ-QuickSort בגלל cache locality רע.
לא יציב.

4. תרגיל 2: HeapSort בסדר יורד

השאלה
ניאלץ לשנות את HeapSort כך שימיין בסדר יורד. אילו שינויים נדרשים?

גישה 1 — שימוש ב-Min-Heap

HeapSort(A)
  BuildMinHeap(A)            ← במקום BuildMaxHeap
  n = length(A)
  For h = n downto 2
    x = A[1]
    PercDown(1, A[h], h-1, A)
    A[h] = x

כל DeleteMin מוציא את הקטן ביותר ושם בסוף ⟹ מערך ממוין מהגדול לקטן.

גישה 2 — Max-Heap + היפוך מערך

HeapSort(A)
  BuildMaxHeap(A)
  n = length(A)
  For h = n downto 2
    x = A[1]
    PercDown(1, A[h], h-1, A)
    A[h] = x
  ReverseArray(A)            ← היפוך לקבלת סדר יורד

יתרונות וחסרונות

גישהיתרוןחיסרון
1 (Min-Heap)נקייה. אין פעולה נוספת.צריך מימוש נפרד של PercDown ל-Min-Heap.
2 (Reverse)שימוש חוזר ב-BuildMaxHeap הקיים.O(n) נוסף ל-Reverse — קבוע גדול יותר.

אסימפטוטית — שתיהן \(\Theta(n \log n)\). בפועל: גישה 1 עדיפה.

5. MergeSort - Recap

פסאודוקוד
Mergesort(A, l, r)
  if l < r then
    q ← ⌊(l+r)/2⌋
    Mergesort(A, l, q)
    Mergesort(A, q+1, r)
    Merge(A, l, q, r)

Merge(A, p, q, r): מיזוג שני התת-מערכים הממוינים \(A[p..q]\) ו-\(A[q+1..r]\) ל-\(A[p..r]\) ב-\(\Theta(n)\) זמן ו-\(O(n)\) מקום עזר.

זמן (Best/Avg/Worst)מקוםיציב
\(\Theta(n \log n)\)\(\Theta(n)\)כן
משוואת זמן
\(T(n) = 2T(n/2) + \Theta(n) = \Theta(n \log n)\) לכל קלט (best, avg, worst).

6. דף עזר

אלגוריתםBestAvgWorstמקוםיציב
Bubble Sort\(\Theta(n)\)\(\Theta(n^2)\)\(\Theta(n^2)\)in-placeכן
HeapSort\(\Theta(n)\)\(\Theta(n \log n)\)\(\Theta(n \log n)\)in-placeלא
MergeSort\(\Theta(n \log n)\)\(\Theta(n \log n)\)\(\Theta(n \log n)\)\(\Theta(n)\)כן
QuickSort\(\Theta(n \log n)\)\(\Theta(n \log n)\)\(\Theta(n^2)\)in-place + \(O(\log n)\) ממוצעלא
תבניות לניתוח רקורסיה
• \(T(n) = T(n/2) + O(1) \Rightarrow O(\log n)\) (Binary Search).
• \(T(n) = T(n/2) + O(n) \Rightarrow O(n)\) (סכום הולך ויורד פי 2).
• \(T(n) = 2T(n/2) + O(n) \Rightarrow O(n \log n)\) (MergeSort).
• \(T(n) = T(n-1) + O(n) \Rightarrow O(n^2)\) (QuickSort Worst).
• \(T(n) = T(n-1) + O(1) \Rightarrow O(n)\) (לולאה).
איזה sort לבחור?
• דרישה ליציבות: MergeSort.
• ביצועים בפועל מעולים: QuickSort (עם בחירת pivot חכמה).
• מובטח \(\Theta(n \log n)\) worst + in-place: HeapSort.
• מערכים מאוד קטנים (n ≤ 20): Insertion/Bubble Sort (קבוע נמוך).