003.class3_4
Download
Report
Transcript 003.class3_4
מבני נתונים
שיעור – 3,4מבני נתונים בסיסיים.
מערך ,תור ,מחסנית.
מבנה נתונים -כללי
• מבנה נתונים מורכב מהגדרה מופשטת של
המבנה והפעולות האפשריות עליו.
• מימוש מבנה הנתונים בשפת תכנות .למבנה
הנתונים המופשט יש לרוב מספר דרכי מימוש.
• למבנה נתונים מסויים יש שימושים שונים
(אפליקציות) ,לעיתים מבנה נתונים משמש
בסיס למבנה נתונים מורכב יותר.
2
מערכים
Arrays
מערך הוא מבנה הנתונים הבסיסי והשימושי ביותר ברוב
שפות התכנות.
כל איבר במערך נקרא אלמנט.
כל אלמנט ניתן לגישה ע"י אינדקס מספרי.
ניתן לגשת לאיבר iבתוך מערך Aע"י ]A[i
מערכים – גישה לתאים
גודל מערך הינו קבוע (ידוע בזמן קומפילציה).
אינדקס של איבר במערך יכול להיות כל ביטוי שערכו מספר
שלם – קבוע או משתנה או ביטוי מורכב ,ואף כזה שערכו ידוע
רק בזמן ריצה.
האינדקסים של מערך בגודל nהם המספרים בין 0ל.n-1 -
התייחסות לאיבר לא קיים במערך (איבר שהאינדקס שלו קטן
מ 0 -או גדול או שווה ל )n -עשויה לגרום לטעות בזמן ריצה.
4
Arrays
מערכים
הצהרה על מערך
int[] anArray; // declares an array of integers
byte[] anArrayOfBytes;
short[] anArrayOfShorts;
long[] anArrayOfLongs;
float[] anArrayOfFloats;
double[] anArrayOfDoubles;
boolean[] anArrayOfBooleans;
char[] anArrayOfChars;
String[] anArrayOfStrings;
לשם כך יש לאתחל את,ההצהרה על מערך אינה יצירה של אובייקט מערך
:המערך
anArray = new int[10]; // create an array of integers using “new”
integer מספרים מסוג10 בשלב זה נוצר אובייקט מערך ומוקצה מקום ל
•
•
•
Arrays
מערכים
אתחול ערכי האלמנטים במערך
anArray[0] = 100; // initialize first element
anArray[1] = 200; // initialize second element
anArray[2] = 300; // etc.
•
:גישה אל האלמנטים של המערך ע"י האינדקס
System.out.println("Element 1 at index 0: " + anArray[0]);
System.out.println("Element 2 at index 1: " + anArray[1]);
System.out.println("Element 3 at index 2: " + anAray[2]);
:דרך נוספת ליצירה ואתחול מערך
int[] anArray = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
.בדרך זו אורך המערך נקבע באופן אוטומטי ע"פ מספר האלמנטים בסוגריים
: למשל מערך דו מימדי יוגדר כך,ניתן להגדיר מערכים מרובי מימדים
int [][] 2dArray=new int[5][10];
... דוגמא...למעשה נוצר מערך של מערכים
•
•
•
•
•
מערכים
Arrays
מערך עצמים
•
כאשר אברי המערך הינם עצמים ולא טיפוסים פרימיטיביים יש צורך להקצות כל
! איבר במערך במפורש
•
Line line_arr[];
line_arr = new Line[3];
line_arr[0].draw(); // BUG!
line_arr[0] = new Line();
line_arr
line_arr
line_arr
Array Object
Line Object
Array Object
Line Object
Line Object
מערכים -מוטיבציה
נתונה רשימה של מספרים המייצגים ציונים.
יש לחשב את ממוצע כל הציונים ולהדפיס את הציונים הגבוהים
מהממוצע.
הבעיה:
על מנת לחשב את הממוצע יש לקרוא את כל הציונים.
לא ניתן לדעת לפני סוף הקלט ,אלו מהציונים גבוהים מהממוצע.
לא ניתן לדעת בעת קליטת הנתונים אלו מהם צריכים להיות מודפסים.
יש לאחסן את כל הנתונים.
הפתרון :שימוש במערכים.
8
// This program reads 10 grades and prints the average grade.
דוגמא- מערכים
import java.util.Scanner;
Public static void main(String args[])
{
int grades[] = new int[10];
int i;
בכל.קליטת נתונים לתוך המערך
double sum=0;
Scanner scan = new Scanner(System.in);
איטרציה נקלט מספר אחד לתוך
for (i=0; i<10; i++) {
משמשi (המשתנהi -התא ה
grades[i]=scan.nextInt();
כמונה "הרץ" על האינדקסים של
}
.)המערך
for (i=0; i<10; i++) {
sum = sum+grades[i];
.סכימת אברי מערך
}
double average = sum/10;
System.out.println("The following grades are above average:\n");
for (i=0; i<10; i++) {
הדפסת התאים הרלוונטיים מן
if (grades[i]>average) {
.המערך
System.out.println(grades[i]);
}
}
}
מבנה נתונים מופשט
Abstract Data Type
•
•
•
•
הגדרת מבנה הנתונים ,תכונותיו והפעולות
(שיטות) הניתנות לביצוע על המבנה.
הגדרת מבנה נתונים מופשט מנותקת מאופן
המימוש של המבנה (ארגון זיכרון המחשב)
והשיטות שלו.
מגדיר מה קורה בעת שגיאות בהפעלת שיטות.
לעיתים יש כמה דרכים למימוש של מבנה
נתונים מופשט.
10
מחסנית – מבנה נתונים מופשט
.• מחסנית יכולה להכיל כל סוג של נתון
• הכנסה והוצאת איברים מהמחסנית ע"פ
LIFO "עקרון "אחרון נכנס ראשון יוצא
:• פעולות עיקריות
11
push(object o): inserts an element
object pop(): removes and returns the last inserted element
object top(): returns a reference to the last inserted element
without removing it
integer size(): returns the number of elements stored
boolean isEmpty(): indicates whether no elements are
stored
מחסנית -שגיאות
• שגיאות עלולות להתרחש בעת נסיון להפעלת
שיטה ששייכת למבנה הנתונים.
• פעולות POPו TOPלא ניתנות לביצוע על
מחסנית ריקה.
• נסיון לבצע פעולות אלו על מחסנית ריקה תגרור
הודעת שגיאה מתאימה.
12
שימושים למחסנית
• שימושים ישירים:
– היסטוריית אתרים בדפדפן
– פעולות undoלמיניהן
– שמירת משתנים מקומיים של פונקציות הקוראות
לפונקציות אחרות...
• שימושים עקיפים:
– שמירת נתונים לאלגוריתמים אחרים
– בסיס למבני נתונים מורכבים יותר
13
מחסנית -דוגמא
נגדיר מחלקה Stackעם השיטות הרלוונטיות
;)(Stack s = new Stack
;)s.push(12
;)s.push(4
;) s.push( s.top() + 2
)(s.pop
מה תכיל המחסנית?s.push( s.top() ); //
14
מימוש מחסנית ע"י מערך
•
•
•
•
•
מימוש פשוט למחסנית ניתן בעזרת מערך.
מכניסים ערכים משמאל לימין.
משתנה tמצביע על הערך העליון של המחסנית
חסרון :יש להגדיר מראש את הגודל המירבי.
חסרון :אם מנסים להכניס ערך למחסנית מלאה
מקבלים שגיאה.
…
N-1
t
S
0 1 2
15
מימוש מחסנית ע"י מערך
:) (נניח שמחזיקה ערכים שלמיםStack נגדיר את המחלקה
class public Stack{
private int S[]; //משתנה מחלקה מערך של שלמים
private int num, t;
public Stack(int n){S=new int[n]; num=n; t=-1}// בנאי מאתחל את המערך
public int size(){return t+1;}
public boolean isEmpty(){return t==-1;}
public int top(){
if isEmpty() then throw emptyStackException;//שגיאת מחסנית ריקה
else return S[t];}
public int pop(){
if isEmpty() then throw emptyStackException;//שגיאת מחסנית ריקה
else {int e = S[t]; t = t – 1; return e;}}
public void push(int new){
if size()==num then throw fullStackException;//שגיאת מחסנית מלאה
else {t = t + 1; S[t]=new;}}
•
שגיאות טיפוסיות בשימוש במחסנית
• כתוב תוכנית המדפיסה את ערכי המחסנית תוך כדי מחיקתם:
{public class Main
{)][public static void main(String args
;)(Stack s = new Stack
הכנסת ערכים למחסנית //
)for(int i = 0; i < 7; i++
;) s.push( i
הדפסת הערכים תוך כדי מחיקתם //
)for(int i = 0; i < s.size(); i++
;) )(System.out.println( s.pop
}
}
מה הטעות?
17
שגיאות טיפוסיות בשימוש במחסנית
:דרך נכונה
public class Main{
public static void main(String args[]){
Stack s = new Stack();
// הכנסת ערכים למחסנית
for(int i = 0; i < 7; i++)
s.push( i );
// הדפסת הערכים תוך כדי מחיקתם
int limit = s.size();
for(int i = 0; i < limit; i++)
System.out.println( s.pop() );
//דרך נוספת
// while( !s.isEmpty() )
// System.out.println( s.pop() );
}
}
•
דוגמא לשימוש במחסנית:
בדיקת איזון סוגריים
• בכתיבת תוכניות מחשב כאשר יש שימוש
בסוגריים () ][,}{,חייב להיות איזון בביטוי המכיל
סוגריים ,כלומר כל פתיחת סוגר חייבת להיסגר
• מחסנית היא כלי יעיל לבדיקת איזון סוגריים.
כאשר מאתרים סגירת סוגריים יש לוודא שהיא
תואמת לסוג הסוגריים האחרונים שנפתחו...
• מה האלגוריתם?
19
אלגוריתם לבדיקת איזון סוגריים
• בנה מחסנית ריקה
• עבור על הביטוי תו אחר תו עד סופו (לולאה):
– אם התו הוא פתיחת סוגריים הכנס אותו למחסנית ()push
– אם התו הוא סגירת סוגריים עשה
• אם המחסנית ריקה דווח "לא מאוזן"
• אחרת ,שלוף תו מהמחסנית ( ,)popאם התו הנ"ל לא מתאים
לסימון הסוגר דווח "לא מאוזן"
• אם לאחר מעבר על כל התווים המחסנית אינה ריקה
דווח "לא מאוזן"
20
• דוגמאif(a+{2*max(A[],5)}>7) :
חישוב ביטוי מתמטי בעזרת מחסנית
•
•
•
•
כמה זה ? 2+3*5, 4-4/2, 1+4^2
בחישוב ביטויים מתמטיים לא ניתן לבצע את
הפעולות לפי הסדר משמאל לימין .יש קדימויות
ברורות בין הפעולות (כפל לפני חיבור וכו)..
זהו אתגר בחישוב אוטומטי של ביטויים
בתוכניות מחשב.
כמה זה ??? 1 - 2 - 4 ^ 5 * 3 * 6 / 7 ^ 2 ^ 2
21
ייצוג Infixלעומת Postfix
•
•
•
•
הדרך בה אנו מורגלים לרשום ביטויים
מתמטיים נקרא ייצוג Infixלמשל 8*3+4
ייצוג Postfixניתן לחישוב ללא התחשבות
בקדימויות בין פעולות .אופן בניית הביטוי ב
Postfixלוקח בחשבון את הקדימויות.
הביטוי 2*3+4מיוצג כ 2 3 * 4 +ב Postfix
ניתן להשתמש במחסנית לחישוב ביטוי Postfix
וכמו כן להמרת ייצוג Infixל Postfix
22
המרת Infixל Postfix
• עבור על הביטוי ( Infixנניח שהוא נתון כמחרוזת) תו אחר
תו (לולאה) ובנה את ביטוי ה Postfix
–
–
–
–
אם התו הוא מספר או משתנה :הוסף אותו לביטוי החדש מימין.
אם התו הוא פעולה :שלוף ערכים מהמחסנית והוסף אותם
לביטוי החדש עד אשר תופיע פעולה בעלת קדימות נמוכה
מהפעולה הנוכחית או סוגר שמאלי או סוף המחסנית ואז הוסף
את הפעולה הנוכחית למחסנית.
אם התו הוא סוגר שמאלי :הוסף אותו למחסנית.
אם התו הוא סוגר ימני :שלוף ערכים מהמחסנית והוסף אותם
לביטוי החדש עד שניתקלים בסוגר שמאלי או בסוף המחסנית
(את הסוגריים לא מוסיפים לביטוי החדש)
• כאשר מסיימים לעבור על כל התווים יש לשלוף את שאר
הערכים מתוך המחסנית ולהוסיפם לביטוי החדש.
23
דוגמא פשוטה
Infix Expression:
PostFix Expression:
Operator Stack:
24
3+2*4
דוגמא פשוטה
Infix Expression:
PostFix Expression:
Operator Stack:
25
+2*4
3
דוגמא פשוטה
Infix Expression:
PostFix Expression:
Operator Stack:
26
2*4
3
+
דוגמא פשוטה
Infix Expression:
PostFix Expression:
Operator Stack:
27
*4
32
+
דוגמא פשוטה
Infix Expression:
PostFix Expression:
Operator Stack:
28
4
32
+*
דוגמא פשוטה
Infix Expression:
PostFix Expression:
Operator Stack:
29
324
+*
דוגמא פשוטה
Infix Expression:
PostFix Expression:
Operator Stack:
30
324*+
הדגמת פעולת האלגוריתם
Infix: 1 - 2 ^ 3 ^ 3 - ( 4 + 5 * 6 ) * 7
Postfix: 1 2 3 ^ 3 ^ - 4 5 6 * + 7 * -
31
אלגוריתם לחישוב Postfix
• עבור על הביטוי ( postfixנניח שהוא נתון כמחרוזת)
תו אחר תו משמאל לימין (לולאה):
– אם התו הוא מספר או משתנה הוסף אותו למחסנית
– אחרת ,אם התו הוא פעולה עשה:
•
•
•
•
שלוף ערך מהמחסנית והצב אותו מימין לפעולה.
שלוף ערך מהמחסנית והצב אותו משמאל לפעולה.
בצע את החישוב של הפעולה עם הערכים.
הוסף את התוצאה למחסנית.
• כאשר מסיימים לעבור על כל התווים ,הערך האחרון
שנשאר במחסנית הוא התוצאה הסופית.
32
המרה וחישוב -דוגמא
5 * (6 + 2) – 12 / 4 = 37
Infix
? = – 5 6 2 + * 12 4 /
Postfix
33
תור – מבנה נתונים מופשט
.תור יכול להכיל כל סוג של נתון
הכנסה והוצאת איברים מהתור ע"פ
FIFO "עקרון "ראשון נכנס ראשון יוצא
הכנסת איבר לסוף התור והוצאה מתחילתו
:פעולות עיקריות
34
•
•
•
•
enqueue(object): inserts an element at the end of the queue
object dequeue(): removes and returns the element at the
front of the queue
object front(): returns the element at the front without
removing it
integer size(): returns the number of elements stored
boolean isEmpty(): indicates whether no elements are
stored
תור -שגיאות
• שגיאות עלולות להתרחש בעת נסיון להפעלת
שיטה ששייכת למבנה הנתונים.
• פעולות dequeueו frontלא ניתנות לביצוע על
תור ריק.
• נסיון לבצע פעולות אלו על תור ריק תגרור הודעת
שגיאה מתאימה(throw EmptyQueueException) .
35
דוגמא- פעולות על תור
Operation
enqueue(5)
enqueue(3)
dequeue()
enqueue(7)
dequeue()
front()
dequeue()
dequeue()
isEmpty()
enqueue(9)
enqueue(7)
size()
enqueue(3)
enqueue(5)
dequeue()
36
Output
–
–
5
–
3
7
7
“error”
true
–
–
2
–
–
9
Q
(5)
(5, 3)
(3)
(3, 7)
(7)
(7)
()
()
()
(9)
(9, 7)
(9, 7)
(9, 7, 3)
(9, 7, 3, 5)
(7, 3, 5)
שימושים לתור
• שימושים ישירים:
–
–
–
–
–
רשימות המתנה
גישה למשאבים משותפים (תור למדפסת ציבורית)
תכנות מקבילי (תור לביצוע פעולות חישוב)
שימושים רבים במערכות הפעלה (קלט/פלט)
סימולציות למוקדי שירות (לחישוב מספר פקידים)
• שימושים עקיפים:
– שמירת נתונים לאלגוריתמים אחרים
– בסיס למבני נתונים מורכבים יותר
37
מימוש תור ע"י מערך
•
•
•
•
ניתן להשתמש במערך למימוש תור.
נחזיק משתנים f, rשיציינו את תחילת וסוף התור
ויעודכנו בהתאמה.
מיקום rנשאר ריק (הוא למעשה המיקום המציין
את אחד מאחורי האחרון בתור)
בעיה :נגיע מהר מאוד לסוף המערך מכוון שכל
הוצאת ערך מהתור תקדם את rלכיוון fעד
שיחרוג מגבולות המערך .למרות שמספר הערכים
בתור קטן מגודל המערך בפועל.
38
בעיית המימוש ע"י מערך
•בנה תור המבוסס על מערך בגודל )f=0, r=0( 5
•הכנס 4ערכים לתור ()f=0, r=5
•הוצא 4ערכים מהתור ()f=4, r=5
•הכנס ערך לתור – שגיאת "תור מלא" למרות שהתור ריק!!
4
3
2
1
0
פתרון :שימוש במערך בצורה מעגלית.
לא פותר את בעיית גודל מקסימלי של התור.
39
תור – מימוש ע"י מערך מעגלי
qback
qfront
qback
qback
qback
D
A
C
B
C
C
C
B
qfront
qfront
Insert elements A,B, C Remove element A Remove element B
D
C
qback
D
E
C
40
D
E
D
qfront
qback qfront
Insert element D, Insert element E
Array View
qback
qfront
Insert element D,
qfront C
qfront C
qback
Insert element D, Insert element E
Circular View
מימוש תור ע"י מערך מעגלי
שימוש במערך בגודל Nבאופן מעגלי
שני משתנים העוקבים אחר תחילת וסוף התור
fאינדקס המציין את תחילת התור
rאינדקס המציין מיקום אחד אחרי סוף התור
אינדקס rנשמר ריק במערך.
תור בתצורה רגילה
r
Q
f
0 1 2
תור בתצורה מעגלית
f
r
Q
0 1 2
פעולות תור
• נשתמש באופרטורAlgorithm size()
return (N - f + r) mod N
(שארית אחריMOD
חלוקה) לתפעול מעגלי
Algorithm isEmpty()
.של המערך
return (f = r)
Q
0 1 2
f
0 1 2
r
r
Q
42
f
פעולות תור
זורקתenqueue • שיטתAlgorithm enqueue(o)
if size() = N - 1 then
.שגיאה אם התור מלא
throw FullQueueException
else
Q[r] o
r (r + 1) mod N
Q
0 1 2
f
0 1 2
r
r
Q
43
f
פעולות תור
זורקתdequeue • שיטתAlgorithm dequeue()
if isEmpty() then
.שגיאה אם התור מלא
throw EmptyQueueException
else
o Q[f]
f (f + 1) mod N
return o
Q
0 1 2
f
0 1 2
r
r
Q
44
f
java - מימוש תור ע"י מערך
:) (נניח שמחזיקה ערכים שלמיםQueue נגדיר את המחלקה
class public Queue{
private int Q[]; //משתנה מחלקה מערך של שלמים
private int N, f, r; //משתנים המצביעים על תחילת וסוף התור
public Stack(int n){Q=new int[n];N=n; f=0; r=0}// בנאי מאתחל את המערך
public int size(){return (N-f+r) mod N;}
public boolean isEmpty(){return f==r;}
public int front(){
if isEmpty() then throw emptyQueueException;//שגיאת תור ריק
else return Q[f];}
public int dequeue(){
if isEmpty() then throw emptyQueueException;//שגיאת תור ריק
else {int e = Q[f]; f = (f+1) mod N; return e;}}
public void enqueue(int new){
if size()==N-1 then throw fullQueueException;//שגיאת תור מלא
else {Q[r]=new; r = (r+1) mod N}}
•
שימוש בתור
• כתוב תוכנית המדפיסה את ערכי התור תוך כדי מחיקתם
:ותדפיס את ממוצע האיברים והאיבר הכי גדול
public class Main{
public static void main(String args[]){
Queue q = new Queue (10);
// הכנסת ערכים לתור
for (int i=0; i<8; i++)
q.enqueue(i*2);
// הדפסת הערכים תוך כדי מחיקתם
int tmp ;
int sum = 0;
int max = q.front();
while (!q.isEmpty())
{tmp= q.dequeue() ; sum=sum+tmp;
if tmp>max then max=tmp;
System.out.println(tmp);}
System.out.println(“max=“ + max);
System.out.println(“mean=“ + sum/8);
}
46
דוגמא – סימולציית תור בבנק
•
•
•
•
תור יחיד עם נותן שירות אחד.
ידוע כי זמן השירות הממוצע לאדם הוא דקה.
כמו כן ידוע כי בכל דקה מצטרפים בין 0ל 2
לקוחות חדשים לתור.
בנוסף המערכת תחשב:
– מספר הלקוחות שקיבלו שירות בכל רגע נתון.
– זמן ההמתנה הכללי של כל הלקוחות בתור.
– זמן ההמתנה בתור המקסימלי.
47
פסאודו קוד לסימולציית תור
Initialize the queue to empty.
for ( minute = 0 ; minute < n ; ++minute ) {
if the queue is not empty, then remove the customer
at the front of the queue.
Compute a random number k between 0 and 3.
If k is 1, then add one customer to the line.
If k is 2, then add two customers to the line.
Otherwise (if k is 0 or 3), do not add any customers
to the line. }
48
סימולצית תור ל 5דקות
49
שימוש בתור לתזמון Round Robin
• כאשר מספר לקוחות מקבלים שירות ממקור יחיד
ומעוניינים לתת לכל לקוח שירות לזמן קצוב ,להחזירו
לסוף התור ולהמשיך להבא בתור.
• ניתן להשתמש בתור Qולבצע את הפעולות הבאות:
הוצאת הלקוח הבא בתור )(e = Q.dequeue
מתן שירות ללקוח Service element e
החזרת הלקוח לסוף התור )Q.enqueue(e
1.
2.
3.
The Queue
3. Enqueue the
serviced element
2 . Service the
next element
1. Deque the
next element
Shared
Service
50
תור עם עדיפויות
•
•
•
•
•
בתור עם עדיפויות איברי התור יוצאים בהתאם
לעדיפות שנקבעה מראש ולאו דוקא ע"פ סדר
כניסתם לתור.
איברים בעלי אותה העדיפות יוצאו ע"פ FIFO
ניתן לממש תור עם עדיפויות ע"י מספר תורים
רגילים.
לכל רמת עדיפות יהיה תור משלו.
האיבר הבא בתור ייקבע ע"פ התור הלא ריק
בעל העדיפות הגבוה ביותר.
51
מימוש תור עם עדיפויות
class public PQueue{
private Queue list[]; //)מערך של תורים (לכל רמת עדיפות תור נפרד
private int prior, N; //מספר רמות העדיפות
public PQueue(int p, int n){//מספר רמות העדיפות וגודל מערך לכל רמה
prior=p; N=n; list=new Queue[p];
for (int i=0;i<p;i++) list[i]=new Queue(n);}// בנאי מאתחל את המערך
public int size(){int sz; for (int i=0;i<num;i++) sz=sz+list[i].size();return sz;}
public boolean isEmpty(){boolean e=True; for (int i=0;i<num;i++) e=e &&
list[i].isEmpty(); return e;}
public int front(){for (int i=0;i<n;i++) if !list[i].isEmpty then return
list[i].front(); throw emptyQueueException;}
public int dequeue(){for (int i=0;i<n;i++) if !list[i].isEmpty then return
list[i].dequeue(); throw emptyQueueException;}
public void enqueue(int new, int priority){//ערך חדש ורמת עדיפותו
if list[priority].size()==N-1 then throw fullQueueException;//תור מלא
else list[priority].enqueu(new);}
52