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

הרצאה 1 - מבוא: אלגוריתמים, מבני נתונים ו-ADT

Introduction to Algorithms, Data Structures & ADTs

תוכן עניינים

  1. מהו אלגוריתם ומהו מבנה נתונים
  2. בעיית המיון (Sorting Problem)
  3. Insertion Sort — פסאודו-קוד
  4. מבני נתונים אלמנטריים
  5. ADT — Abstract Data Type
  6. Sequence ADT ופעולותיו
  7. Stack, Queue, Deque ADTs
  8. ADT vs Data Structure vs Implementation
  9. מימוש Sequence עם מערך
  10. מימוש Sequence עם מערך מעגלי (Circular Array)
  11. רשימה מקושרת יחידה (Singly Linked List)
  12. רשימה מקושרת כפולה (Doubly Linked List)
  13. השוואת סיבוכיות
  14. מערכים דינמיים עם הכפלה (Doubling)
  15. סיכום

1. מהו אלגוריתם ומהו מבנה נתונים

הגדרה: Data (נתונים)
מידע (Information) שניתן לנתח ולעבד — מספרים, מילים, שירים, סרטים וכו'.
הגדרה: Algorithm (אלגוריתם)
פרוצדורה מוגדרת-היטב לפתרון בעיה.
Input → עיבוד (manipulation) → Output
הגדרה: Data Structure (מבנה נתונים)
הדרך שבה הנתונים מאורגנים.
מבנה נתונים = אחסון נתונים + אלגוריתמים לפעולות עליהם.

2. בעיית המיון (Sorting Problem)

הגדרת הבעיה
Input: סדרה \(\langle a_1, a_2, \ldots, a_n \rangle\)
Output: תמורה \(\langle b_1, b_2, \ldots, b_n \rangle\) כך ש-\(b_1 \leq b_2 \leq \ldots \leq b_n\)
דוגמה
Input: \(\langle 31, 41, 59, 26, 41, 58 \rangle\) → Output: \(\langle 26, 31, 41, 41, 58, 59 \rangle\)
הערה
ניתן למיין לא רק מספרים — גם תמונות, שירים, סרטים, שמות. צריך להגדיר/לציין סדר (order).

3. Insertion Sort — פסאודו-קוד

עוברים על האיברים לפי סדר. עבור כל איבר — מכניסים אותו למקום הנכון בתוך הקידומת שכבר ממוינת, עד שהאיבר האחרון מוכנס.

פסאודו-קוד: Insertion-Sort(A)
for i ← 2 to length[A]
    key ← A[i]
    j ← i - 1
    while j > 0 and A[j] > key
        A[j+1] ← A[j]
        j ← j - 1
    end
    A[j+1] ← key
end

מעקב — דוגמה

דוגמה: מיון [5, 2, 4, 6, 1, 3]
  • i=2: key=2 → [2, 5, 4, 6, 1, 3]
  • i=3: key=4 → [2, 4, 5, 6, 1, 3]
  • i=4: key=6 → [2, 4, 5, 6, 1, 3]
  • i=5: key=1 → [1, 2, 4, 5, 6, 3]
  • i=6: key=3 → [1, 2, 3, 4, 5, 6]
סיבוכיות
  • Best case: \(O(n)\) — המערך כבר ממוין.
  • Worst case: \(O(n^2)\) — המערך ממוין בסדר הפוך.

4. מבני נתונים אלמנטריים

מבני נתונים בסיסיים
  • Array (מערך): זיכרון רציף, גישה אקראית ב-\(O(1)\).
  • Stack (מחסנית): LIFO — Last In, First Out.
  • Queue (תור): FIFO — First In, First Out.
  • Linked List (רשימה מקושרת): צמתים עם מצביעים.
  • Tree (עץ): מבנה היררכי.

5. ADT — Abstract Data Type

הגדרה: ADT
תיאור מופשט (מתמטי) של אובייקטים (ערכים) והפונקציונליות שלהם (פעולות).
מנקודת המבט של המשתמש.
לא מציין את המימוש.
ההבדלים: ADT מול Data Structure מול Implementation
  • ADT = Specification (מה) — מפרט מופשט של פעולות.
  • Data Structure = מבנה לוגי + פרוצדורות (איך, לוגית) — מבנה שמממש ADT.
  • Implementation = אובייקטים וקוד קונקרטיים (איך, בפועל).

6. Sequence ADT ופעולותיו

הגדרה: Sequence ADT
מייצג סדרה של איברים. פעולות:
  • S = Sequence() — יצירת סדרה ריקה
  • Insert(S, i, b) — הכנסת \(b\) כאיבר ה-\(i\)
  • Retrieve(S, i) — החזרת האיבר ה-\(i\)
  • Delete(S, i) — מחיקה והחזרת האיבר ה-\(i\)
  • Length(S) — אורך הסדרה
מקרים מיוחדים:
  • Insert-First, Insert-Last
  • Delete-First, Delete-Last
  • Retrieve-First, Retrieve-Last

7. Stack, Queue, Deque ADTs

הגדרה: Stack (מחסנית) — LIFO
  • Push(S, b) — הכנסת איבר לראש
  • Top(S) — החזרת האיבר בראש
  • Pop(S) — מחיקה והחזרת האיבר בראש
הכנסה ומחיקה רק מקצה אחד.
הגדרה: Queue (תור) — FIFO
  • Enqueue(Q, b) — הכנסת איבר לסוף
  • Head(Q) — החזרת האיבר בראש
  • Dequeue(Q) — מחיקה והחזרת האיבר בראש
הכנסה בסוף, מחיקה מההתחלה.
הגדרה: Deque (תור דו-כיווני)
  • Insert-First, Insert-Last
  • Delete-First, Delete-Last
  • Retrieve-First, Retrieve-Last
מכליל גם Stack וגם Queue.

8. ADT vs Data Structure vs Implementation

עקרון מפתח
המשתמש מכיר את ה-Specification (ADT) — ולא את המימוש.
המימוש יכול להשתנות לאורך הזמן בלי לשנות את הממשק.

מדדי איכות למימוש:
  • סיבוכיות זמן (Time complexity)
  • סיבוכיות מקום (Space complexity)
  • זמן עיבוד מקדים (Preprocessing time)
  • פשטות (Simplicity)
דוגמה: Stack ממומש עם מערך
  • Initialize: top = 0
  • Push(x): top = top + 1; A[top] = x
  • Top: A[top]
  • Pop: top = top - 1
זהו מימוש (Implementation) של ה-Stack ADT.

9. מימוש Sequence עם מערך

מבנה
struct עם שדות: array, maxlen (M), length (n).
צריך לדעת את maxlen מראש.
פעולה סיבוכיות
Retrieve(S, i) \(O(1)\)
Insert-Last / Delete-Last \(O(1)\)
Insert(S, i, b) / Delete(S, i) \(O(n - i + 1)\) — צריך להזיז איברים
פסאודו-קוד: Retrieve(S, i)
return S.array[i]
פסאודו-קוד: Insert-Last(S, b)
S.length ← S.length + 1
S.array[S.length] ← b
פסאודו-קוד: Insert(S, i, b)
for j ← S.length down to i
    S.array[j+1] ← S.array[j]
end
S.array[i] ← b
S.length ← S.length + 1

10. מימוש Sequence עם מערך מעגלי (Circular Array)

הגדרה: Circular Array
מוסיפים שדה start.
גישה: S.array[(S.start + i) mod S.maxlen]
כעת Insert-First ו-Delete-First גם הם \(O(1)\) — על ידי הזזת מצביע start.
פסאודו-קוד: Delete-First(S)
b ← S.array[S.start]
S.start ← (S.start + 1) mod S.maxlen
S.length ← S.length - 1
return b
שרשור (Concatenation)
\(O(\min\{n_1, n_2\} + 1)\) — מעתיקים את הקטן מבין השניים.

סיכום סיבוכיות — מערך מעגלי

פעולה סיבוכיות
Insert-First / Delete-First \(O(1)\)
Insert-Last / Delete-Last \(O(1)\)
Insert(S, i, b) / Delete(S, i) \(O(\min\{i+1,\; n-i+1\})\)
Retrieve(S, i) \(O(1)\)
Concatenation \(O(\min\{n_1, n_2\} + 1)\)

11. רשימה מקושרת יחידה (Singly Linked List)

הגדרה: Singly Linked List
אובייקט List עם שדות: first, last, length.
כל צומת (Node): item, next.
אין גישה אקראית יעילה ב-\(O(1)\).
יתרונות: גודל בלתי מוגבל, אין צורך בהקצאה מראש, סריקה \(O(n)\), שרשור \(O(1)\).
פסאודו-קוד: Insert-First(L, b)
create node B
B.next ← L.first
L.first ← B
if L.length = 0: L.last ← B
L.length ← L.length + 1

סיבוכיות: \(O(1)\).

פסאודו-קוד: Retrieve-Node(L, i)
node ← L.first
for k ← 1 to i
    node ← node.next
end
return node

סיבוכיות: \(O(i+1)\).

פסאודו-קוד: Insert(L, i, b)
A ← Retrieve-Node(L, i-1)
Insert-After(A, B)

סיבוכיות: \(O(i+1)\).

פסאודו-קוד: Insert-After(A, B)
B.next ← A.next
A.next ← B

סיבוכיות: \(O(1)\).

פסאודו-קוד: Delete-After(A)
A.next ← A.next.next

סיבוכיות: \(O(1)\).

מגבלה
Delete(L, i) ו-Delete-Last דורשים \(O(n)\) ברשימה מקושרת יחידה — כי אי אפשר לחזור אחורה.
פסאודו-קוד: Concat(L1, L2)
L1.last.next ← L2.first
L1.last ← L2.last
L1.length ← L1.length + L2.length

סיבוכיות: \(O(1)\).

12. רשימה מקושרת כפולה (Doubly Linked List)

הגדרה: Doubly Linked List
כל צומת מכיל prev וגם next.
כעת Insert-Last, Delete-Last, Delete(A) — כולם \(O(1)\).
פסאודו-קוד: Delete(A)
A.prev.next ← A.next
A.next.prev ← A.prev

סיבוכיות: \(O(1)\). שימו לב: הצומת \(A\) עצמו לא משתנה — צריך לטפל ב-L.length בנפרד.

פסאודו-קוד: Insert-After(A, B)
B.prev ← A
B.next ← A.next
A.next ← B
B.next.prev ← B

סיבוכיות: \(O(1)\).

13. השוואת סיבוכיות

פעולה Circular Array Doubly Linked List
Insert/Delete-First \(O(1)\) \(O(1)\)
Insert/Delete-Last \(O(1)\) \(O(1)\)
Insert/Delete(i) \(O(\min\{i+1,\; n-i+1\})\) \(O(\min\{i+1,\; n-i+1\})\)
Retrieve(i) \(O(1)\) \(O(\min\{i+1,\; n-i+1\})\)
Concatenation \(O(\min\{n_1, n_2\} + 1)\) \(O(1)\)
מסקנות
  • יתרון מערך מעגלי: גישה אקראית (Retrieve) ב-\(O(1)\).
  • יתרון DLL: שרשור (Concatenation) ב-\(O(1)\), גודל בלתי מוגבל.

14. מערכים דינמיים עם הכפלה (Doubling)

הרעיון
כשהמערך מלא — מקצים מערך חדש גדול יותר ומעתיקים את כל האיברים.
הגדלה ב-1 בכל פעם — יקר מדי!
עלות \(n\) פעולות Insert-Last:
\(1 + 2 + 3 + \ldots + n = \frac{n(n+1)}{2} = O(n^2)\)
אסטרטגיית הכפלה (Doubling)
כשהמערך מלא — מכפילים את הגודל.
עלות \(n\) פעולות Insert-Last (כש-\(n = 2^k\)):
\(\underbrace{(1+1+\ldots+1)}_{n \text{ insertions}} + \underbrace{(1+2+4+\ldots+\tfrac{n}{2})}_{\text{copying}} = n + (n-1) = O(n)\)
תוצאה: Amortized cost
העלות ה-amortized של Insert-Last עם אסטרטגיית הכפלה:
\(\frac{O(n)}{n} = O(1)\)

15. סיכום

Algorithm
Data
ADT
Data Structure
Implementation
נקודות מפתח
  • אלגוריתם = פרוצדורה מוגדרת-היטב לפתרון בעיה; מבנה נתונים = אחסון + אלגוריתמים.
  • Insertion Sort: best case \(O(n)\), worst case \(O(n^2)\).
  • ADT מגדיר מה, Data Structure מגדיר איך (לוגית), Implementation מגדיר איך (בפועל).
  • Sequence ADT ניתן למימוש עם מערך, מערך מעגלי, רשימה מקושרת (יחידה/כפולה).
  • Stack (LIFO), Queue (FIFO), Deque — כולם מקרים מיוחדים של Sequence.
  • מערך מעגלי: גישה אקראית \(O(1)\). DLL: שרשור \(O(1)\), גודל בלתי מוגבל.
  • מערך דינמי עם הכפלה: Insert-Last ב-amortized \(O(1)\).
צור סיכום