Download as pdf or txt
Download as pdf or txt
You are on page 1of 89

‫מספר קורס‪202-1-1011 :‬‬

‫מרצה‪ :‬מיכל שמש‬


‫מתרגל‪ :‬שקד מטר‬
‫יוצר המסמך‪ :‬יעל גייזלר‬

‫סוג החומר‪ :‬סיכום הרצאות‬

‫שם הקורס‪ :‬מבוא למדעי המחשב‬

‫‪2021‬‬
‫באדיבות מדור אקדמיה‪ ,‬אגודת הסטודנטים‪ ,‬אוניברסיטת בן גוריון‪.‬‬
‫‪www.bgu4u.co.il‬‬
‫תוכן עניינים‬
‫הרצאה ‪ – 1‬הכרת הקורס‪ ,‬אלגוריתם ‪2 .................................................................................................‬‬
‫הרצאה ‪ – 2‬מתחילים לתכנת ‪5 ...........................................................................................................‬‬
‫הרצאה ‪ – 3‬אלמנטים בסיסיים בשפה ‪8 ...............................................................................................‬‬
‫הרצאה ‪ – 4‬מערכים ‪13 .....................................................................................................................‬‬
‫הרצאה ‪ – 5‬פונקציות ‪16 ...................................................................................................................‬‬
‫הרצאה ‪ – 6‬חיפוש ומיון ‪19 ................................................................................................................‬‬
‫הרצאה ‪ – 7‬המשך מיון‪ ,‬חתימות ‪23 ....................................................................................................‬‬
‫הרצאה ‪ – 8‬מחרוזות וטיפוסים עוטפים ‪27 ............................................................................................‬‬
‫הרצאה ‪ – 9‬בעיית הסיפוק הבוליאני ‪31 ........................................................................................ SAT‬‬
‫הרצאה ‪ – 10‬רקורסיה ‪32 ................................................................................................................ 1‬‬
‫הרצאה ‪ – 11‬רקורסיה ‪36 ................................................................................................................ 2‬‬
‫הרצאה ‪ – 12‬מבוא לתכנות מובנה עצמים ‪39 ..................................................................................... 1‬‬
‫הרצאה ‪ – 13‬מבוא לתכנות מובנה עצמים ‪43 ..................................................................................... 2‬‬
‫הרצאה ‪ – 14‬מבוא לתכנות מובנה עצמים ‪45 ..................................................................................... 3‬‬
‫הרצאה ‪ – 15‬מבוא לתכנות מובנה עצמים ‪49 ..................................................................................... 4‬‬
‫הרצאה ‪ – 16‬ממשקים ‪52 ..................................................................................................................‬‬
‫הרצאה ‪ – 17‬המשך ממשקים ‪55 ........................................................................................................‬‬
‫הרצאה ‪ – 18‬עצים בינאריים ‪59 ..........................................................................................................‬‬
‫הרצאה ‪ – 19‬עצי חיפוש בינאריים ‪64 ..................................................................................................‬‬
‫הרצאה ‪ – 20‬איטראטורים על עצים‪ ,‬מחסנית(‪ ,)Stack‬תור(‪67 ..................................................... )Queue‬‬
‫הרצאה ‪ – 21‬שימושים במחסנית‪ ,‬ביטויים בנוסח ‪72 .................................................................. Postfix‬‬
‫הרצאה ‪ – 22‬גרפים ‪76 ................................................................................................................... 1‬‬
‫הרצאה ‪ – 23‬גרפים ‪80 ................................................................................................................... 2‬‬
‫הרצאה ‪ – 24‬מיון רקורסיבי‪82 ..................................................................................... memoization ,‬‬
‫הרצאה ‪ – 25‬המשך ‪85 ................................................................................................ memoization‬‬
‫‪25.10.18‬‬

‫הרצאה ‪1‬‬
‫כללי‪:‬‬
‫מיכל שמש ‪shemesh@cs.bgu.ac.il‬‬
‫משרד ‪37/319‬‬
‫שעות קבלה יום ה' ‪10-12‬‬
‫אתר הקורס ‪www.cs.bgu.ac.il/~intro191‬‬

‫מרכיבי הציון בקורס‪:‬‬


‫מבחן ‪66%‬‬
‫בוחן ‪)16.11.18( 20%‬‬
‫עבודות בית ‪12%‬‬
‫תרגילונים ‪2%‬‬
‫*במידה והציון במבחן גבוה מהציון בבוחן (כלומר חל שיפור)‪ ,‬משקל הבוחן בציון הסופי יורד‪.‬‬

‫דיקנאט הסטודנטים‬
‫פנינה ישראלי – עו"ס‬
‫‪pninai@bgu.ac.il‬‬
‫‪08-6528584‬‬

‫אלגוריתם‬
‫סדרת הוראות שביצוען יובל אותנו לפתרון הבעיה‪.‬‬
‫‪ 5‬תכונות לאלגוריתם ע"פ ‪:Knuth‬‬
‫סופיות‬ ‫‪.1‬‬
‫מוגדרות‬ ‫‪.2‬‬
‫קלט‬ ‫‪.3‬‬
‫פלט‬ ‫‪.4‬‬
‫אפקטיביות (מחזיר תשובה נכונה)‬ ‫‪.5‬‬
‫ניתן לתאר אלגוריתם בכל צורה‪/‬שפה שהיא‪ ,‬על עוד הוא מובן לכותב ולמבצע‪.‬‬

‫בעיה לדוגמה‪:‬‬
‫קלט‪ :‬מס' שלם וחיובי ‪X‬‬
‫פלט‪ :‬מס' הספרות של ‪X‬‬

‫‪2‬‬
‫דוגמה לאלגוריתם‪:‬‬

‫חילוף מותנה‬
‫קלט‪ :‬שני מספרים ‪x, y‬‬
‫פלט‪ :‬אותם שני מספרים ‪ x, y‬כך ש ‪x≤y‬‬
‫‪x=2 x=4 x=1‬‬ ‫דוגמה‪x=2 x=4 x=3 :‬‬
‫‪y=5 y=4 y=3‬‬ ‫‪y=5 y=4 y=1‬‬

‫‪GCD‬‬
‫בעיית מציאת המחלק המשותף המקסימלי – ‪Greatest Common Divisor‬‬
‫קלט‪ m, n :‬חיוביים שלמים‬
‫פלט‪GCD(m,n) :‬‬
‫*תזכורת‪ i :‬מחלק את ‪ m‬אם שארית החלוקה של ‪ m‬ב‪ i-‬היא ‪.0‬‬
‫מחלקים‬
‫‪1‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪4‬‬ ‫‪6 8 12‬‬ ‫‪16‬‬ ‫‪24‬‬ ‫‪48‬‬ ‫‪m=48‬‬
‫‪1‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪5‬‬ ‫‪6 10 15‬‬ ‫‪30‬‬ ‫‪n=30‬‬
‫‪GCD(48,30)=6‬‬

‫דוגמה לאלגוריתם נאיבי לפתרון בעיית ‪:GCD‬‬


‫*אלגוריתם נאיבי = חיפוש ממצה בסיסי ובלתי יע‪h‬ל‪ ,‬כאשר מס' הנתונים שיש לחפש בהם הוא גדול‪.‬‬
‫קלט‪ m,n :‬שלמים חיוביים‬ ‫‪.1‬‬
‫)‪i ← min(m,n‬‬ ‫‪.2‬‬
‫כל עוד לא מתקיים (‪ i‬מחלק את ‪ m‬וגם ‪ i‬מחלק את ‪)n‬‬ ‫‪.3‬‬
‫‪i ← i-1 .3.1‬‬
‫פלט‬ ‫‪.4‬‬

‫‪3‬‬
‫פעולת מציאת שארית מסומנת ב‪ %-‬ונקראת ‪ .modulo‬השארית מסומנת ב‪.)remainder( r-‬‬
‫‪0≤r< n ,r = m%n ↔ m = p × n + r‬‬

‫האלגוריתם של אוקלידס‪:‬‬
‫‪m‬‬ ‫‪n‬‬ ‫‪r‬‬ ‫קלט‪ m,n :‬שלמים חיוביים‬ ‫‪.1‬‬
‫‪30‬‬ ‫‪48‬‬ ‫‪30‬‬ ‫‪r ← m%n‬‬ ‫‪.2‬‬
‫‪48‬‬ ‫‪30‬‬ ‫‪18‬‬ ‫כל עוד (‪)r≠0‬‬ ‫‪.3‬‬
‫‪30‬‬ ‫‪18‬‬ ‫‪12‬‬ ‫‪m←n .3.1‬‬
‫‪n ←r .3.2‬‬
‫‪18‬‬ ‫‪12‬‬ ‫‪6‬‬
‫‪r←m%n .3.3‬‬
‫‪12‬‬ ‫‪6‬‬ ‫‪0‬‬
‫פלט‪n :‬‬ ‫‪.4‬‬
‫האלגוריתם תמיד עוצר ומחזיר תשובה נכונה לכל קלט‪ .‬בכל שלב ‪ m,n‬מקבלים ערכים חדשים‪.m’,n’ :‬‬
‫קבוצת המחלקים המשותפים של ‪ = m,n‬קבוצת המחלקים המשותפים של ’‪ .m’,n‬ניתן להוכיח בהכלה דו כיוונית‪.‬‬

‫‪4‬‬
‫‪26.10.18‬‬

‫הרצאה ‪2‬‬
‫מתחילים לתכנת‬

‫תהליך כתיבת אלגוריתם‬


‫‪ .1‬נכתוב אלגוריתם בשפה חופשית‪.‬‬
‫‪ .2‬נכתוב אותו בשפת התכנות ‪.JAVA‬‬
‫‪ .3‬תרגום התכנית לשפה מכונה בשתי שיטות‪:‬‬
‫*קומפילציה (הידור)‬
‫*אינטרפרטציה (פירוש)‬
‫בשפת ‪ JAVA‬יש שילוב של שתי השיטות‪:‬‬
‫‪ .1‬בשלב הראשון מתבצעת קומפילציה של התכנית שכתובה בשפת ‪ JAVA‬לתכנית בשפת מגונה וירטואלית‬
‫‪.Java Virtual Machine – JVM‬‬
‫‪ .2‬האינטרפרטר (ה‪ )JVM‬עובר על התכנית שורה‪-‬שורה‪ ,‬מתרגם לשפת מכונה ומריץ‪.‬‬

‫דוגמה ‪ – 1‬האלגוריתם של אוקלידס בשפת ‪:java‬‬


‫;‪import java.util.Scanner‬‬
‫{ ‪public class GCD‬‬
‫{)‪public static void main(String[] args‬‬
‫;)‪Scanner myScanner = new Scanner(System.in‬‬
‫;)(‪int m = myScanner.nextInt‬‬
‫;)(‪int n = myScanner.nextInt‬‬
‫;‪int r = m%n‬‬
‫{ )‪while (r != 0‬‬
‫;‪m=n‬‬
‫;‪n=r‬‬
‫;‪r = m%n‬‬
‫}‬
‫;)‪System.out.println(n‬‬
‫}‬
‫}‬

‫דוגמה ‪ – 2‬אלגוריתם למציאת מס' הספרות של מספר‪:‬‬


‫קלט‪ x :‬שלם חיובי‬ ‫‪.1‬‬
‫‪counter←1‬‬ ‫‪.2‬‬
‫כל עוד (‪)x≥10‬‬ ‫‪.3‬‬
‫‪x = x/10 .3.1‬‬
‫‪counter←conter+1 .3.2‬‬
‫פלט‪counter :‬‬ ‫‪.4‬‬

‫‪5‬‬
import java.util.Scanner;
public class DIGITS {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int x = myScanner.nextInt();
int counter = 1;
while (x >= 0) {
x = x/10
counter = counter+1;
}
System.out.println(counter);
}
}

GCD ‫ – אלגוריתם נאיבי למציאת‬3 ‫דוגמה‬


import java.util.Scanner;
public class NaiveGCD {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int m = myScanner.nextInt();
int n = myScanner.nextInt();
int i = x;
while (!(m%i == 0 & n%i == 0)) {
i = i-1
}
System.out.println(i);
}
}

:‫ – בדיקת ראשוניות של מספר‬4 ‫דוגמה‬


√number-‫ ל‬2 ‫ נעבור על המהלכים האפשריים בין‬,number ‫בהינתן מספר‬
.)‫ מס' הוא ראשוני עד אשר הוכח אחרת (=ימצא לו מחלק‬:‫הנחה‬
.)‫ ניתן לבדוק מחלקים רק עד שורש המספר (כולל‬:‫טענה‬

√m ‫ הקטן או שווה ל‬p ‫ מחלק‬m-‫ נוכיח שקיים ל‬.‫ פריק‬m-‫ נניח ש‬.m>1 ‫ נתון‬:‫הוכחה‬
p×q=m ‫ וגם‬1<p≤q<m ‫ שלמים כך ש‬p,q ‫ פריק ← קיימים‬m
p=q -‫נניח ש‬ •
p×q=p×p=m
p2 =m
p=√m
)‫ (ללא הגבלת הכלליות‬p<q ‫ נניח‬.p≠q :‫אחרת‬ •
p×q<p×p=m
p2 <m
p<√m

6
import java.util.Scanner;
public class IsPrime {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int number = myScanner.nextInt();
boolean isPrime =true;
int divisor = 2;
while (divisor*divisor <= number & isPrime) {
if (number%divisor == 0){
isPrime = false;
}
divisor = divisor+1;
}
System.out.println(isPrime);
}

:)‫ (כולל‬n ‫ – תכנה שמדפיסה את כל המספרים הראשוניים עד‬5 ‫דוגמה‬


import java.util.Scanner;
public class AllPrimes {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int n = myScanner.nextInt();
int number = 2;
while (number <= n){
boolean isPrime =true;
int divisor = 2;
while (divisor*divisor <= number & isPrime) {
if (number%divisor == 0)
isPrime = false;
divisor = divisor+1;
}
if (isPrime)
System.out.println(number);
number = number+1
}
}
}

7
28.10.18

3 ‫הרצאה‬
‫אלמנטיים בסיסיים בשפה‬
:‫משתנים‬
.‫ ניתן לרשום בו ערך‬.‫משתנה הוא מקום בזכרון בעל שם וטיפוס‬
:‫דוגמאות‬
type name value
int n 50
int number 2
boolean isPrime true
Scanner myScanner ?

boolean isPrime; , int n; :)‫על כל משתנה צריך להצהיר (ההצהרה תתבצע פעם אחת‬
isPrime = true , n = 50; :‫לפני שעושים שימוש במשתנה יש לבצע השמה מתאימה‬
:‫ טיפוסים פרייטיביים‬8 java-‫ב‬
long(64 bit), int(32 bit), short(16 bit), byte(8 bit) :‫שלמים‬ .1
double(64 bit), float (32 bit) :‫ממשיים‬ .2
boolean (8 bit) :‫שקר‬/‫אמת‬ .3
char (16 bit) :‫תו‬ .4
byte ‫ = המספר המינימלי שניתן לייצג ע"י‬1 + byte ‫המספר המקסימלי שניתן לייצג ע"י‬

:‫ אופרטור ההשמה‬,‫משפטי השמה‬


type name value Int a = 2;
int a 2 int b = a+1;
int b 3
double c 6.8 double c = 3.4×2;
char d ‘d’ char d = ‘d’;
char e ‘3’
boolean isItTrue True char e = ‘3’;
double f 9.8 boolean isItTrue = (1+1 = = 2);
double f = b + c;

8
‫ביטויים אריתמטיים‬
‫ביטוי אריתמטי הוא ביטוי חשבוני‪ .‬ביטוי חשבוני מורכב מ‪:‬‬
‫סוגריים‬ ‫אופרטורים‬ ‫משתנים‬ ‫ערכים‬
‫)(‬ ‫‪+‬‬ ‫‪a‬‬ ‫‪2‬‬
‫‪-‬‬ ‫‪b‬‬ ‫‪-1‬‬
‫*‬ ‫‪c‬‬ ‫‪-17.5‬‬
‫‪%‬‬

‫ביטוי אריתתמטי בנוי כך‪:‬‬


‫ערך או משתנה – ‪ 3‬או ‪number‬‬ ‫•‬
‫<ביטוי אריתמטי><אופרטור><ביטוי אריתמטי‬ ‫•‬
‫*הערכים מצידי האופרטורים נקראים אופרנדים‪.‬‬

‫סדר פעולות‬
‫כפל וחילוק קודמים לחיבור וחיסור‬
‫‪int x = 3 + 4 – 2 * 5 * 5‬‬
‫אם לא בטוחים – סוגריים‪.‬‬

‫העמסת אופרטורים‬
‫אופרטור יכול לפעול בצורה שונה בהתאם לאופרנדים (=לנתונים) עליהם הוא פועל‪.‬‬
‫דוגמה‪ :‬אופרטור החילוק‬
‫‪type‬‬ ‫‪name‬‬ ‫‪value‬‬
‫;‪int i = 5/2‬‬
‫‪int‬‬ ‫‪i‬‬ ‫‪2‬‬
‫‪double‬‬ ‫‪x‬‬ ‫‪2.0‬‬ ‫;‪double x = 5/2‬‬
‫‪double‬‬ ‫‪y‬‬ ‫‪2.5‬‬
‫‪double‬‬ ‫‪z‬‬ ‫‪2.5‬‬ ‫;‪double y = 5.0/2‬‬
‫;‪double z = 5.0/2.0‬‬

‫העמסה על האופרטור פלוס ‪ +‬כשפוגש מחרוזת‬


‫*מחרוזת מסומנת בגרשיים " ‪"..‬‬
‫;)”‪System.out.println(6 + 3 + “is the answer‬‬
‫”‪“9 is the answer‬‬
‫האופרטור יפעל משמאל לימין‪ .‬כיוון שמחרוזת היא כללית יותר מערך‪ ,‬האופרטור משרשר את הערכים אחד לשני‪9 .‬‬
‫יהפוך למחרוזת "‪ ."9‬מה שיודפס זה‪:‬‬
‫‪.9 is the answer‬‬

‫‪9‬‬
‫דוגמה נוספת‪:‬‬
‫;)‪System.out.println(“The answer is: “+6+3‬‬
‫”‪“The answer is: 6‬‬
‫”‪“The answer is 63‬‬
‫‪.The answer is 63‬‬

‫המרה – ‪conversion ,casting‬‬


‫ניתן לבצע המרה מטיפוס פרימיטיבי אחד לאחר‪.‬‬
‫המרה יכולה להיות בטוחה (מרחיבה) – כמו ב ;‪ .double i = 2‬הביטוי שיוצג יהיה ‪.2.0‬‬ ‫•‬
‫המרה יכולה להיות מסוכנת (מצמצמת) – כמו ב ;‪ .int j = 2.3‬במקרה כזה הקומפיילר יציג שגיאה‬ ‫•‬
‫‪ typecast is not match‬והמשתמש יצטרך "לאשר" את שגיאה זו‪ .‬פעולה זו תקרא ‪ .casting‬כדי‬
‫שהקומפיילר יאשר את השגיאה יש לכתוב את המשתמש המצומצם בסוגריים (‪.)int‬‬
‫‪type‬‬ ‫‪name‬‬ ‫‪value‬‬ ‫דוגמאות‪:‬‬
‫‪double‬‬ ‫‪x‬‬ ‫‪2.0‬‬ ‫;‪int j = (int) 2.3‬‬
‫‪int‬‬ ‫‪k‬‬ ‫‪2‬‬ ‫;)‪int k = (int) (5.0/2‬‬
‫‪int‬‬ ‫‪f‬‬ ‫‪2‬‬ ‫;‪int f = ((int)5.0)/2‬‬

‫ביטויים בוליאניים‬
‫מורכבים מ‪:‬‬
‫ליטרלים (ערכים) ‪true, false‬‬ ‫•‬
‫משתנים (מטיפוס ‪)boolean‬‬ ‫•‬
‫פעולות השוואה‪<= ,>= ,< ,> ,!= ,== :‬‬ ‫•‬
‫פעולות לוגיות‪&& ,|| ,)and( & ,)or( | ,)not( ! :‬‬ ‫•‬
‫ביטוי בוליאני מורכב כך‪:‬‬
‫ליטרל או משתנה‬ ‫•‬
‫<ביטוי בוליאני><אופרטור><ביטוי בוליאני>‬ ‫•‬
‫<ביטוי אריתמטי><אופרטור><ביטוי אריתמטי>‬ ‫•‬
‫<ביטוי בוליאני>!‬ ‫•‬

‫אופרטורים סדרתיים‪:‬‬
‫יהיו ‪ a‬ו‪ b-‬ביטויים בוליאניים‬
‫הביטוי ‪ – a&b , a|b‬מחשבים קודם כל את ‪ a‬בנפרד ואת ‪ b‬בנפרד ואח"כ מופעל האופרטור‪.‬‬ ‫•‬
‫הביטוי ‪ a&&b , a||b‬פועלים סדרתית‪ .‬קודם כל מחושב ‪ .a‬לפי תוצאת החישוב של ‪ a‬מוחלט האם לחשב‬ ‫•‬
‫את ‪ b‬ואז מופעל האופרטור‪ .‬דוגמה‪:‬‬
‫;‪double x = 0‬‬
‫)‪ - (x != 0) && (100/x ==2‬לאחר חישוב הביטוי הראשון יתקבל ‪ false‬והאופרטור לא יופעל‪.‬‬

‫;‪double x = 0‬‬
‫)‪(x != 0) & (100/x ==2‬‬
‫‪false‬‬ ‫שגיאה‬

‫‪10‬‬
‫ רק אז‬,false ‫ מחזיר‬a ‫ אם‬.‫ והאופרטור לא יופעל‬b ‫ אין צורך לבדוק את‬true ‫ מחזיר‬a ‫ אם‬- || ‫באופרטור‬
.b ‫האופרטור יופעל ויבדוק את‬

‫פעולות קלט ופלט‬


)‫ קולטת מהמשתמש ערך שלם (או כל ערך אחר כתלות בטיפוס שנזין‬- myScanner.nextInt(); :‫פעולת קלט‬
i‫ פולטת את הערך המוזן ב‬- System.out.println (i); :‫פעולת פלט‬

block ‫משפט‬
{ { -‫משפט בלוק הוא אוסף משפטים לביצוע סדרתי עטופים ב‬

‫משפטי תנאי‬
if (‫{ )ביטוי בוליאני‬
.
‫ בלוק פקודות‬.
.
{

if else ‫משפט‬
if (‫)תנאי בוליאני‬
}‫{בלוק פקודות‬
else
}‫{בלוק פקודות‬

while ‫עוד‬-‫לולאת כל‬


while (‫)ביטוי בולאיני‬
}‫{בלוק פקודות‬

for ‫לולאת‬
for (<‫)>קידום<;>תנאי<;>אתחול‬
}‫{בלוק פקודות‬
:‫ נוכל לכתוב גם כך‬AllPrimes ‫את התכנית‬
import java.util.Scanner;
public class AllPrimes {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int n = myScanner.nextInt();
int number = 2
while (number <= n){
boolean isPrime =true;
For (int divisor = 2; divisor*divisor<=number & is prime; divisor = divisor+1){
if (number%divisor == 0)
isPrime = false;
divisor = divisor+1;
}
if else (isPrime)
System.out.println(isPrime);
number = number+1
}
}
}

11
‫‪( scope‬טווח החיים) של משתנה‪:‬‬
‫משתנה קיים אך ורק בתוך הבלוק שבו הוא הוצהר‪ .‬כאשר יוצאים מהבלוק‪ ,‬טבלת הערכים של אותו בלוק נמחקת‬
‫ואיתה כל המשתנים שהוצהרו בה‪.‬‬
‫אם משתנה הוגדר מחוץ לבלוק וערכו השתנה בתוך הבלוק‪ ,‬הערך החדש שלו נשאר‪.‬‬

‫‪12‬‬
‫‪1.11.18‬‬

‫הרצאה ‪4‬‬
‫תזכורת ‪ -‬תוכנית המדפיסה את כל הראשוניים עד ‪n‬‬
‫;‪import java.util.Scanner‬‬
‫{ ‪public class AllPrimes‬‬
‫{)‪public static void main(String[] args‬‬
‫;)‪Scanner myScanner = new Scanner(System.in‬‬
‫‪int n = myScanner.nextInt(); //50‬‬
‫{)‪for (int number =2 ; number <= n; number = number +1‬‬
‫;‪boolean isPrime =true‬‬
‫;‪int divisor = 2‬‬
‫{ )‪for (int divisor=2; divisor*divisor <= number & is prime; divisor = divisor+1‬‬
‫)‪if (number%divisor == 0‬‬
‫;‪isPrime = false; = divisor+1‬‬
‫}‬
‫)‪if (isPrime‬‬
‫;)‪System.out.println(number‬‬
‫}‬
‫}‬
‫}‬

‫המשפט היסודי של האריתמטיקה‬


‫כל משפט שלם וחיובי הגדול מ‪ 1-‬הינו ראשוני או שניתן להצגה כמכפלת גורמים ראשוניים‪ .‬מכפלה זו יחידה עד כדי‬
‫סדר‪ .‬למשל‪2×2×5=20 :‬‬
‫ע"פ משפט זה‪ ,‬רואים כי אין צורך לבדוק את כל המחלקים האפשריים בבדיקת המספרים הראשוניים‪ .‬יש צורך לעבור‬
‫על מספרים ראשוניים בלבד‪.‬‬

‫מערכים‬
‫הגדרה‪ :‬רצף תאים מטיפוס מסויים בזכרון‪.‬‬
‫הצהרה‪.int[] arr = new int [3]; :‬‬
‫המספר שבסוגריים מסמן את מספר התאים שמוקצים‪ .‬חייבת להיות התאמה בין הטיפוסים‪.‬‬
‫‪ = arr‬קיצור של ‪ ,array‬מערך‬
‫המקומות של ‪ arr‬מסומנים משמאל לימין ונספרים מ‪.0‬‬
‫את הערכים של ‪ arr‬בדרך כלל מאתחלים ל‪.0-‬‬
‫;]‪int[] arr1 = new int[3‬‬
‫;‪int x = 3‬‬
‫;‪arr1[0]=1‬‬
‫;‪arr1[1] = 2‬‬
‫;‪arr1[2] = arr[1]+1‬‬

‫‪13‬‬
‫;}‪int[] arr2 = {1,2,3‬‬
‫‪ arr1 – S.o.p(arr1 == arr2); //false‬ו‪ arr2‬מצביעים על מקומות שונים ולכן יודפס ‪.false‬‬
‫;‪int y = 3‬‬
‫‪S.o.p (x == y); //true‬‬
‫‪int [] arr3 = arr2‬‬
‫‪S.o.p (arr2 == arr3); //true‬‬

‫מערך שהוקצה עבורו מקום בזיכרון‬


‫ניתן לברר כמה תאים הוקצו עבורו ע"י שימוש במילה ‪.length‬‬
‫‪( S.o.p(arr3.length); //3‬כלומר הוקצו מקומות ‪)0,1,2‬‬
‫ניתן להכריז על כך שלא הוקצה מקום (כלומר הוקצו ‪ 0‬מקומות) ע"י שימוש בערך ‪.null‬‬

‫;)]‪ S.o.p(arr3[3‬שגיאת זמן ריצה‬


‫;]‪int[]= arr[4] = new int [0‬‬
‫‪S.o.p(arr4.length); //0‬‬
‫;‪int[] arr5 = null‬‬
‫;)‪ S.o.p(arr5.length‬שגיאת זמן ריצה‬
‫;‪int[] arr6‬‬
‫;)]‪ S.o.p(arr6[0‬שגיאת קומפילציה‬
‫;]‪arr5 = new int[2‬‬
‫;]‪arr5=new int[3‬‬

‫כדי להדפיס מערך מסויים צריך לוודא שהוקצה עבורו מקום בזכרון‪ ,‬כלומר שהוא לא ‪.null‬‬

‫‪14‬‬
arr2 ‫לולאה המדפיסה את ערכי התאים של‬
for (int i=0; arr2!=null && i < arr2.length; i = i+1){
System.out.println(arr2[i]);
}

‫ ) מכילים את אותם ערכים בדיוק‬arr3 ,arr1( ‫תכנית הבודקת אם שני מערכים‬


boolean isEqual = (arr1.length == arr3.length); //true
for (int i=0; I < arr1.length & isEqual; i = i+1){
if (arr1[i] != arr3[i])
isEqual = false;
}
System.out.println(isEqual);

‫ עם מערכים‬- n ‫תכנית המדפיסה את כל הראשוניים עד‬


‫ ובעזרת הערכים השמורים בו‬,‫ נשמור בו כל ראשונים שנמצא‬.primes ‫נשכתב את התכנית כך שניצור מערך בשם‬
‫ הבא ראשוני‬number‫נבדוק האם ה‬
import java.util.Scanner;
public class AllPrimes {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int n = myScanner.nextInt(); //50
int[] primes = new int[n];
int numberOfPrimes = 0; // ‫מונה את הראשוניים שנמצאו עד כה ומספר לנו מהו האינדקס של התא הפנוי הבא‬
for (int number =2 ; number <= n; number = number +1){
boolean isPrime =true;
for (int primesIndex = 0; primesIndex < numberOfPimes &&
primes[primesIndex]*primes[primesIndex] <= number; primesIndex = primesIndex+1){
if(number%primes[primesIndex] == 0)
isPrime = false;
}
if (isPrime){
primes[numberOfPrimes] = number;
numberOfPrimes = numberOfPrimes+1;
}

System.out.println(number);
}
}
}

15
‫‪2.11.18‬‬

‫הרצאה ‪5‬‬
‫פונקציות חריגות ו‪Testing-‬‬
‫פונקציה היא בלוק של פקודות שמאוגדות יחד‪ .‬יש לה שם ותפקיד‪.‬‬
‫כדי "להתחיל" פונקציה כותבים ‪.public static‬‬

‫פונקציית ‪Main‬‬
‫סדר הכתיבה של הפונקציות בתוך המחלקה לא משנה‪ ,‬כיוון שהתכנית מיד תחפש קודם כל את פונקציית ‪.main‬‬
‫הפונקציה יכולה לקרוא לפונקציה השניה כמה פעמים שתרצה‪.‬‬
‫בפונקציה תמיד תהיה הצהרה על משתנה שישלח אותנו לפונקציה הבאה‪ .‬כדי לחזור לפונקציית ‪ main‬נכתוב‬
‫)(‪ return‬בהתאם למשתנה שהוצהר‪.‬‬
‫דוגמה לפונקציה למציאת ‪GCD‬‬
‫;‪import java.util.Scanner‬‬
‫{ ‪public class GCD‬‬
‫{)‪public static void main(String[] args‬‬
‫;)‪Scanner myScanner = new Scanner(System.in‬‬
‫;)(‪int m = myScanner.nextInt‬‬
‫;)(‪int n = myScanner.nextInt‬‬
‫;)‪int result = gcd(m,n‬‬
‫;)‪System.out.println(“The gcd of “ +x+ “and “ +y+ “is: “ +result‬‬
‫}‬
‫}‬

‫"‪ "int result‬יפנה אותנו לפונקציית ‪:gcd‬‬

‫{)‪public static int gcd(int m, int n‬‬


‫;‪int r = m%n‬‬
‫{ )‪while (r != 0‬‬
‫;‪m=n‬‬
‫;‪n=r‬‬
‫;‪r = m%n‬‬
‫}‬
‫;‪return n‬‬
‫"‪ "return n‬יחזיר אותנו בחזרה לפונקציית ‪.Main‬‬

‫‪16‬‬
‫מבנה הפוקנציה‬
public static <return value type><name>(‫{)רשימת הפרמטרים‬
.
int output;
.
.
.
return output;
}
‫ אם לא‬.)‫ (כלומר כלום‬void ‫ אך ניתן לציין שהערך שמחזירה הפונקציה היא‬,‫הפונקציה תמיד מחזירה ערך מסויים‬
.return ‫רוצים שהפונקציה תחזיר ערך מסויים אפשר גם לוותר על השורה‬

‫דוגמאות נוספות‬
‫פונקציה למציאת מחלקים של מספר‬
import java.util.Scanner;
public class Divisors {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int n = myScanner.nextInt();
int divisor = findDivisor(n);
if (divisor == -1)
System.out.println(n+” is prime”);
else
System.out.println(divisor +” divides ”+n);
}
} //end of class

findDivisor ‫ יפנה אותנו לפונקציה‬int divisor


//assume n>0
public static int findDivisor(int n){
int output = -1;
for (int divisor = 2; divisor*divisor <= n & output == -1; divisor = divisor +1){
if (n%divisor == 0)
output = divisor;
}
return output;
}

17
//assume n>0, 1<divisor<n
public static boolean isDivisor(int n, int divisor){
return (n&divisor == 0);
}
**continuing from main(next function)**
System.out.println(“Testing “ + n1 + “: “);

if (findDivisor(n3) != -1)

System.out.println(“error“);

:)‫ הראשונה שכתבנו‬main‫ שבודקת את נכונות הפונקציות (יכולה להחליף את פונקצית ה‬main ‫דוגמה לפונקציית‬
import java.util.Scanner;
public class Divisors {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int n1 = 25;
int n2 = 100;
int n3 = 31;
System.out.println(“Testing “ + n1 + “: “)
if (isDivisor (n1, findDivisor(n1)))
System.out.println(“success!”);
else
System.out.println(“error”);
}
} //end of class

**back to isDivisor**

‫ היא‬,‫פונקציית בדיקה (ופונקציות באופן כללי) לא צריכה לדעת כיצד פועלות הפונקציות האחרות איתן הן עובדות‬
.‫צריכה רק את שמות הפונקציה ואת שמות הפרמטרים‬

‫בעיית המיון‬
.‫ מסודרים בסדר לא יורד‬,‫ מערך עם אברי מערך הקלט‬:‫ פלט‬.‫ מערך של מספרים שלמים‬:‫קלט‬
{1, 2, 5, 7, 10} :‫דוגמה למערך ממויין‬
?‫בהינתן מערך של מספרים – כיצד נוודא שהוא ממויין‬
- isSorted ‫פונקציית‬
import java.util.Scanner;
public class isSorted {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int[] arr = {5,7,3,2};
boolean ans = isSorted(arr);
System.out.println(“ans”);

} //end of class

// assume arr != null


public static boolean isSorted(int[] arr){
if (arr == null)
throw new IllegalArgumentExaption(“input array is null”);
boolean sorted = true;
for (int i = 0; i<arr.length-1 & sorted; i = i+1){
if (arr[i] > arr [i+1])
sorted = false;
}
return sorted;
}

18
2.11.18

6 ‫הרצאה‬
‫חיפוש ומיון‬
void ‫דוגמה עם פונקציה מסוג‬
import java.util.Scanner;
public class Change {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int a = 3;
int b = 5;
change (a,b);
System.out.println(a + “, “ + b);

} //end of class

public static int change(int a, int b){


a = 10;
b = 20;
}
return;

.“3, 5” ‫ לא מחזירה ערך בסוף ולכן במקרה זה הפלט יהיה‬void ‫פונקציה מסוג‬
‫דוגמה זו באה להמחיש את העבודה עם פונקציות וטיפוסים פרימיטיביים וכי טבלת הנתונים נמחקת בסוף כל מחזור‬
.‫חיים של הפונקציה‬

‫דוגמה נוספת‬
import java.util.Scanner;
public class ChangeAarray {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int[] arr = {1, 2, 5, 4};
printArray(arr);
change (a,b);
System.out.println(a + “, “ + b);

} //end of class

19
:‫ תעביר אותנו לפונקציה הבאה‬printArray ‫קריאה לפונקציה‬
public static int printArray(int a, int b){
if (arr == null)
System.out.println(arr);
else{
System.out.println(“{ “);
for (int element: arr)
System.out.println(element+” “);
System.out.println(“}”);
}
return;
}

// arr != null, aarr.length >= 2


public static int changeArray(int[] arr){
if (arr == null || arr.length <= 2)
throw new IllegalArgumentExeption;
arr[1] = 9;
int[] otherArray = {1, 2, 3};
arr = otherArray;
arr[1] = 90;
}

‫בעיית החיפוש‬
. key )‫ מערך של מסרים שלמים (יתכן והוא ריק) ומפתח (מספר שלם‬:‫ קלט‬.1
.‫ אם לא נמצא בו‬-1 ‫ מיקום המפתח במערך או‬:‫ פלט‬.2
8 5 2 ‫מפתח‬ {1, 2, 5, 5, 7} :‫דוגמה‬
-1 3 2 1 ‫פלט‬
.‫ לא משנה איזה‬,‫ יוחזר מיקום אחד‬,‫כאשר קימיים מס' מיקומים‬

20
‫חיפוש ליניארי‬
.)‫מעבר ובדיקה של כל הערכים (עד מציאת מיקום המפתח או עד סוף המערך אם לא קיים בו‬

import java.util.Scanner;
public class LinearSearch {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int[] arr = {1, 2, 5, 4};
int key = 4;
System.out.println(arr, key);

//arr != null
public static linearSearch(int[] arr, int key){
if (arr == null)
throw new NullPointerExeption();
int output = -1;
for (int i=0; i < arr.length & output == -1; i = i+1){
if (arr[i] == key)
output = i;
}
return output;
}

‫ לבדיקת הקוד‬main ‫פונקציית‬


import java.util.Scanner;
public class LinearSearch {
public static void main(String[] args){
Scanner myScanner = new Scanner(System.in);
int[] arr = {1, 2, 5, 4};
int key = 4;
if (linearSearch(arr, key) == 2)
System.out.println(“success”);
else
System.out.println(“error”);

‫חיפוש בינארי – בעיית החיפוש במערך ממויין‬


‫ מערך ממויין של מספרים שלמים (יתכן והוא ריק) ומפתח‬:‫קלט‬
.‫ אם לא נמצא בו‬-1 ‫ מיקום המפתח במערך או‬:‫פלט‬
60 - ‫ מפתח‬:‫דוגמה‬
‫בכל פעם חוצים את המערך לשניים – לוקחים את האינדקס הגבוה ביותר והאינדקס הנמוך ביותר ועושים להם‬
‫ וכן‬,5‫ לכן ניתן "לזרוק" את החלק השמאלי ולקדם את האינדקס הנמוך ל‬,60‫ קטן מ‬10 .4 ,‫ממוצע – במקרה הזה‬
.‫הלאה‬
‫ אם המפתח לא נמצא עד לפעולת הממוצע האחרונה (בה עושים ממוצע לאיבר עם‬.‫אם המפתח אינו נמצא במערך‬
.‫ המפתח אינו נמצא במערך‬,)‫עצמו למעשה‬

-1 0 3 10 10 17 52 60 63
0 1 2 3 4 5 6 7 8

21
‫הקוד‬
‫‪//arr != null, arr is sorted.‬‬
‫{)‪public static int binarySearch(int[], int key‬‬
‫;}‪int[] arr = {1, 2, 5, 4‬‬
‫;‪int key = 4‬‬
‫)‪if (arr == null‬‬
‫;)(‪throw new IllegalArgumentExeption‬‬
‫;‪int output = -1‬‬
‫;‪boolean found = false‬‬
‫;‪int low = 0‬‬
‫;‪int high = arr.length-1‬‬
‫{)‪while (low <= high & !found‬‬
‫;‪int middle = (low+high)/2‬‬
‫{)‪if (arr[middle]==key‬‬
‫;‪found = true‬‬
‫;‪output = middle‬‬
‫}‬
‫)‪else if (arr[middle]<key‬‬
‫;‪low = middle+1‬‬
‫‪else‬‬
‫;‪high = middle-1‬‬
‫}‬
‫;‪return output‬‬
‫‪} //end of class‬‬

‫חיפוש ביניארי לוקח ‪ log n‬פעולות‪ .‬במדעי המחשב בסיס הלוג הוא תמיד ‪!!2‬‬

‫בעיית המיון‬
‫קלט‪ :‬מערך של מספרים שלמים‬
‫פלט‪ :‬מערך עם אברי מערך הקלט‪ ,‬מסודרים בסדר לא יורד‪.‬‬

‫מיון בחירה – ‪SelectionSort‬‬


‫נעבור מקום‪ -‬מקום במערך‪ .‬בכל שלב נכניס את האיבר הקטן ביותר (מבין האיברים שלא בחרנו עדיין) למקום הבא‬
‫במערך‪ .‬נחליף את המיקום של האיבר במקום שבו אנו נמצאים עם האיבר הכי קטן‪.‬‬
‫בכל שלב‪ ,‬כל האיברים שמשמאל למקום שבו אנו עומדים ממויינים כבר‪ .‬טענה זו נקראת ‪ ,invariant‬אינבריאנטה‬
‫זמן ריצה פרופורציוני לאורך המערך ‪ - n2 -‬פונקציה ריבועית‬
‫דוגמה‪:‬‬

‫‪-6‬‬ ‫‪0‬‬ ‫‪10‬‬ ‫‪1‬‬ ‫‪-7‬‬ ‫‪2‬‬ ‫‪1‬‬ ‫‪2‬‬


‫‪0‬‬ ‫‪1‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬ ‫‪7‬‬

‫‪-7‬‬ ‫‪0‬‬ ‫‪10‬‬ ‫‪1‬‬ ‫‪-6‬‬ ‫‪2‬‬ ‫‪1‬‬ ‫‪2‬‬


‫‪0‬‬ ‫‪1‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬ ‫‪7‬‬

‫‪-7‬‬ ‫‪-6‬‬ ‫‪10‬‬ ‫‪1‬‬ ‫‪0‬‬ ‫‪2‬‬ ‫‪1‬‬ ‫‪2‬‬


‫‪0‬‬ ‫‪1‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬ ‫‪7‬‬

‫‪-7‬‬ ‫‪-6‬‬ ‫‪0‬‬ ‫‪1‬‬ ‫‪1‬‬ ‫‪2‬‬ ‫‪2‬‬ ‫‪10‬‬ ‫וכן הלאה‪ ,‬עד למצב שבו כל האיברים ממוינים‬
‫‪0‬‬ ‫‪1‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪4‬‬ ‫‪5‬‬ ‫‪6‬‬ ‫‪7‬‬

‫‪22‬‬
‫‪4.11.18‬‬

‫הרצאה ‪7‬‬
‫פונקציית ‪ - SelectionSort‬המשך‬

‫{)‪public static void selectionSort(int[] arr‬‬


‫)‪if (arr == null‬‬
‫;)(‪throw new IllegalArgumentExeption‬‬
‫{)‪for (int i=0; i<arr.length-1; i=i+1‬‬
‫;)‪int minIndex = minIndex(arr, i‬‬
‫;)‪swap (arr, i, minIndex‬‬
‫}‬
‫}‬

‫פונקציית ‪main‬‬
‫;‪import java.util.Scanner‬‬
‫{ ‪public class Sort‬‬
‫{)‪public static void main(String[] args‬‬
‫;)‪Scanner myScanner = new Scanner(System.in‬‬
‫;}‪int[] arr = {1, 0, 7, 3, -1 ,6‬‬
‫;)‪SelectionSort (arr‬‬
‫}‪printArray (arr); //{-1 0 1 3 6 7‬‬
‫}‬
‫}‬

‫מיון הכנסה ‪InsertionSort‬‬


‫בכל שלב האיבר הבא במערך נכס למקומו המתאים משמאל‪.‬‬
‫הפונקציה מניחה שכל האיברים משמאל כבר ממויינים בינים לבין עצמם‪ .‬עם כל איבר‪ ,‬בודקים את כל האיברים‬
‫משמאלו עד שמגיעים לאיבר שלא גדולים ממש ממנו (ז"א קטנים או שווים לו)‪.‬‬
‫טענה נשמרת (אינווריאנטה) – ב כל שלב באלוריתם‪ ,‬כאשר האיבר הבא מוכנס למקומו‪ ,‬האיברים שלפניו (משמאמלו)‬
‫כבר ממויינים‪.‬‬
‫זמן ריצה פרופורציוני לאורך המערך ‪ - n2 -‬פונקציה ריבועית‬

‫‪23‬‬
‫{)‪public static void insertionSort(int[] arr‬‬
‫)‪if (arr == null‬‬
‫;)(‪throw new IllegalArgumentExeption‬‬
‫{)‪for (int i=1; i<arr.length; i=i+1‬‬
‫;)‪insert (arr, i‬‬
‫}‬
‫}‬

‫{)‪public static void insert(int[] arr, int i‬‬


‫)‪if (arr == null‬‬
‫;)(‪throw new IllegalArgumentExeption‬‬
‫)‪if (i<0 | i >= arr.length‬‬
‫;)(‪throw new IllegalArgumentExeption‬‬
‫;]‪int value = arr [i‬‬
‫‪while (i>0 && arr[i-1]>value):‬‬
‫;]‪arr[i] = arr[i-1‬‬
‫;‪i = i-1‬‬
‫}‬
‫;‪arr[i] = value‬‬
‫}‬

‫חתימה של פונקציה ‪Signature‬‬


‫מחלקה מורכבת מאוסף של פונקציות‪.‬‬ ‫‪-‬‬
‫ניתן להעמיס על שם הפונקציה (‪ )overloading‬מספר פעולות‪.‬‬ ‫‪-‬‬
‫בתוך מחלקה‪ ,‬לכל פונקציה חייבת להיות חתימה ייחודית משלה‪ ,‬המורכבת מ‪:‬‬ ‫‪-‬‬
‫‪ o‬שם הפונקציה‬
‫‪ o‬רשימת הפרמטרים (רשימות הפרמטרים נבדלות זו מזו במספר הפרמטרים ‪ ,‬טיפוס הפרמטרים‬
‫וסדר הפרמטרים)‬
‫ערך ההחזרה לא מהווה חלק מהחתימה‪.‬‬ ‫‪-‬‬
‫במידה ולא נמצאת חתימה מתאימה‪ ,‬תתכן המרת טיפוסים (המרה מרחיבה – אין איבוד מידע)‪.‬‬ ‫‪-‬‬
‫במקרה שיש קריאה לפונקציה שאין לה חתימה מתאימה בדיוק ויש מס' פונקציות שיכולות להתאים‪ ,‬תופיע‬ ‫‪-‬‬
‫שגיאת קומפילציה‪.‬‬
‫דוגמה‪:‬‬

‫‪24‬‬
import java.util.Scanner;
public class Signatures {
public static int div2(int n){
Scanner myScanner = new Scanner(System.in);
return n/2;
{
public static double div2(double d){
Scanner myScanner = new Scanner(System.in);
return d/2;
}
public static void main(String[] args]{
int x = 5;
double y = 5.0;
System.out.println(div2(x)); //2
System.out.println(div2(y)); //2.5
}
}

‫מערכים דו מימדיים‬
.‫מערכים שהתאים שלהם מכילים מערכים אחרים – מטריצות‬
import java.util.Scanner;
public class Signatures {
public static void main(String[] args]{
int size = 3;
int[] arr = new int[size];
int[][] 2darr = new int [size][];
2darr = new int[size][4];
2darr[1][0] = 7;
2darr[2][3] = 2;
2darr = {{1,2}, {}, {3}, null}
System.out.println(2darr[2].length); //1
}

‫בעיית הטיול הגדול‬


)‫ – עיר המוצא‬0( 0,1,2…n-1 ‫ נסמן‬,‫ ערים‬n ‫ רשימה של‬:‫קלט‬
‫קווי תעופה בין הערים‬
.)‫ המבקר בכל הערים פעם אחת בדיוק (אם קיים‬,‫ מסתיים בה‬,0 ‫ מסלול טיול המתחיל בעיר‬:‫פלט‬
‫ את הפתרון נייצג כמערך חד מימדי‬.flights ‫נייצג את הקלט לבעיה כמערך דו מימדי שנקרא‬
int[] tour = {0,2,1,3}
boolean[][] flights = {{f,f,t,t}, {f,f,t,t}, {t,t,f,t}, {t,t,t,f}}

0 1 2 3
0 F F T T
1 F F T T
2 T T F T
3 T T T F

25
import java.util.Scanner;
public class BigTrip {
public static boolean hasLegalSteps(boolean[][] flights, int[] tour]{
boolean ans = true;
for {int i=0; i<tour.length-1 & ans; i=i+1){
if flighs[[tour[i]][tour[i+1]] == false)
ans = false;
}
return ans & flights[tour[tour.length-1]][0])
}
}

26
8.11.18

8 ‫הרצאה‬
‫מחרוזת וטיפוסים עוטפים‬
String - ‫מחרוזת‬
String - ‫טיפוס המייצר אוסף של תווים‬
‫ ניתן ליצור סטרינג חדש באותו שם ולהגדיר ערך‬.‫ – לא ניתן לשנות אותן אחרי שיצרנו אותן‬Immutable ‫מחרוזות הן‬
.‫אחר‬
String str1 = new String(“Hello”);
String srtr2 = “world!”
String str3 = new String();
String str4 = new String(“hello”);
String str5 = “”;
String str6 = null
String str = ”world!!”
System.out.println(str1 == str4); //false
String str7 = str1.substring(2);
String str8 = Str1 + “ “ + str2;

27
:‫ כמה שיטות‬String ‫לטיפוס‬
length .1
System.out.println(str1.length()); //5
System.out.println(str3.length()); //0
System.out.println(str6.length()); //RunTime error
‫ יש סוגריים כיוון שלטיפוס יש שיטה כלשהי שבה הוא משתמש‬str1.length()‫ ב‬,‫בניגוד למערכים‬
equals .2
‫שיטה בוליאנית שבודקת אם סטרינג שווה לסטרינג אחר‬
System.out.println(st1.equals(str4)); //true
‫ מאותחל‬str1 ‫ – ללא תתקבל פה שגיאת זמן ריצה כי‬System.out.println(st1.equals(str6)); //false
str6‫לערך מסויים וניתן לבדוק אותו מול‬
str6 ‫ – תתקבל שגיאת זמן ריצה כי‬System.out.println(str6.equals(str1)); //RunTime error
.‫ ואין איך לבדוק אותו‬null‫מאותחל ב‬
charAt .3
i ‫שיטה שבודקת את ערך התו שבמקום‬
System.out.println(str1.charAt(0)); //’H’
System.out.println(str.charAt(0)); //RunTime error
indexOf .4
‫ יוחזר האינדקס הראשון‬,‫ אם התו מופיע מספר פעמים‬.‫שיטה שבודקת את האינדקס של התו שהכנסנו‬
System.out.println(str1.indexOf(‘l’)); //’2’
System.out.println(str1.indexOf(‘A’)); //’-1’
substring .5
‫שיטה שמדפיסה (או יוצרת – תלוי אם הגדרנו מחרוזת חדשה) תת מחרוזת לפי האינדקסים שהגדרנו לה‬
)‫ (לא כולל‬m‫ (כולל אותו) עד האינדסק ה‬i‫) – המחרוזת תדפיס מהמספר ה‬i, m( ‫בסוגרים‬
System.out.println(str1.substring(0,3)); // “Hel”
‫ בגוגל‬String API Java ‫ לחפש‬,‫*כדי לקבל עוד מידע ולחפש עוד שיטות‬

:‫ ותו כלשהו ומחזירה את מספר המופעים של התו במחרוזת‬null‫נכתוב פונקציה המקבלת מחרוזת שונה מ‬

public static void main(String[] args]{


String str1 = new String(“Hello”);
char ch = ‘l’;
int ans = countCharIntSTring(str1, ch);

//assumes str != null


public static int countCharIntString(String[] str, char ch]{
int counter = 0;
for (int i=0; i<str.length(); i = i+1){
if (str.chatAt(i) == ch){
counter = counter+1;
}
}
return counter;
}

28
16 ,10 ,8 ‫מספרים בבסיס‬
.10 ‫ נרצה לחשב את ערכה המספרי בבסיס‬.16 ,10 ,8 ‫נתונה לנו מחרוזת המייצגת מספר שלם ואי שלילי בבסיס‬
0x ‫ יש קידומת‬16 ‫ למספרים בבסיס‬,0 ‫ יש קידומת‬8 ‫למספרים בבסיס‬
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

16 ‫בסיס‬ 8 ‫בסיס‬ 10 ‫בסיס‬


0x 00 0 0
0x6 06 6 6
0xC 014 12 12

//assumes S != null and S.length > 0 and S represents a number


public static int toInt (String S){
int base;
int first; //‫האינדקס ממנו מתחיל המספר‬
int value = 0, power = 1;
if (S.length == 1 | s.charAt != ‘0’){
base = 10;
first = 1;
}
else if (s.charAt(1) != ‘x’){
base = 8;
first = 1;
else{
base = 16;
first = 2;

for(int i= s.length()-1; i >= first; i = i-1){


value = value + toInt(s.charAt(i))*power;
power = power*base;
}
return value;

public static int toInt(char ch){


//1 ‫ אם התו אינו חוקי תחזיר‬.10 ‫ ומחזירה את ערכה המספרי בבסיס‬16 ‫הפונקציה מקבלת תו המייצג ספרה בבסיס‬
int value = -1;
if (‘0’ <= ch & ch <= ’9’){
value = ch – ‘0’;
}
else if (‘A’ <= ch & ch <= ‘F’){
value = ch – ‘A’ +10;
}
return value;
}

:‫מימוש חלופי‬
public static int toInt(char ch){
//1 ‫ אם התו אינו חוקי תחזיר‬.10 ‫ ומחזירה את ערכה המספרי בבסיס‬16 ‫הפונקציה מקבלת תו המייצג ספרה בבסיס‬
String digits = “0123456789ABCDEF”
return.indexOf(ch).
}

29
‫טיפוסים עוטפים‬
‫ב‪ java‬לכל טיפוס פרימיטיבי יש מחלקה עוטפת מתאימה‪ .‬המחלקה תכתב עם אות גדולה וצריך להגדיר אותה‪.‬‬
‫למחלקות אלה שיטות כמו של מחרוזות‪.‬‬
‫(‪(boolean – Boolean ,int – Integer‬‬
‫;)‪Integer n = new Integer(42‬‬
‫;)‪Boolean B = New Boolean(false‬‬
‫;‪Integer n2 = 42‬‬
‫‪System.out.println(n+3); //45‬‬

‫‪30‬‬
‫‪11.11.18‬‬

‫הרצאה ‪9‬‬
‫בעיית הסיפוק הבוליאני ‪SAT -‬‬
‫בעיה‪ :‬בהינתן נוסחה בתחשיב הפסוקים‪ ,‬האם יש לה השמה מספקת?‬
‫תחשיב הפסוקים הוא בעצם אוסף של בעיות בוליאניות‪ .‬נוסחה בתחשיב הפסוקים בנויה מדיסיונקציות (ביטויים‬
‫לוגיים שמפריד ביניהם האופרטור "או" |) שמחוברות ביניהן עם האופרטור &‪.‬‬
‫באופן כללי‪ ,‬כל ביטוי המיוצג ב‪ JAVA‬ע"י האופרטורים & | ! הוא ביטוי בוליאני ולכן הוא נחשב כנוסחה בתחשיב‬
‫הפסוקים‪.‬‬
‫את נוסחת הפסוקים שיצרנו נכניס כקלט לפותרן ה‪ SAT‬וכפלט נקבל השמה מספקת (אם יש כזו)‪.‬‬
‫הקלט לפותרן ה‪ SAT‬חייב להיות נוסחה בצורת ‪.CNF‬‬
‫כיצד נראית נוסחת ‪?CNF‬‬
‫נוסחת ‪ CNF‬בנויה כמערך דו מימדי של ‪.int‬‬
‫כל שורה מייצגת דיסיונקציה והשורות ביחד מייצגות קוניונקציה‬
‫("מחוברות" ביניהן עם האופרטור &)‪.‬‬
‫לכל ביטוי לוגי יש ייצוג מספרי‪ .‬הייצוג המספרי מתחיל מ‪1‬‬
‫(ולא מ)‪ .0‬את המספר נכניס למערך בהתאם לבעיה שנרצה‬
‫לפתור‪ .‬אם נרצה לייצג שלילה של הביטוי‪ ,‬נכניס אותו כמספר‬
‫שלילי‪.‬‬

‫פותרן ה‪ SAT‬יחזיר לנו בחזרה השמה (‪ )assignment‬המיוצגת כמערך חד מימדי בוליאני‪ .‬הערך במקום ה‪ 0-‬לא‬
‫רלוונטי כיוון שהביטויים הלוגיים שלנו ממוספרים מ‪ 1‬ומעלה‪.‬‬
‫אם אין השמה מספקת‪ ,‬יוחזר מערך ‪.null‬‬
‫מהי השמה מספקת?‬
‫השמה מספקת ‪ CNF‬אם היא מספקת את כל הפסוקיות שלו‪.‬‬ ‫‪.1‬‬
‫השמה מספקת פסוקית אם היא מספקת לפחות ליטרל אחד‪.‬‬ ‫‪.2‬‬
‫השמה מספקת ליטרל חיובי ‪ X‬אם היא מציבה בו ערך "אמת"‪.‬‬ ‫‪.3‬‬
‫השמה מספקת ליטרל שלילי ‪ ¬X‬אם היא מציבה ב ‪ X‬ערך "שקר"‪.‬‬ ‫‪.4‬‬

‫כך תראה השמה מספקת לדוגמה למעלה‪:‬‬

‫‪31‬‬
15.11.18

10 ‫הרצאה‬
)1 ‫מבוא לחשיבה רקורסיבית (רקורסיה‬
n ‫פונקציית העצרת של‬
‫פתרון איטראטיבי‬
n>=0 ,n ‫פונקציית העצרת של‬
f(n) = 1*2*3*…..*n
f(0) = 1
public static int factorial (int n){
int output = 1;
for (int i=2; i<=n; i = i+1){
output = output*i;
}
return output;
}

‫פתרון רקורסיבי‬
n! = 1*2*3*…..*(n-1)*n

.‫ עצמה עם קלט "קטן" יותר‬f ‫ מוגדרת ע"י‬f(n) ‫הפונקציה‬

//n>=0 ,n! ‫הפונקציה מחזירה את הערך‬


public static int factorial (int n){
int output;
if (n==0){
output = 1;
}
else{
output = n*factorial(n-1);
}
return output;
}

32
‫רקורסיה היא פונקציה שמחשבת ערך ע"י קריאה לעצמה‪.‬‬

‫מאמץ מחשבתי‬ ‫מקום‬ ‫זמן‬

‫קטן 😊‬ ‫קבוע וקטן (לא תלוי ב‪)n‬‬ ‫סדר גודל של ‪n‬‬ ‫פתרון איטראטיבי‬

‫גדול ☹‬ ‫סדר גודל של ‪n‬‬ ‫סדר גודל של ‪n‬‬ ‫פתרון רקורסיבי‬

‫זיווג מושלם – ‪Perfect Matching‬‬


‫פתרון רקורסיבי‬
‫למסיבה מגיעים ‪ n‬חלזונות‪.‬‬
‫בכמה דרכים שונות ניתן לחלק את החלזונות לזוגות?‬
‫דוגמה‪n=4 :‬‬
‫ישנן שלוש דרכים‪:‬‬
‫{(‪})1,2( ,)3,4‬‬
‫{(‪})1,3( ,)2,4‬‬
‫{(‪})4,1( ,)3,2‬‬

‫‪33‬‬
‫הפונקציה מחזירה את מספר הדרכים לחלק ‪ n‬חלזונות לזוגות‪ n ,‬זוגי‪//n>=0 ,‬‬
‫{)‪public static int pm (int n‬‬
‫;‪int output‬‬
‫{)‪if (n==0‬‬
‫;‪output = 1‬‬
‫}‬
‫{‪else‬‬
‫;)‪output = (n-1)*pm(n-2‬‬
‫}‬
‫;‪return output‬‬
‫}‬

‫פתרון איטראטיבי‬
‫הפונקציה מחזירה את מספר הדרכים לחלק ‪ n‬חלזונות לזוגות‪ n ,‬זוגי‪//n>=0 ,‬‬
‫{)‪public static int pm (int n‬‬
‫;‪int output = 1‬‬
‫{)‪for (int i=3; i<n; i = i+2‬‬
‫;‪output = output*i‬‬
‫}‬
‫;‪return output‬‬
‫}‬

‫מאמץ מחשבתי‬ ‫מקום‬ ‫זמן‬

‫גדול ☹‬ ‫קבוע וקטן (לא תלוי ב‪)n‬‬ ‫סדר גודל של ‪n‬‬ ‫פתרון איטראטיבי‬

‫קטן 😊‬ ‫סדר גודל של ‪n‬‬ ‫סדר גודל של ‪n‬‬ ‫פתרון רקורסיבי‬

‫סדרת פיבונאצ'י‬
‫סדרת פיבונאצ'י מוגדרת כך שכל איבר שווה לסכום שני האיברים הקודמים לו‪1, 1, 2, 3, 5, 8, 13, 21, 34… :‬‬

‫‪34‬‬
‫פתרון רקורסיבי‬
‫‪//assumes n>=0‬‬
‫{)‪public static int fib (int n‬‬
‫;‪int output‬‬
‫{)‪if (n==0 | n==1‬‬
‫;‪output = 1‬‬
‫}‬
‫{‪else‬‬
‫;)‪output = fib(n-1)+fib(n-2‬‬
‫}‬
‫;‪return output‬‬
‫}‬

‫פתרון איטראטיבי‬
‫‪//assumes n>=0‬‬
‫{)‪public static int fib (int n‬‬
‫;‪int output‬‬
‫{)‪if (n==0 | n==1‬‬
‫;‪output = 1‬‬
‫}‬
‫{‪else‬‬
‫;‪int f0 = 1, f1 =1‬‬
‫{)‪for (int i=2; i<=n; i = i+1‬‬
‫;‪output = f0 + f1‬‬
‫;‪f0 = f1‬‬
‫;‪f1 = outpout‬‬
‫}‬
‫}‬
‫;‪return output‬‬
‫}‬

‫מאמץ מחשבתי‬ ‫מקום‬ ‫זמן‬


‫גדול ☹‬ ‫קבוע וקטן (לא תלוי ב‪)n‬‬ ‫סדר גודל של ‪n‬‬ ‫פתרון איטראטיבי‬
‫קטן 😊‬ ‫סדר גודל של ‪n‬‬ ‫‪2n‬‬ ‫פתרון רקורסיבי‬

‫ספיידרמן‬
‫ספיידרמן מטפס על בניין בעל ‪ n‬קומות‪ .‬בכל צעד יכול לקפוץ קומה ‪ 1‬או ‪.2‬‬
‫בכמה דרכים שונות יכול ספיידרמן להגיע לקומה ה‪?n‬‬
‫דוגמה‪:‬‬
‫אם ‪n=4‬‬
‫‪2+2‬‬ ‫‪.1‬‬
‫‪1+1 +2‬‬ ‫‪.2‬‬
‫‪1+2+1‬‬ ‫‪.3‬‬
‫‪2+1+1‬‬ ‫‪.4‬‬
‫‪1 + 1+1+1‬‬ ‫‪.5‬‬

‫פתרון רקורסיבי‬
‫נשים לב כי הנוסחה הרקורסיבית לספיידרמן‪ ,‬היא בדיוק הנוסחה המדוייקת לפיבונאצ'י‬

‫‪35‬‬
18.11.18

11 ‫הרצאה‬
2 ‫רקורסיה‬
‫ בשיטה רקורסיבית‬GCD ‫פתרון בעיית‬
public static int gcd (int x, int y){
int output;
if (x%y == 0){
output = y;
}
else{
output = gcd(y, x%y);
}
return output;
}

‫היפוך מחרוזת‬
“abcd” :‫קלט‬
“dcba” :‫פלט‬

//assumes s != null
public static String reverse (String s){
String output;
if (s.length() == 0){
output = s;
}
else{
output = reverse(s.substring(1)) + s.charAt(0);
}
return output;
}

‫מגדלי האנוי‬

36
‫בכל שלב מותר להעביר רק דסקית אחת‪ .‬אסור להניח דסקית על דסקית קטנה ממנה‪.‬‬
‫נעביר ברקורסיה ‪ n-1‬דסקיות מ‪ a -‬ל‪ .b-‬נזיז דסקית מ‪ a-‬ל‪.b-‬‬
‫נזיז דסקית מ‪ a-‬ל‪.c-‬‬
‫נעביר ברקורסיה ‪ n-1‬דסקיות מ‪ b-‬ל‪.c-‬‬

‫{)‪public static void hanoi (int n, char source, char dest, char extre‬‬
‫{)‪if (n>0‬‬
‫;)‪hanoi (n-1, source, extra, dest‬‬
‫;)‪System.out.println(“move sisk from “ + source + “ to “ + dest‬‬
‫;)‪hanoi (n-1, extra, dest, source‬‬
‫{‬
‫}‬

‫חישוב מספר הצרופים‬


‫בהינתן ‪ n>=0‬ו‪ K>=0‬שלמים‪ ,‬אנו רוצים לבדוק כמה אפשרויות שונות קיימות לבחירת ‪ k‬פריטים שונים מתוך ‪.(nk) n‬‬
‫‪n‬‬ ‫‪n-1‬‬ ‫‪n-1‬‬
‫) (‪( )= ( )+‬‬
‫‪k‬‬ ‫‪k‬‬ ‫‪k-1‬‬
‫בלי ‪n‬‬ ‫עם ‪n‬‬

‫‪37‬‬
//n >= 0, k >= 0
public static int choice (int n, int k({
int output;
if (k>n){
output = 0;
}
else if (k == 0 | k == n) {
output = 1;
}
else{
output = choice(n-1, k) + choice(n-1, k-1);
}
return output;
}

‫הדפסת מספר הצירופים‬


.n ‫ פריטים שונים מתוך‬k ‫אנחנו רוצים להדפיס את כל האפשרויות השונות לבחירת‬

//acc ‫ ולכל אחת מהן שרשר את‬n ‫ איברים מתוך‬k ‫הדפס את כל האפשרויות לבחור‬
public static void printAllChoices (int n, int k, String acc){
int output;
if (k <= n){
if(k==0){
System.out.println(acc);
}
else{
printAllChoices (n-1, k, acc); //n ‫בלי‬
printAllChoices (n-1, k-1, acc + “ “ + n); //n ‫עם‬

}
}

38
22.11.18

12 ‫הרצאה‬
‫מבוא לתכנות מונחה עצמים‬
n ‫ – תכנית המדפיסה את כל הראשוניים עד‬4 ‫תזכורת משיעור‬
public static int primesArray(int n){
Scanner myScanner = new Scanner(System.in);
int[] primes = new int[n];
int numberOfPrimes = 0; //‫מונה את הראשוניים שנמצאו עד כה ומספר לנו מהו האינדקס של התא הפנוי הבא‬
for (int number =2 ; number <= n; number = number +1){
boolean isPrime =true;
for (int primesIndex = 0; primesIndex < numberOfPimes && primes[primesIndex]*primes[primesIndex] <= number; primesIndex = primesIndex+1{
if(number%primes[primesIndex] == 0)
isPrime = false;
}
if (isPrime){
primes[numberOfPrimes] = number;
numberOfPrimes = numberOfPrimes+1;
}

System.out.println(number);
}
}
}

Integer i1 = new Integer(15);


Object object1 = i1;
int i2 = (Integer) o1;
int i3 = ((Integer)o1).intValue()*3;
Object[] arr = new Object[2];
arr[0] = i1;
arr[1] = new Integer(7);

‫ הוא לא טיפוס‬Object -‫ על אף ש‬.‫ שמייצגת כל משתנה שאינו פרימיטיבי‬JAVA‫ היא מחלקה מופשטת ב‬Object
)Object o1 = i1 ‫ הוא יכול להצביע למקום בזכרון ע"י השמה של משתנה לא פרימיטבי אחר (כמו‬,‫פרימיטיבי‬
.Integer ‫ הוא גם‬Object ‫ לא כל‬.Object ‫ הוא גם‬,‫ ובאופן כללי כל משתנה שאינו פרימיטיבי‬,Integer ‫כל‬

39
‫ניתן ליצור מערך מסוג ‪ .Object‬כאמור‪ Object ,‬יכול לייצג טיפוסים שונים‪ ,‬לכן כך ניתן לייצר מערך בעל טיפוסים‬
‫שונים‪.‬‬

‫מחלקה חדשה‪DynamicArray -‬‬


‫נרצה להגדיר טיפוס חדש‪/‬מחלקה חדשה שנקראת ‪DynamicArray‬‬
‫;)(‪DynamicArray arr = new DynamicArray‬‬
‫‪S.o.p(arr.size()); //0‬‬
‫;))‪arr.add(new Integer(7‬‬
‫‪S.o.p(arr.size()); //1‬‬
‫;)‪Integer i = (Integer)arr.get(0‬‬
‫‪S.o.p(i.intValue()*2); //4‬‬

‫עקרונות לעיצוב המחלקה החדשה‬


‫‪ .1‬קוד כחול‪ :‬מה שהמשתמש צריך לדעת‪ .‬שיטות העצם‪:‬‬
‫‪ – size() .1‬מחזירה את מס' הנתונים במערך‬
‫‪ – get(int) .2‬מחזירה את העצם במקום ה‪ i-‬במערך‬
‫‪ – add(Object) .3‬מוסיפה עצם למקום הפנוי הבא בסוף המערך‬
‫(תמיד יש מקום במערך)‬
‫‪ .2‬קוד אדום‪ :‬אופן המימוש – לא עניינו של המשתמש‪ .‬נבדיל בין‪:‬‬
‫‪size .1‬‬
‫‪ -‬הגודל הלוגי של המערך‪ ,‬מס' האיברים בו שכבר "מלאים"‬
‫‪ -‬תמיד מכיל אינדקס של התא הפנוי הבא – תמיד קיים תא פנוי כזה‬
‫‪ – Capacity .2‬מספר התאים בזכרון שמוקצים בפועל במערך‪ ,‬גודל פיזי‪.‬‬

‫‪40‬‬
public class DynamicArray {
‫שדות‬ private static int DEFAULT_CAPACITY = //‫ביצוע השמה‬
fields private Object[] data;
private int size;
private int incrementSize;

‫בנאים‬ public DynamicArray(){…}


public DynamicArray(int initialCapacity){…}

‫שיטות‬ public int size(){return size}


public Object get(int index){…}
public void add(Object element){…}

}
:‫ שנזכרנו בו בתחילת השיעור‬4 ‫נשכתב את הקוד משיעור‬

public class Test{


public static void main(){
Scanner myScanner = new Scanner(System.in);
int n = myScanner.nextInt();
DynamicArray primes = primesDArray(n);
for (int i=0; i<primes.size(); i=i+1){
System.out.println((Integer)primes.get(i)));
}
}

public static int DynamicArray primesDArray(int n){


Scanner myScanner = new Scanner(System.in);
int n = myScanner.nextInt(); //50
int[] primes = new int[n];
int numberOfPrimes = 0;
for (int number =2 ; number <= n; number = number +1){
boolean isPrime =true;
for (int i = 0; i < numberOfPimes && primes[i]*primes[i] <= number; i = i+1{
if(number%primes[i] == 0)
isPrime = false;
}
if (isPrime){
primes[numberOfPrimes] = number;
numberOfPrimes = numberOfPrimes+1;
}

}
return primes;
}

41
‫מימוש הבנאים‬
.X ‫ יש את השם‬X ‫לכל הבנאים של מחלקה‬

public DynamicArray (int initialCapacity){


//initialCpacity ‫בנאי זה ("בנאי פרמטרי") מאתחל מערך ריק בגודל ראשוני של‬
if (initialCapacity <= 0){
throw new IllegalArgumentException(“..”);
}
data = new Object[initialCpacity];
size = 0;
incrementSize = initialCapacity;
}

public DynamicArray{
this(DEFAULT_CAPACITY);
{

:‫נממש את שלושת השיטות‬

public Object get (int index){


if (!rangeCheck(index){
throw new IllegalArgumentException(“..”);
}
}

private boolean rangeCheck(int index){


return !(index<0 | index >=0);
}

public void add(Object element){


data[size] = element;
size = size +1;
ensureCapacity();

private void ensureCapacity(){


if(size() >= data.length){
object[] newData = new Object[data.length + incrementSize];
for (int i=0; i<size(); i= i+1){
newData[i] = data[i];
}
data = newData;
}

42
‫‪25.11.2018‬‬

‫הרצאה ‪13‬‬
‫מבוא לתכנות מובנה עצמים ‪ – 2‬רשימה משורשרת‬
‫במחלקה ‪ Object‬שתי שיטות חשובות‪:‬‬
‫‪boolean equals(Object) .1‬‬
‫מבצעת השוואה לוגית בין העצם המפעיל את השיטה לעצם המתקבל כפרמטר‪.‬‬
‫השיטה מחזירה ‪ true‬כאשר הערכים של שני האובייקטיבים שווים‪.‬‬
‫‪String toString() .2‬‬
‫השיטה מחזירה מחרוזת המתארת את האובייקט‪.‬‬
‫היתרון של הרשימה המשורשרת הוא שניתן לבצע שינויים במערך – לא מוגבלים מבחינת מקום ומבחינת הקצאת‬
‫תאים‪ ,‬ניתן להכניס איברים באמצע המערך‪.‬‬
‫רשימה מקושרת בנויה מאוסף של חוליות‪ .‬הרשימה שומרת כל ערך‪/‬נתון בחוליה‪ .‬החוליות מקושרות אחת לשניה‬
‫באמצעות הפניות‪.‬‬
‫אוסף החוליות יוצר אוביי קט שנקרא רשימה‪ .‬הרשימה מתאפיינת ע"י החוליה הראשונה‪ .‬כלומר‪ ,‬החוליה הראשונה‬
‫מגדירה את אופי הרשימה‪ .‬אנחנו לא רוצים לאפשר לנתון הראשון בחוליה הראשונה להיות ‪null‬‬

‫מימוש המחלקה ‪( remove‬כפי שנראה בעמוד הבא)‪:‬‬


‫{)‪public static void main(String[] args‬‬
‫‪:‬‬
‫;)‪myList.remove(3‬‬
‫‪:‬‬
‫}‬

‫‪43‬‬
public class LinkedList{ //‫הרשימה‬
private Link first;
public LinkedList(){
first = null; //‫הבנאי הריק‬
}
public boolean isEmpty(){
return first == null;
}
public void addFirst(Object element){
if (element == null)
throw new IllegalArgumentException();
if (isEmpty())
first = new Link(element);
else
first = new Link(element, first);

}
public int size(){
int counter = 0;
for (Link curr = first; curr != null; curr = curr.getNext()){
counter = counter + 1;
}
return counter;
}
public boolean remove(Object element){
//‫השירה מסירה את החוליה הראשונה ברשימה המכילה את אלמנט‬
//‫יוחזר אמת אם"ם התבצעה הסרה‬
if (element == null)
throw new IllegalArgumentException();
Link prev = first, curr = first;
boolean removed = false;
while (curr != null & !removed){
if (currr.getData().equals(element)){
if (curr == first)
first = first.getNext();
else
prev.setNext(curr.getNext());
removed = true;
}
else {
prev = curr;
curr = curr.getNext();
}
return removed;
}
public String toString(){
String output = “”;
Link curr = first;
while (curr != null){
output = output + curr.toString() +” “;
curr = curr.getNext;
}
return output;
}

public class Link{ //‫החוליות‬


private Object data;
private Link next;
public Link(Object data, Link next){
this.data = data;
this.next = next;
}
public Link(Object data){
this(data, null);
}
public Link getNext(){
return next;
}
public void setNext(Link next){
this.next = next;
}
public Object getData(){
return data;
}
public String toString(){
44 return “” + data.String;
}
‫‪29.11.2018‬‬

‫הרצאה ‪14‬‬
‫מבוא לתכנות מונחה עצמים ‪ – 3‬המחלקה ‪ ,Object‬הורשה‪ ,‬דריסה‬
‫המחלקה ‪ Object‬מייצגת את הרעיון המופשט של "עצם"‪ ,‬דבר מאוד מאוד כללי‪.‬‬
‫השיטה ‪ equals‬במחלקה ‪ Object‬פועלת כמו האופרטור ==‪ .‬השוואה לוגית של ‪Object‬ים היא בבחינת המקום בזכרון‬
‫ולא יותר זה‪.‬‬
‫;)(‪Object o1 = new Object‬‬
‫;)(‪Object o2 = new Object‬‬
‫‪o1 == o2; //false‬‬
‫;‪Object o3 = o2‬‬
‫‪o2 == o3; //true‬‬
‫‪S.op(o1.toString()); //java.lang.Object@2a139a55‬‬
‫;)‪ // S.o.p(o1‬עובר קומפילציה‪ ,‬ג'אווה תפעיל את השיטה ‪ toString‬בעצמה‬
‫‪o1.equals(o2); //false‬‬
‫‪o2.equals(o3); // true‬‬

‫הרחבה‪/‬הורשה‬
‫כל מחלקה מרחיבה את המחלקה ‪ .Object‬כל רעיון שנתאר הוא קודם כל עצם‪ .‬ז"א‪ ,‬כל רעיון הוא גם ‪ Object‬בבסיסו‪,‬‬
‫ובנוסף לכל רעיון‪/‬מחלקה יש מאפיינים נוספים שייחדו אותה (וזה מה שמבדיל ‪ Double‬מ‪ Integer-‬וכו')‪.‬‬
‫כל מחלקה יורשת את השיטות של המחלקה ‪.Object‬‬
‫מה שזה בעצם אומר‪ ,‬זה שכשיוצרים מחלקה חדשה ‪ ,DynamicArray‬יש לה "גרעין" שהוא בעצם ‪ Object‬ולכן עם‬
‫היווצרה היא מקבלת גם את השיטות ‪ toString ,equals‬שיש ל‪ Object‬וניתן להפעיל אותן עליה גם ללא להגדיר אותן‬
‫כשיטות של המחלקה החדשה‪.‬‬

‫‪45‬‬
‫כלל הבצל‬
‫כשאנחנו מפעילים שיטה על מופע מסויים‪ ,‬החיפוש אחריה מתחיל מבחוץ פנימה‪ .‬כלומר‪ ,‬ממחלקת הבן (המחלקה‬
‫המרחיבה) למחלקת האב‪ .‬השיטה הראשונה שתמצא היא זו שתופעל‪.‬‬

‫דריסה‬
‫ניתן ליצור שיטה חדשה בעלת אותו השם ובעלת אותה חתימה (כפי שהיא מופיעה במחלקת האב) במחלקה‬
‫המרחיבה‪ .‬כשנפעיל את השיטה בזמן ריצה‪ ,‬השיטה שנמצאת במחלקה המרחיבה יותר היא זו שתופעל‪ .‬כך בעצם‬
‫דורסים את השיטה שנמצאת במחלקת האב ומונעים את הפעלתה‪.‬‬

‫‪Reference Type VS. Instance Type‬‬


‫;)”‪String s1 = new String(“Hello‬‬
‫;)”‪Object o1 = new String(“World‬‬
‫;)‪Object o2 = new Integer (2‬‬
‫;)‪char c1 = s1.charAt(0‬‬
‫‪char c2 = o1.chatAt(0); //compilation error‬‬
‫‪char c3 = ((String)o1).CharAt(0); //W‬‬
‫‪char c4 = ((string)o2).charAt(0); //runtime error‬‬

‫‪46‬‬
‫ אם הגדרנו‬,‫ כלומר‬.‫ מתייחסת לאיך שהוגדר העצם‬reference ,‫ ההפניה‬.‫ נקבע רק בזמן ריצה‬Instance type‫ה‬
.‫ כדי שהקומפיילר יעביר את זה‬casting ‫ נצטרך לבצע‬,String ‫ ונרצה לממש עליו שיטה של‬Object‫סטרינג חדש כ‬
.‫מחלקת האב יכולה להצביע על מחלקת הבן אך לא להפך‬

instanceof ‫האופרטור‬
<var name> instanceof <type>
.type ‫ מצביע בפועל על מופע של המחלקה‬varname ‫ אם בזמן ריצה המשתנה‬true ‫זהו אופרטור בוליאני שיחזיר‬
.false ‫אחרת יחזיר‬
.false ‫ תמיד יחזיר‬,null ‫ שיוגדר כערך‬varname ‫כל משתנה‬
.true ‫ יחזירו‬,Object ‫ מול הטיפוס‬instanceof ‫ כשתופעל עליו השיטה‬,)null ‫ (שערכו אינו‬varname ‫כל משתנה‬
String s = new String (“Hello”);
Integer i = new Integer (3);
Object o = i;
S.o.p (s instanceof String); //true
s = null;
S.o.p (s instanceof String); //false
S.o.p (s instanceof Object); //false;
S.o.p (o instanceof Integer); //true
S.o.p (o instanceof String); //false
S.o.p (i instanceof Object); //true

47
:DynamicArray ‫במחלקה‬
public boolean equals (Object other){
boolean isEqual = true;
if (!(other instanceof DynamicArray)){
isEqual = false;
}
else if (size() != ((DynamicArray)other).size()){
isEqual = false;
}
else {
for (int i=0; i<size() & isEqual ; i=i+1){
if (!get(i).equals(((DynamicArray)other).get(i)){
isEqual = false;
}
}
}
return isEqual;
}

:Integer ‫ שמקבלת רק טיפוסים מסוג‬DynamicArray ‫נרצה לכתוב מחלקה חדשה‬


public class DynamicIntegerArray extends DynamicArray{
//constructors
public DynamicIntegerArray(){
super();
{

//methods
public void add(Object element){
if (!(element instanceof Integer))
throw new IllegalArgumentException();
super.add(element);
}
public Object set(int index, Object element){
if (!(element instanceof Integer))
throw new IllegalArgumentException();
return super.set(index, element);
}
public int sum() {
int sum = 0;
for (int i = 0; i < size(); i = i + 1)
sum = sum + (Integer) get(i);
return sum;
}
}

48
‫‪2.12.18‬‬

‫הרצאה ‪15‬‬
‫מבוא לתכנות מובנה עצמים ‪ ,Generics – 4‬ממשקים‬
‫מערך דינאמי גנרי‬
‫נרצה להגדיר מחלקה של מערך דינאמי של טיפוס לא ידוע ‪ ,T‬ולתת למשתמש לקבוע מהו ‪ %‬בזמן ריצה‪.‬‬
‫כאשר אנחנו לא יודעים איזה טיפוסים אנו רוצים שה‪ DynamicArray-‬יקבל‪ ,‬במקום ליצור מחלקה של‬
‫‪ ,DynamicArray‬ניתן ליצור מחלקה של ‪ DynamicArray‬מטיפוס ‪ T‬כלשהו‪ .‬בזמן אמת‪ ,‬בזמן ריצה‪ ,‬כאשר המחשב‬
‫יקבל מהמשתמש את הטיפוס ‪ ,T‬הוא יפעיל את השיטות הרלוונטיות אליו‪.‬‬
‫כיוון שבחלק מהבנאים והשיטות לא נוכל להבטיח בוודאות שהמערך הדינאמי שלנו הוא מאותו טיפוס ‪ T‬שהוגדר‪ ,‬נציב‬
‫סימן שאלה ? במקום בו מצהירים על טיפוס המחלקה‪.‬‬
‫"?" ב‪ JAVA‬מכונה ‪ ,wild card‬הוא מתנהג כמו ג'וקר ולמעשה יכול להחליף כל דבר‪.‬‬

‫‪49‬‬
public class DynamicArray <T> implements List<T> { //‫הסבר על ליסט בהמשך‬
private Object[] data; //‫שדות – ללא שינוי מהמחלקה הדינאמית הרגילה‬
private int size;

public DynamicArray(){…} //‫ללא שינוי‬


public DynamicArray(initialCapacity){…} //‫ללא שינוי‬
public int size(){…}
public void add(T element){…} //‫נשאר אותו דבר למעט החתימה‬
public void add(int index, T element){…} //‫נשאר אותו דבר למעט החתימה‬
public T set (
public T get(int index){
rangeCheck(index);
return (T)data[index]; //(type safety) ‫כאשר נכתוב את זה בבית נקבל אזהרה מהקומפיילר‬
}
public boolean equals (Object other){
boolean isEqual = true;
if (!(other instanceof DynamicArray<?>)){
isEqual = false;
}
else if (size() != ((DynamicArray<?>)other).size()){
isEqual = false;
}
else {
for (int i=0; i<size() & isEqual ; i=i+1){
if (!get(i).equals(((DynamicArray<?>)other).get(i)){
isEqual = false;
}
}
}
return isEqual;
}
}

public class Test{


public static void main(){
DynamicArray<Integer> integerDA = new DynamicArray<Integer>();
DynamicArray<String> stringDA = new DynamicArray<String>();
stringDA.add("Hello");
char c = stringSA.get(0).charAt(0);
stringDA.add(1); //compilation error
stringDA.equals(integerDA);
}
}

‫ נכתוב זאת‬.‫ נכתוב מחלקה מרחיבה למחלקה מטיפוס ספציפי‬,‫אחרי יצירת מערך דינאמי שיכול לקבל כל טיפוס‬
public class DynamicIntegerArray extends DynamicArray<T>{..} :‫בצורה זו‬

.T ‫ מטיפוס‬DynamicArray ‫בבלוק נכתוב את כל השיטות הרלוונטיות המרחיבות למחלקת‬

‫ממשקים‬
.)‫ממשק הוא הגדרה של התנהגות בלבד (ולא מימוש‬
.‫ ומאפשר לייצג רעיון מופשט ע"י טיפוס‬,‫ממשק מגדיר טיפוס ע"י אפיון תכונות‬
.‫ משתמשים כדי לממש את הממשק‬implements ‫ במילה‬.‫ לממשק‬java‫ זאת מילה שמורה ב‬interface ‫המילה‬

50
public class interface List <T>{
public int size(){…}
public void add(T element){…}
public void add(int index, T element
public T set (
public T get(int index){
:
:
}
}
//:‫במחלקה אחרת‬
List<Integer> myList = new DynamicArray<Integer>();
myList.add(32); //‫לא מתקמפל‬
myList.add(0,32); //‫עובר בשלוםם‬

:‫ברשימות‬

List ‫נרצה להגדיר מחלקה שבה פונקציית סטטית שעוברת על רשימה‬

public static void printVertical(List someList){


for (int i=0; i<someList.size(), i= i+1){
System.out.println(someList.get(i));
}
}

51
6.12.18

16 ‫הרצאה‬
‫המשך ממשקים‬
‫ הקבוצה תממש את‬,‫ כלומר‬.)‫ אין חזרות‬,‫נכיר ממשק נוסף המתאר את הרעיון המתמטי של קבוצה (אין חשיבות לסדר‬
.‫הממשק‬

public interface Set <T>{


public int size();
public boolean add(T element);
public boolean remove(T element);
public boolean contains(T element);
public boolean isEmpty();
}‫ב‬

Set<Integer> set1 = new SetAsDynamicArray<Integer>();


set1.add(1);
set1.add(2);
set1.add(1);
set1.size(); //2
//set1 = {1,2} or {2,1}
Set<Integer> set2 = new SetAsDynamicArray<Integer>();
set2.add(3);
set2.add(2);
set2.add(3);
set2.size(); //2
//set3 = {3,2} or {2,3}
Sets.union(set1, set2); //creates new set {1,2,3}

52
public class SetAsDynamicArray <T> implements Set<T>{
//fields
private List<T> elements;
//constructors
public SetAsDynamicArray(){
elements = new DynamicArray<T>();
}
//methods
public int size(){
return elements size;
}
public boolean contains(T element){
return elements.contains(element);
}
public boolean isEmpty(){
return elements.isEmpty();
}
public boolean remove(T element){
return elements.remove(element);
}
public boolean add(T element){
if (element == null){
throw new IllegalArgumentException();
if (!contains(element)){
elements.add(element);
return true;
}
return false;
}
public Iterator <T> iterator(){
return element.iterator();
}

‫ לצורך כך נגדיר שני רעיונות שעובדים‬.‫אנחנו צריכים להגדיר שיטה שמאפשר לעבור באופן סדרתי על מבנה הנתונים‬
.‫ הרעיון שגורם למבנה הנתונים לאפשר "מעבר" עליו והרעיון של לולאה כעצם‬:‫ביחד‬
:‫שני הרעיונות האלו ייוצגו ע"י שני ממשקים מתאימים‬
.‫ מייצג לולאה (מעבר יחיד חד פעמי) על איברי מבנה הנתונים‬Iterator<T> ‫ הממשק‬.1
public interface Iterator <T>{
public boolean hasNext();
public T next();
}

.‫ מייצג את היכולת לבצע לולאה (מעבר סדרתי) על איברי המבנה‬Iterable<T> ‫ הממשק‬.2


public interface Iterable <T>{
public Iterator<t> iterator();
}

.‫ יש למבנה זה איטראטור‬,‫משמע‬

53
public class DynamicArray <T> implements List<T>{
public Iterator<T> iterator(){
return new DynamicArrayIterator(this);
}
}

public class DynamicArrayIterator <T> implements Iterator<T>{


//the index of the next element that would be returned
private new DynamicArray<T> array;
int index;
public DynamicArray Iterator (DynamicArray<T> array){
this.array = array;
index = 0;
}
public boolean hasNext(){
return (index<array.size());
}
public T next(){
if (!hasNext){
throw new NoSuchElementException();
}
T next = array.get(index);
index = index+1;
return next;
}
}

:sets ‫במחלקה‬
public static <T> boolean subset (Set<T> setA, Set<T> setB)
boolean isSubset = true;
Iterator<T> iterA = setA.iterator();
while (iterA.hasNext() & isSubset){
isSubset = setB.contains(iterA.next());
}
}

54
13.12.18

17 ‫הרצאה‬
‫המשך ממשקים‬
:‫דוגמה‬
List<String> guests = new SetAsDynamicArray<>();
List<String> guests = new SetAsLinkedList<>(); :‫או לחילופין אפשר לכתוב‬//
guests.add("super-man");
guests.add("spider-man");
Iterator<String> iter = guests.iterator();
while(iter.hasNext){ S.o.p(iter.next());}

:DynamicArray ‫המחשת שימוש באיטראטור של‬

:LinkedList ‫המחשת שימוש באיטראטור‬

55
‫איטראטור מספרי פיבונאצ'י‬
public class FibonacciIterator implements Iterator<Integer>{
private int maxValue;
private int prevValue; //‫האיבר האחרון שהוחזר‬
private int nextValue; //‫האיבר הבא שיוחזר‬
public FibonacciIterator (int maxValue){
this.maxValue = maxValue;
nextValue = 1;
nextValue = 0;
{
public Integer hasNext(){
return next value <= maxValue;
}
public Integer next(){
if (!hasNext())
throw new NoSuchElementException(); //‫צריך לייבא מג'אווה‬
Integer output = nextValue;
nextValue = nextValue + prevValue;
prevValue = output;
return output;
}
}

//main-‫ב‬
Iterator<Integer> fibIter = new FibbonaciIterator(100);
while (fibIter.hasNext()){
System.out.println(fibIter.next());
}

for-each ‫לולאת‬
,)for-each( ‫ ניתן לבצע לולאה בכתיב מקוצר‬Iterable<T> ‫כאשר בידינו (עצם) מופע של מחלקה המממשת את‬
.‫ שניתן לעבור עליו‬,iterable ‫ מאפשרת לנו לעשות את זה עבור כל עצם שהוא‬Java .‫שלמה ללא תנאים‬
:‫נכתוב את זה כך‬
for (T element: iterableObject){
action with element;
}

:‫ במחלקה המכילה פונקציות סטטיות על קבוצות‬for-each ‫נראה איך להשתמש בלולאה‬


)‫(דוגמה לפונקציה המחשבת חיתוך של שתי קבוצות‬
//for-each ‫מימוש עם‬
public static <T> Set<T> intersection (Set<T> setA, Set<T> setB){
Set<T< intersection = new SetAsDynamicArray;
for (T element: setA){
if (setB.contains(element)){
intersection.add(element);
}
return intersection;
}

//iterator ‫ מימוש עם‬,‫לחילופין‬


public static <T> Set<T> intersection (Set<T> setA, Set<T> setB){
Set<T< intersection = new SetAsDynamicArray;
Iterator<T> iterA = setA.iterator;
while (iterA.hasNext()){;
if (setB.contains(element)){
intersection.add(element);
}
}
return intersection;
}

56
‫מושג הסדר הטבעי‬
‫ ועוד‬...,Integer, String, :‫מוג הסדר הטבעי קיים עבור מספר טיפוסים‬
:Comparable ‫הרעיון הזה מיוצג ע"י הממשק‬

public interface Comparable<T>{


public interface compareTo(T element);
}

:‫ הניתנים להשוואה‬a,b ‫ עבור שני עצמים‬:‫החוזה של הממשק אומר‬


a.compareTo(b)<0 ‫אז‬ a<b ‫אם‬
a.compareTo(b)=0 ‫ אז‬a.equals(b) ‫אם‬
a.compareTo(b)>0 ‫אז‬ a>b ‫אם‬
//Integer ‫בתוך המחלקה‬
public int compareTo (Integer element){
return this.intValue() = element.intValue();
}

insertionSort ‫מיון הכנסה‬


comparable ,‫) עבור כל דבר שהוא בר השוואה‬7 ‫(ראינו בהרצאה‬InsertionSort ‫נכתוב מיון הכנסה‬
=
public static <T> extends Comparable<T>> void insertionSort (T[] arr){
for (int i=1; i<arr.length; i=i+1){
insert (arr,i)
}
}

public static <T extends Comparable<T>> void insert insertionSort (T[] arr, int i){
T value = arr[i];
while (i>0 && arr[i-1].compareTo(value)>0){
arr[i] = arr[i-1];
i = i-1;
}
arr[i] = value;
}

comparator
.comparator – ‫ משתמשים בגורם חיצוני שמבצע השוואה‬,‫כאשר אין סדר טבעי‬
:‫הרעיון של משווה חיצוני מיוצג ע"י הממשק‬

57
public class Student{
private int id;
private String name;
:
:
}

public interface Comparator <T> {


public int compare (T obj1, T obj2);
}

public class StudentComparatorByID implements Comparatore<Student>{


public int compare (Student a, Student b){
return a.getID()-b.getID();
}
}

public class StudentComparatorByName implements Comparatore<Student>{


public String compare (Student a, Student b){
return a.getName()-b.getName ();
}
}

‫מיון הכנסה‬
:‫השוואה‬-‫כעת נכתוב מיון הכנסה גם עבור טיפוסים שאינם בני‬
public static <T> void insertionSort (T[] arr, Comparator<T> comp){
for (int i=1; i<arr.length; i=i+1){
insert (arr,i)
}
}

public <T> void insert insertionSort (T[] arr, int i, Comparator<T> comp){
T value = arr[i];
while (i>0 && comp.compare(arr[i-],value)>0){
arr[i] = arr[i-1];
i = i-1;
}
arr[i] = value;
}

58
‫‪16.12.18‬‬

‫הרצאה ‪18‬‬
‫עצים בינאריים – ‪Binary Trees‬‬
‫עצים בינאריים תומכים בפעולות הבאות בצורה יעילה‪:‬‬
‫הכנסה‬
‫הוצאה‬
‫חיפוש‬
‫מבנה העץ‪:‬‬
‫עץ בינארי הוא מבנה נתונים המייצג עץ מושרש המורכב מקודקודים‪/‬צמתים (‪)node‬‬
‫הקודקוד "העליון" בעץ הוא השורש‬
‫כל קודקוד מכיל נתון (מידע) מסוים‬
‫כל קדקוד מכיל בנוסף הפניות לשני בנים‪ :‬בן ימני ובן שמאלי (הקודקוד יכול להכיל הפניה ליותר משני בנים‪ ,‬אך אנחנו‬
‫מדברים על עצים בינארים)‬
‫מבנה העץ הוא רקורסיבי‪ :‬כל קודקוד הוא שורש של תת‪-‬עץ כלשהו‬
‫לכל קודקוד יש לכל היותר אב אחד‪ .‬רק לשורש אין אב‪.‬‬

‫לכל לעץ יש גודל‪ .‬גודל העץ הוא כמספר הקודקודים‪.‬‬


‫גובה העץ – נקבע לפי המרחק הגדול ביותר בין העלה לשורש‪ .‬אם העץ "מאוזן"‪ ,‬גובה העץ הוא לוגריתמי ביחס לגודל‬
‫העץ‪.‬‬
‫העץ מתאפיין ע"י השורש שלו (כשם שרשימה מקושרת מתאפיינת ע"י החוליה הראשונה שלה)‬

‫‪59‬‬
‫‪BinaryNode & BinaryTree‬‬
‫המחלקה ‪ BinaryTree‬משמשת כמחלקה עוטפת למחלקה ‪ BinaryNode‬שכן מבנה הנתונים הוא העץ עצמו‪,‬‬
‫והשיטות הציבוריות שלו הן ממשק המשתמש‪.‬‬
‫המשתמש לא מכיר את המחלקה ‪.BinaryNode‬‬
‫הגדרות‪:‬‬
‫צומת ‪ A‬הוא אב (‪ )parent‬של צומת ‪ B‬אם ‪ B‬הוא בן (‪ )child‬שמאלי או בן ימני של ‪.A‬‬ ‫‪.1‬‬
‫צומת ‪ B‬הוא צאצא (‪ )successor‬של צומת ‪ A‬אם‪:‬‬ ‫‪.2‬‬
‫• ‪,B = A‬‬
‫• ‪ B‬הוא בן (שמאלי או ימני) של ‪,A‬‬
‫• ‪ B‬הוא צאצא של בן (שמאלי או ימני) של ‪.A‬‬
‫צומת ‪ A‬הוא אב קדמון (‪ )ancestor/predecessor‬של צומת ‪ B‬אם ‪ B‬הוא צאצא של ‪.A‬‬ ‫‪.3‬‬
‫תת העץ המושרש בצומת ‪ A‬הוא תת הגרף המכיל את כל הצאצאים של ‪.A‬‬ ‫‪.4‬‬
‫עומק של צומת הוא מספר הקשתות במסלול מהצומת אל שורש העץ (לכן עומק השורש הוא ‪)0‬‬ ‫‪.5‬‬
‫גובה של צומת שווה למספר הקשתות המקסימאלי במסלול מהצומת לעלה בתת העץ שלו (לכן גובה של‬ ‫‪.6‬‬
‫עלים הוא ‪)0‬‬
‫גובה של עץ שווה לגובה של שורש העץ‪.‬‬ ‫‪.7‬‬
‫גודל של עץ שווה למספר הצמתים בעץ‪.‬‬ ‫‪.8‬‬

‫מאפייני ההרשאה ‪protected‬‬


‫ההרשאה ‪ protected‬מאפשר למחלקות יורשות לגשת לשדות ושיטות במחלקת האב‬
‫עבור המחלקות היורשות‪ ,‬שדה או שיטה שהם ‪ protected‬במחלקת האב מתנהגים כמו ‪ :Public‬ניתן לגשת אליהם‪,‬‬
‫ניתן לדרוס את השיטות (לא נכון לגבי שדות)‬
‫עבור מחלקות שאינן יורשות (מחלקות חיצוניות) שדה או שיטה עם מאפיין ‪ protected‬מתנהגים כמו ‪.private‬‬

‫הכנסה ‪Insert‬‬
‫הכנסת נתון לעץ תהיה שרירותית‪ ,‬ללא חשיבות לתא בו הוכנס הנתון‪.‬‬
‫בהינתן איבר שאמור להתווסף לעץ‪:‬‬
‫בהסתברות ‪ ,1/2‬הכניסו את האיבר לתת העץ השמאלי‪ ,‬ובהסתברות ‪ 1/2‬לתת העץ הימני‬ ‫‪-‬‬
‫הוספת איבר‪ ,‬כמו פעולות רבות אחרות על עץ ‪,‬היא פעולה רקורסיבית‬ ‫‪-‬‬

‫חיפוש איבר – השיטה ‪Contains‬‬


‫חיפוש איבר‪ ,‬כמו פעולת ההכנסה‪ ,‬גם היא פעולה רקורסיבית‪.‬‬
‫אם האיבר המבוקש שווה לאיבר השורש‪ ,‬החזר תשובה חיובית‪.‬‬
‫אחרת‪ ,‬חפש את האיבר בתת העץ השמאלי ואם הוא נמצא שם החזר תשובה חיובית‪.‬‬
‫אחרת‪ ,‬חפש את האיבר בתת העץ הימני ואם הוא נמצא שם החזר תשובה חיובית‪.‬‬
‫בסוף – אם לא נמצא‪ ,‬החזר תשובה שלילית‪.‬‬

‫בדיקת שוויון בין עצים‬


‫שני עצים יוגדרו כשווים אם יש להם את אותו המבנה ואותו התוכן‪.‬‬

‫‪60‬‬
‫סריקה של עצים‬
‫ישנן מספר דרכים לעבור באופן סדרתי על כל צמתי העץ‪:‬‬
‫‪inOrder .1‬‬
‫עוברת על איברי העץ בסדר ממויין מקטן לגדול‬
‫• מעבר על תת העץ השמאלי (אם קיים)‬
‫• מעבר על השורש‬
‫• מעבר על תת העץ הימני (אם קיים)‬
‫‪ :preOrder .2‬שורש‪ ,‬שמאל‪ ,‬ימין‬
‫‪ :postOrder .3‬שמאל‪ ,‬ימין‪ ,‬שורש‬

‫‪61‬‬
public class BinaryNode<T> {
protected T data;
protected BinaryNode<T> left;
protected BinaryNode<T> right;

public BinaryNode(T data) {


if (data == null)}
throw new NullPointerException();
{
this.data = data;
left = null;
right = null;
}
public void insert(T element) {
if (Math.random() < 0.5) {
if (left == null)
left = new BinaryNode(element);
else
left.insert(element);
}
else {
if (right == null)
right = new BinaryNode(element);
else
right.insert(element);
}
}
}
public boolean contains(T element) {
boolean found = false;
if (data.equals(element))
found = true;
else if (left != null && left.contains(element))
found = true;
else if (right!= null && right.contains(element))
found = true;
return found;
}
public int height() {
int leftH = -1, rightH = -1;
if (left != null)
leftH = left.height();
if (right != nul))
rightH = rightH.height();
return Math.max(leftH, rightH)+1;
}
public int size() {
int leftS = 0, rightH = 0;
if (left != null)
leftH = left.size();
if (right != null))
rightS = rightH.size();
return leftS+rightS+1;
}
public boolean equals (Object other){
boolean isEqual = false;
if (other instanceof BinaryNode<?>){
BinaryNode<?> otherNode = (BinaryNode<?>)other;
isEqual = (data.equals(otherNode.data)) &&
((left == null & otherNode.left == null) ||
(left != null && left.equals(otherNode.left))) &&
((right == null & otherNode.right == null) ||
(right!= null && right.equals(otherNode.left)));
}
return isEqual;
}
public void inOrder() {
if(left != null)
left.inOrder();
System.out.println(data.toString());
if(right != null)
right.inOrder();
}
62
public class BinaryTree<T> implements Iterable<T>{
protected BinaryNode<T> root;
public BinaryTree(){
root = null;
}
public boolean isEmpty(){
return root == null;
}
public void insert(T element) {
if (data == null)}
throw new NullPointerException();
{

if (isEmpty())}
root = new BinaryNode<>(element);
{
else}
root.insert(element);
}
{
public boolean contains(T element) {
if (isEmpty())
return false;
else
return root.contains(element);
}
public int height(){
if (isEmpty())
return -1;
else
return root.height();
}
public int size(){
if (isEmpty())
return 0;
else
return root.size();
}
public boolean equals (Object other) {
boolean isEqual = true;
if (!(other instanceof BinaryTree<?>))}
isEqual = false;
{
else if (isEmpty())}
isEqual = )(BinaryTree<?>)other).isEmpty();
{
else}
root.equals(((BinaryTree<?>)other).root);
}
return isEqual;
{
public void inOrder() {
if(!isEmpty())
root.inOrder();

}
{

63
‫‪20.12.18‬‬

‫הרצאה ‪19‬‬
‫עצי חיפוש בינאריים‬
‫הרעיון של עצי חיפוש בינאריים‪:‬‬
‫כל האיברים בתת העץ השמאלי קטנים מהאיבר בשורש‪ ,‬וכל האיברים בתת העץ‬ ‫•‬
‫הימני גדולים מהאיבר בשורש‬
‫תנאים אלו מתקיימים גם ביחס לשורש העץ כולו‪ ,‬וגם בכל אחד מתתי העצים של‬ ‫•‬
‫העץ‪.‬‬
‫*בקורס זה לא נאפשר כפילויות בעץ‪.‬‬
‫תחזוק של מבנה נתונים התומך בפעולות חיפוש‪ ,‬הכנסה והוצאה הוא פעולה בסיסית ביותר‪.‬‬
‫בעץ בינארי נבצע ‪ log n‬פעולות(צעדים) כדי למצוא‪/‬לחפש‪/‬להכניס איבר מסויים‪.‬‬
‫המחלקה ‪ BinarySearchTree‬לא מממשת את המחלקה ‪ comparable‬כדי שהיא לא תוגבל רק לטיפוסים‬
‫שמממשים את ‪ comparable‬בעצמם‪.‬‬
‫אם אכן רוצים לעשות חיפוש על עץ בינארי שמכיל עצמים שהם ‪ comparable‬מטבעם‪ ,‬צריך לכתוב להם מחלקה‬
‫‪.comparator‬‬
‫**לטיפוסים שהם ‪ comparable‬יש את השיטה ‪compareTo‬‬
‫**לטיפוסים שהם לא ‪ comparable‬צריך לכתוב ‪ comparator‬שיכיל את השיטה ‪ compare‬שמקבלת כקלט שני‬
‫אובייקטים‪.‬‬
‫אם למשל מדובר בעץ של טיפוסים מסוג ‪:Integer‬‬
‫{ >‪public class IntegerComparator implements Comparator<Integer‬‬
‫{)‪public int compare(Integer i1, Integer i2‬‬
‫{)‪if (i1 == null‬‬
‫;)(‪throw new NullPointerException‬‬
‫}‬
‫;)‪return i1.compareTo(i2‬‬
‫}‬
‫}‬

‫הסרת איבר מהעץ‬


‫כשנסיר איבר מהעץ (מבנה העץ ישתנה) נבדיל בין שלושה מקרים‪:‬‬
‫‪ .1‬המידע נמצא בעלה – נעדכן את המצביע המתאים של אביו להיות ‪null‬‬
‫‪ .2‬המידע נמצא בצומת בעל בן יחיד – נעדכן את המצביע המתאים של אביו להצביע על בנו היחיד‪.‬‬
‫‪ .3‬המידע נמצא בצומת בעל שני בנים‪ :‬נחליף את המידע בצומת עם מידע הקטן ביותר בתת העץ הימני ונסיר‬
‫את המידע הקטן הזה מתת העץ הימני‪.‬‬
‫(‪ 1‬ו‪ 3-‬הם מקרי קצה)‬

‫‪64‬‬
public class BinarySearchTree<T> extands BinaryTree<T>{

private Comparator<T> treeComparator;


public BinarySearchTree(Comparator<T> treeComparator){
super();
this.treeComparator = treeComparator;
{
public void insert(T element) {
if (element == null)
throw new NullPointerException();
if(isEmpty())
root = new BinarySearchNode(element, treeComparator);
else
root.insert(element);
}
public void remove(T toRemove){
if(!isEmpty())
root = root.remove(toRemove);

}
{

65
public class BinarySearchNode<T> extands BinaryNode<T>{

private Comparator<T> treeComparator;


public BinarySearchNode(T data, Comparator<T> treeComparator){
super(data);
this.treeComparator = treeComparator;
{

public void insert(T element) {


if (treeComparator.compare(element, data)< 0){
if (left == null)
left = new BinarySearchNode<T>(element, treeComparator);
else
left.insert(element);
}
else if (treeComparator.compare(element, data) > 0){
if (right == null)
right = new BinarySearchNode<T>(element, treeComparator);
else
right.insert(element);
}
}
}

public BinaryNode<T> remove(T element){


//element would (might) be removed from the left sub-tree
if (treeComparator.compare(element, data)<0){
if (left != null)
left = left.remove(element);
}
//element would (might) be removed from the right sub-tree
else if (treeComparator.compare(element, data) > 0){
if (right != null)
right = right.remove(element);
}
//cases 1 or 2
else{ //need to remove the data in this node
if (left == null | right == null){ // no children/1 child
if (left == null) // (base cases)
return right;
else
return left;
}
else{ // this node has two children
data = ((BinarySearchNode<T>) right).findMin();
right = right.remove(data);
}
}
return this;
{
{

66
23.12.18

20 ‫הרצאה‬
Queue ‫ תור‬,Stack ‫ מחסנית‬,Tree Iterator
Stack ‫הממשק‬
‫ הפעולה הראשונה שמתבטלת זו‬undo ‫ (כשעושים‬word‫ כמו למשל שמירת פעולות ב‬,‫ממשק שמוכר לנו כבר‬
.'‫ מנגנון הקריאה לפונקציות וכו‬,)‫האחרונה שבוצעה‬
public interface Stack<T>{
//Returns true iff this stack is empty.
public boolean isEmpty();

//Removes the object at the top of this stack and returns that object.
public T pop();

//Pushes an item onto the top of this stack.


public void push(T element);

//Returns the top element without removing it.


public T peek();
}

StackAsDynamicArray ‫המחלקה‬
public class StackAsDynamicArray<T> implements Stack<T> {
private DynamicArray<T> array ;

public StackAsDynamicArray() {
this.array = new DynamicArray<>() ;
}

public boolean isEmpty() {


return array.isEmpty();
}

public T pop() {
if (isEmpty)
throw new NoSuchElementException();
return array.remove(array.size()-1) ;
}

public void push(T element) {


if(element == null)
throw new NullPointerException();
array.add(element);
}

public T peek() {
if (isEmpty)
throw new NoSuchElementException();
return array.get(array.size()-1) ;
}
}

67
:stack‫דוגמה לשימוש ב‬
//a part of some main function
Stack<String> st = new StackAsDynamicArray();
st.push("man");
st.push("bites");
st.push("dog");

while(!st.isEmpty()) {
System.out.print(st.pop() + " ");
}
System.out.println();
// will print 'dog bites man'

‫איטראטורים על עצים‬
public class BinaryTree<T> implements Iterable<T>{
protected BinaryNode<T> root;
... //all the methods we've seen already

//Default method
public Iterator<T> inOrderIterator() {
return new BinaryTreeInOrderIterator<>(root);
}

public Iterator<T> preOrderIterator() {


return new BinaryTreePreOrderIterator<>(root);
}

//)‫ אחכ הבנים משמאל לימין וכן הלאה‬,‫מעבר על העץ לפי רמות (קודם השורש‬
public Iterator<T> bfsIterator() {
return new BinaryTreeBFSIterator<>(root);
}

public Iterator<T> iterator() {


return inOrderIterator();
}
}

68
‫מימוש האיטראטורים‬
InOrder Iterator
public class BinaryTreeInOrderIterator<T> implements Iterator<T>{
private Stack<BinaryNode<T>> stack;
public BinaryTreeInOrderIterator(BinaryNode<T> root) {
stack = new StackAsDynamicArray<>();
prepareNext(root); //making sure the stack is legal
}

private void prepareNext(BinaryNode<T> node) {


while (node != null) {
stack.push(node);
node = node.left;
}
}

public boolean hasNext() {


return !stack.isEmpty();
}

public T next() {
if(!hasNext())
throw new NoSuchElementException();
BinaryNode<T> node = stack.pop();
if (node.right != null)
prepareNext(node.right);
return node.data;
}
}

PreOrder Iterator
public class BinaryTreePreOrderIterator<T> implements Iterator<T>{
private Stack<BinaryNode<T>> stack;

public BinaryTreePreOrderIterator(BinaryNode<T> root) {


stack = new StackAsDynamicArray<>();
stack.push(root);
}

public boolean hasNext() {


return !stack.isEmpty();
}

public T next() {
if(!hasNext())
throw new NoSuchElementException();
BinaryNode<T> node = stack.pop();
if (node.right != null)
stack.push(node.right);
if (node.left != null)
stack.push(node.left);
return node.data;
}
}

69
BFS Itertator
public class BinaryTreeBFSIterator<T> implements Iterator<T>{
private Stack<BinaryNode<T>> q;

public BinaryTreeBFSIterator(BinaryNode<T> root) {


q = new QueueAsLinkedList<T>>();
q.enqueue(root);
}

public boolean hasNext() {


return !q.isEmpty();
}

public T next() {
if(!hasNext())
throw new NoSuchElementException();
BinaryNode<T> node = q.dequeue();
if (node.left != null)
q.enqueue(node.left);
if (node.right != null)
q.enqueue(node.right);

return node.data;
}
}

.‫ כפי שנראה בהמשך‬Queue ‫לטובת זה נשתמש בממשק‬

‫ – תור‬Queue ‫הממשק‬
‫ כמו למשל הקלדה במקלדת (האות הראשונה שתוקלד היא‬.‫ הוא ממשק שכבר מוכר לנו‬Queue ‫גם הממשק‬
.'‫הראשונה שתופיע במחשב) וכד‬
public interface Queue<T> {
//Returns true if this queue is empty.
public boolean isEmpty();

//Removes the object at the front of this queue and returns that object.
public T dequeue();

//Insert an item into the back of this queue.


public void enqueue(T element);

//Returns the top element without removing it.


public T peek();
{

QueueAsLinkedList ‫המחלקה‬

70
public class QueueAsLinkedList<T> implements Queue<T>{
private List<T> list;
public QueueAsLinkedList() {
list = new LinkedList<>();
}

public boolean isEmpty() {


return list.isEmpty();
}

public void enqueue(T element) { //like the method add


if(element == null)
throw new NullPointerException();
list.add(element);
}

public T dequeue() { //like the method remove


if(isEmpty())
throw new NoSuchElementException();
return list.remove(0);
}

public T peek() {
if(isEmpty())
throw new NoSuchElementException();
return list.get(0);
}
}

‫סריקה לפי רמות‬


)1 ‫ (רמה‬1 ‫נרצה להדפיס קודם את‬ -
)2 ‫ (רמה‬1 ‫ שבעומק‬2-‫ ו‬9 ‫אחר כך את‬ -
)3 ‫ (רמה‬2 ‫אחר כך את כל מי שבעומק‬ -
...‫וכן הלאה‬ -
.‫נראה שאין אפשרות לעשות זאת באופן רקורסיבי‬
.BFS Iterator-‫לצורך כך נשתמש ב‬

BFS Itertator
public class BinaryTreeBFSIterator<T> implements Iterator<T>{
private Stack<BinaryNode<T>> q;

public BinaryTreeBFSIterator(BinaryNode<T> root) {


q = new QueueAsLinkedList<T>>();
q.enqueue(root);
}

public boolean hasNext() {


return !q.isEmpty();
}

public T next() {
if(!hasNext())
throw new NoSuchElementException();
BinaryNode<T> node = q.dequeue();
if (node.left != null)
q.enqueue(node.left);
if (node.right != null)
q.enqueue(node.right);

return node.data;
}
}

71
‫שוויון בין עצי חיפוש בינאריים‬
.‫ מושג ההשוואה משתכלל‬,)‫כאשר מדובר בעצי חיפוש (המייצגים סדר‬
.‫ מבנה העץ לא מעניין לבדיקת שוויון‬.‫שני עצים שווים כאשר הם מייצגים את אותו הסדר‬
:‫העצים הבאים שווים‬

:‫ בעצי חיפוש בינאריים‬equals ‫השיטה‬


public boolean equals(Object other){

boolean isEqual = true;

if(!(other instanceof BinarySearchTree<?>))


isEqual = false ;
else {
Iterator<?> thisIter = this.iterator() ;
Iterator<?> otherIter = ((BinarySearchTree<?>) other).iterator() ;
while(thisIter.hasNext() & otherIter.hasNext() & isEqual) {
if(!thisIter.next().equals(otherIter.next()))
isEqual = false ;

}
if (isEqual)
isEqual = thisIter.hasNext() == otherIter.hasNext();

}
return isEqual;
}

72
‫‪27.12.18‬‬

‫הרצאה ‪21‬‬
‫שימוש במחסניות‪ ,‬שערוך ביטוי בנוסח ‪postfix‬‬
‫ביטויים בנוסח ‪Postfix‬‬
‫ישנם שלושה נוסחים בהם מקובל להשתמש בכתיבת ביטוי מתמטי‪:‬‬
‫‪ – Infix .1‬כאשר אופרטור הפעולה נכתב בין שני האופרנדים (למשל ‪)1+2‬‬
‫‪ – Prefix .2‬כאשר אופרטור הפעולה נכתב לפני האופרנד (למשל ‪)sinx‬‬
‫‪ – Postfix .3‬כאשר אופרטור הפעולה נכתב אחרי האופרנד (למשל !‪)n‬‬
‫‪ Infix‬הוא הנוסח הנפוץ ביותר אבל עשוי להיות מסורבל לפענוח באופן ממוחשב כאשר מדובר בביטוי מסובך או ארוך‪,‬‬
‫לכן נרצה להמיר ביטויים בנוסח ‪ infix‬לביטויים בנוסח ‪.postfix‬‬
‫ניתן לרשום כל ביטוי כעץ‪:‬‬

‫סריקה‬ ‫נוסח‬ ‫ביטוי‬


‫‪in-order‬‬ ‫‪infix‬‬ ‫‪a+b*c‬‬
‫‪pre-order‬‬ ‫‪prefix‬‬ ‫‪+ a *b c‬‬
‫‪post-order‬‬ ‫‪postfix‬‬ ‫‪abc*+‬‬

‫הסריקה המתאימה על העץ תתן לנו את נוסח הביטוי המתאים לה‪.‬‬


‫ביטוי מקורי בנוסח ‪(5+2*((3+4)*(7+3)+11))*9 = 1503 :infix‬‬
‫ניתן לכתוב את אותו הביטוי בנוסח ‪5 2 3 4 + 7 3 + * 11 + * + 9 :postfix‬‬
‫(נעבוד רק על אופרטורים דו מקומיים‪ .‬כל אופרטור בביטוי יופעל על שני האופרנדים שמופיעים לפניו)‬

‫‪73‬‬
‫חישוב ערך של ביטוי בנוסח ‪Postfix‬‬
‫מימוש ראשון‬
‫בהינתן ביטוי בצורת ‪ ,postfix‬יש לחשב את ערכו‪.‬‬
‫נייצג מספר כ‪ integer‬ואופרטור פעולה כ‪.string‬‬
‫נייצר קלט באופן הבא‪Object[] pfExpr = {5,2,3,4,"+",7,5,"+","*",11,"+","*","+",9,"*"} :‬‬
‫נכתוב פונקציה סטטית בשם ‪ evaluate‬המחשבת את ערך הביטוי באמצעות מחסנית‪.‬‬
‫בכל פעם שנראה מספר‪ ,‬נכניס אותו כאינטג'ר למחסנית‪ .‬כשנפגוש מחרוזת (כלומר אופרטור)‪ ,‬נשלוף מהמחסנית‬
‫את שני המספרים האחרונים שהכנסנו‪ ,‬נפעיל עליהם את האופרטור‪ ,‬ונחזיר את התוצאה למחסנית‪.‬‬

‫‪//assumes input is valid‬‬


‫{)‪public static int evaluate (Object[] exp‬‬
‫;)(><‪Stack<Integer> st = new StackAsDynamicArray‬‬
‫)‪for (Object item : exp‬‬
‫{)‪if (item instanceof Integer‬‬
‫;)‪st.push((Integer)item‬‬
‫}‬
‫{‪else‬‬
‫;)(‪int rhs = st.pop‬‬
‫;)(‪int lhs = st.pop‬‬
‫))"‪if (item.equals("+‬‬
‫;)‪st.push(lhs+rhs‬‬
‫‪else‬‬
‫;)‪st.push(lhs*rhs‬‬
‫}‬
‫}‬
‫;)(‪int ans = st.pop‬‬
‫;‪return ans‬‬
‫}‬

‫‪74‬‬
‫מימוש שני‬
.‫במימוש זה האופרטורים מבצעים את הפעולה‬
.‫ מייצג עצם שיודע כיצד לפעול על מחסנית‬StackOperator ‫הממשק‬
‫ מבצע‬,‫) על המחסנית – מוציא ומכניס ערכים‬applied( ‫ האופרטור מופעל‬,postfix ‫בהינתן מחסנית לחישוב ביטוי‬
.'‫חישובים וכו‬
.‫ נמצאת בממשק רק כדי להזכיר למשתמש לדרוס אותה‬toString ‫**השיטה‬
//StackOperator ‫הממשק‬
public Interface StackOperator{
public void apply(Stack<Integer> st);
public String toString();
}

//‫המחלקה המממשת‬
public class IntConstantOperator implements StackOperator{

private Integer value;

public IntConstantOperator (int value){


this value = value;
}
public void apply (Stack<Integer> st){
st.push(value);
}
public String toString(){
return value.toString();
}
public int getValue(){
return value;
}
}

//‫פעולת חיבור‬
public class IntPlusOperator implements StackOperator{

public void apply (Stack<Integer> st){


Integer rhs = st.pop();
Integer lhs = st.pop();
st.push(rhs+lhs);
}
public String toString(){
return "+";
}
}

//‫פעולת כפל‬
public class IntMultOperator implements StackOperator{

public void apply (Stack<Integer> st){


Integer rhs = st.pop();
Integer lhs = st.pop();
st.push(rhs*lhs);
}
public String toString(){
return "*";
}
}

75
//‫המחלקה המממשת‬
public class PostfixEvaluator2{

//‫אופרטור‬-‫השימוש בסטאק‬
public static int evaluate (StackOperator[] exp){
Stack<Integer> st = new StackAsDynamicArray<>();
for (StackOperator so: exp){
so.apply(st);
}
int ans = st.pop();
return ans;
}

//‫דוגמה לפונקציית מיין כלשהי‬


public static void main (String[] args){
StackOperator Expr ={new IntConstantOperator(3),
new IntConstantOperator(4),
new IntPlusOperator(),
new IntConstantOperator(7),
new IntConstantOperator(3),
new IntPlusOperator(),
new IntMultOperator()}
System.out.println(evaluate(pfExpr)); //70
}
}

‫מימוש שלישי‬
‫ אח"כ מפעילים עליו את חלק הקוד שבנינו‬.StackOperator ‫לוקחים את סטרינג הקלט והופכים אותו למערך של‬
.2 ‫במימוש‬
public class ExpressionParser {

private List<StackOperator> operators;

public StackOperator[] parse(String str) {


String[] tokens = str.split(" ");
StackOperator[] postfixExpr = new StackOperator[tokens.length];
for (int i = 0; i<tokens.length; i=i+1) {
String token = tokens[i];
if(isInteger(token))
postfixExpr[i] = new IntConstantOperator(new Integer(token));
else {
StackOperator tokenOperator = null;
for (StackOperator operator : operators)}
if (token.equals(operator.toString()))
tokenOperator = operator;
if (tokenOperator == null)
throw new IllegalArgumentException();
postfixExpr[i] = tokenOperator;
}
}
{
return postfixExpr;
}
}

76
‫‪30.12.18‬‬

‫הרצאה ‪22‬‬
‫גרפים – ‪Graphs‬‬
‫גרף בנוי מקבוצת קודקודים (‪ )Vertices/Nodes‬וקבוצת קשתות (‪ .)Edges‬לגרף ‪ n‬קודקודים שממוספרים מ‪ 0‬ועד ‪n-1‬‬
‫ומיוצגים ע"י ‪.Integer‬‬
‫קבוצת הקודקודים מסומנת באות ‪ V‬וקבוצת הקשתות מסומנת ב‪ .E‬בקבוצת הקשתות קיימים זוגות סדורים‪ .‬כל זוג‬
‫סדור מסמן קשר בין שני קודקודים‪.‬‬
‫הקשתות מציינות אם קיים קשר בין שני קודקודים‪ true ,‬אם כן ו‪ false‬אם לא‪.‬‬
‫אחרי שיצרנו גרף לא ניתן להוסיף לו קודקודים נוספים‪.‬‬
‫אנחנו נתעסק בגרפים שאינם מכוונים (כלומר הקשתות אינן מכוונות‪ ,‬כל קשת מייצגת קשר בשני הכיוונים) וחסרי‬
‫קשתות עצמיות (אין קודקוד המחובר לעצמו בקשת)‪.‬‬
‫דוגמה‪:‬‬
‫}‪V = {0, 1, 2, 3‬‬
‫}>‪E = {<0,3>, <1,3>, <1,2>, <2,3‬‬
‫מושגים‪:‬‬
‫‪ – degree‬מספר הקשתות שיש לקודקוד‪.‬‬
‫‪degree(2) = 2‬‬
‫‪ – neighbors‬קבוצת הקודקודים שמחוברים לקודקוד מסויים בקשת‪.‬‬
‫}‪neighborsOf(3) = {0,1,2‬‬
‫‪( Connectivity‬קשירות) – גרף יקרא ק שיר אם מכל קודקוד ניתן להגיע לכל הקודקודים האחרים‬
‫{ ‪public interface Graph‬‬
‫; )(‪public int numberOfVertices‬‬
‫; )(‪public int numberOfEdges‬‬
‫; )‪public boolean containsEdge(int i, int j‬‬
‫; )‪public void addEdge(int i, int j‬‬
‫; )‪public void removeEdge(int i, int j‬‬
‫; )‪public int degree(int v‬‬
‫; )‪public Set<Integer> neighborsOf(int v‬‬
‫; )(‪public Set<Edge> edgeSet‬‬
‫}‬

‫ייצוג של קשתות‬
‫{ ‪public class Edge‬‬
‫קודקוד שמאלי בזוג‪protected int left ; //‬‬
‫קודקוד ימני בזוג‪protected int right ; //‬‬

‫{ )‪public Edge(int left, int right‬‬


‫; ‪this.left = left‬‬
‫; ‪this.right = right‬‬
‫}‬
‫{‬

‫‪77‬‬
‫ המשמעות היא לא שלא ניתן ליצור אינסטנס של‬.‫מחלקה אבסטרקטית היא מחלקה שמייצגת אובייקט מופשט‬
.‫ לא ניתן לייצר אובייקטים מסוג המחלקה‬,‫ זאת אומרת‬.new ‫המחלקה באמצעות המילה‬
.)‫שיטה אבסטרקטית היא שיטה שתופיע במחלקת האב אך תמומש במחלקת הבת (בערך כמו ממשק‬
.‫ גם המחלקה עצמה חייבת להיות אבסטרקטית‬,‫** אם במחלקה יש כמה שיטות אבסטרקטיות‬
public abstract class AbstractGraph implements Graph {

// fields
final private int nVertices ;

// constructors
public AbstractGraph(int nVertices) {
this.nVertices = nVertices ;
}

//methods
public int numberOfVertices(){
return nVertices;
}

public int numberOfEdges(){


return edgesSet().size(); //‫שיטה שתמומש במחלקה היורשת‬
}

public Set<Edge> edgesSet(){


Set<Edge> edges = new SetAsLinkedList<>();
for (int i=0; i<numberOfVertices(); i=i+1)
for (int j=i+1; j<numberOfVertices(); j=j+1)
if (containsEdge(i, j))
edges.add(new Edge(i,j));
return edges;
}

protected boolean rangeCheck(int i, int j){


return (i>=0 & i< numberOfVertices() &&
j>=0 & j< numberOfVertices() &&
i != j);
}

public int degree(int v) {


if(!rangeCheck(v))
throw new IllegalArgumentException();
return neighborsOf(v).size();
}

public set neighborsOf(int v){


Set adj = new SetAsLinkedList();
for (int u=0; u numberOfVertices(); u = u+1){
if (containsEdge(u,v))
adj.add(u);
}
return adj;
}

public String toString(){


return "G= ([" + nVertices + "], " + edgeSet() + ")";
}

public boolean equals(Object other){


if (!(other instanceof Graph))
return false;
Graph otherGraph = (Graph)other;
return (this.numberOfVertices() == otherGraph. numberOfVertices() &&
(this.edgeSet().equals(otherGrph.edgeSet());
}

public abstract boolean containsEdge(int i, int j);


public abstract void addEdge(int i, int j) ;
public abstract void removeEdge(int i, int j) ;

78
:‫ייצוג גרף‬/‫שתי שיטות נפוצות למימוש‬
‫ – סמוך‬adjacent**
‫ – מטריצת שכנויות‬AdjacencyMatrix .1
.‫ מסמן כי לא קיימת קשת‬F ,‫ מסמן כי קיימת קשת‬T .‫ייצוג במטריצה סימטרית‬
0 1 2 3
0 F F F T
1 F F T T
2 F T F T
3 T T T F

‫ – רשימת שכנויות‬AdjacencyList .2
.‫ייצוג ברשימה שבה כל חוליה מפנה לרשימת השכנים שלה‬

public class GraphAsAdjacencyMatrix extends AbstractGraph)

// fields
private boolean[][] edges ;

// constructors
public GraphAsAdjacencyMatrix(int nVertices) {
super (nVertices);
edges = new boolean[nVertices][nVertices];
for (int i=0; i<nVertices; i=i+1)
for (int j=0; j<nVertices; j=j+1)
edges[i][j] = false;
}

public GraphAsAdjacencyMatrix(Graph other){ //‫בנאי מעתיק‬


this(other.numberOfVertices());
for (int i=0; i<numberOfVertices; i=i+1){
for (int j=i+1; j< numberOfVertices; j=j+1)
if(other.containsEdge(i,j)
addEdge(i,j);
}

//methods
public boolean containsEdge(int i, int j){
if (!rangeCheck(i,j){
throw new IllegalArguementException();
}
return edges[i][j]
}

public void addEdge(int i, int j){


if (!rangeCheck(i,j){
throw new IllegalArguementException();
}
edges[i][j] = true;
edges[j][i] = true;
}

public void removeEdge(int i, int j){


if (!rangeCheck(i,j){
throw new IllegalArguementException();
}
edges[i][j] = false;
edges[j][i] = false;
}

79
GraphAlgorithm – ‫אלגוריתמים הפועלים על גרפים‬
.‫ המרכזת אלגוריתמים הפועלים על גרפים‬,GraphAlgorithm ,‫נגדיר מחלקה אבסטרקטית נוספת‬
public abstract GraphAlgorithm {

//field
protected Graph input;

//constructors
public GraphAlgorithm(Graph input){
this.input = input;
}

//methods
public abstract Object runAlgorithm();

public class EvenDegreesAlgorithm extends GraphAlgorithm{

public EvenDegreesAlgorithm(Graph input) {


super(input);
}

public Object runAlgorithm() {


for(int i = 0; i < input.numberOfVertices(); i = i+1)
if(input.degree(i) % 2 != 0)
return false ;
return true ;
}
}

public class ConnectivityAlgorithm extends GraphAlgorithm{

public ConnectivityAlgorithm(Graph input) {


super(input);
}

public Object runAlgorithm() {


int nVertices = input.numberOfVerictes;
boolean[] visited = new boolean[nVertices]
for(int i = 0; i < nVertices (); i = i+1)
visited[i] = false;
dfsvisit(0, visited);
return reachedAll(visited;
}

public void dfsVisit(int i, boolean[] visited){


visited[i] = true;
for (integer j : input.neighborsOf(i))
if (!visited[j])
dfsVisit(j,visited)
}

public boolean reachedAll(boolean[] visited){


for (int i=0; i<visited.size(); i=i+1){
if (!visited[i])
return false;
return true;
}

80
3.1.18

23 ‫הרצאה‬
‫המשך גרפים‬
‫בעיית קיום מעגל אוילר‬
.‫ נרצה לדעת האם הגרף אוילריאני‬,‫בהינתן גרף‬
‫ גרף נקרא אוילריאני אם קיים בו מעגל שעובר בכל קודקודיו ומשתמש בכל קשת‬:‫הגדרה‬
.Euler ‫ מעגל כזה נקרא מעגל‬.‫בדיוק פעם אחת‬
C = <0, 1, 2, 3, 1, 7, 4, 6, 5, 4, 0> :‫הגרף בדוגמה הוא אולריאני‬
:‫משפט‬
.‫ גרף‬G ‫יהי‬
.‫ זוגיות‬G ‫ קשיר וגם כל דרגות קודקודי‬G ‫ קיים מעגל אוילר אם ורק אם‬G-‫ב‬

public class HasEulerianCircuitAlgorithm extends GraphAlgorithm{

private GraphAlgorithm eda;


private GraphAlgorithm ca;

public HasEulerianCircuitAlgorithm (Graph input) {


super(input);
eda = new EvenDegreesAlgorithm(input);
ca = new ConnectivityAlgorithm(input);
}

public Object runAlgorithm() {


return ((Boolean)eda.runAlgorithm()) && ((Boolean)eda.runAlgorithm());
}
}

81
‫מציאת מעגל אוילר‬
‫ נרצה למצוא אותו‬,‫אם בגרף קיים מעגל אוילר‬
public class FindEulerianAlgorithm extends GraphAlgorithm{

private GraphAlgorithm heca;

public FindEuleriaAlgorithm (Graph input) {


super(input);
heca = new HasEulerianCircuitAlgorithm (input);
}

public Object runAlgorithm() {


if (!((Boolean)heca.runAlgorithm()))
return null;
if (input.numberOfVerictes() == 0)
return new LinkedList<>();
if (input.numberOfVerictes() == 1){
List<Integer> circuit = new LinkedList<>();
circuit.add(0);
return circuit;
}
return findEulerianCirctuit();
}

private List<Integer> findEulerianCircuit(){


Graph copy = new GraphAsAdjacencyMatrix(input.numberOfVerictes()); //‫התשובה‬
List<Integer> circuit = new LinkedList<>();
int curr = 0; //‫תחילת המסלול‬
Stack<Integer> st = StackAsDynamicArray<>(); //‫שומרת את צעד המסלול‬
boolean init = true; //‫מאפשר כניסה ראשונה ללולאה‬
while (!st.isEmpty() | init){
init = false;
Set<Integer> adj = copy.neighborsOf(curr);
if (adj.size()>0){
next = adj.iterator().next();
st.push(curr);
copy.removeEdge(curr,next);
curr = next;
}
else{
circuit.add(curr);
curr.st.pop();
}
}
}

82
‫‪6.1.18‬‬

‫הרצאה ‪24‬‬
‫מיון רקורסיבי‪memoization ,‬‬
‫מיון מיזוג‪merge-sort ,‬‬
‫סוג המיון היעיל ביותר הידוע עד היום‪ ,‬זמן הריצה שלו ‪n∙log n‬‬
‫אלגוריתם רקורסיבי שיש בו חישובים חוזרים של אותן תתי בעיות‪.‬‬
‫רעיון האלגוריתם – גישת "הפרד ומשול"‬

‫לשם מיון זה נזדקק ל‪ 4-‬פרוצדורות‪:‬‬


‫)‪int[] sort(int[] arr‬‬
‫)‪int[] splitLeft(int[] arr‬‬
‫)‪int[] splitRight(int[] arr‬‬
‫)‪int[] merge(int[] arr1, int[] arr2‬‬

‫‪83‬‬
public static int[] sort (int[] arr){
if (arr == null)
throw new IllegalArgumentException();
if (arr.length < 2)
return arr;
return merge(sort(splitLeft(arr)), sort(splitRight(arr)));
/* ‫מימוש חלופי‬
int[] arr1 = splitLeft(arr);
int[] arr2 = splitRight(arr);
arr1 = sort(arr1);
arr2 = sort(arr2);
return merge(arr1, arr2);
*/
}

private static int[] splitLeft (int[] arr){


int[] output = new int[arr.length/2];
for (int i=0; i<output.length; i = i+1)
output[i] = arr[i];
return output;
}

private static int[] splitRight (int[] arr){


int[] output = new int[arr.length – (arr.length/2)];
for (int i= arr.length/2; i<arr.length; i = i+1)
output[i-(arr.length/2)] = arr[i];
return output;
}

private static int[] merge (int[] arr1, int[] arr2){


int ind = 0, i1 = 0, i2 = 0;
int len1 = arr1.length, len2 = arr2.length;
int[] output = new int[len1+len2];
while(i1 < len1 & i2 < len2) {
if (arr1[i1] < arr2[i2]){
output[ind] = arr1[i1];
i1 = i1+1;
}
else{
output[ind] = arr2[i2];
i2 = i2+1;
}
ind = ind+1;
}
for(int i = i1; i < len1; i = i+1){
output[ind] = arr1[i];
ind = ind+1;
}
for(int i = i2; i < len2; i = i+1){
output[ind] = arr2[i];
ind = ind+1;
}
return output;
}

84
memoization
.‫ היא שיטה המאפשרת למנוע חישובים חוזרים של אותן תתי הבעיות‬Memoization
‫ להפוך את‬,‫ כלומר‬.‫שיטה זו יכולה "להפוך" אלגוריתם רקורסיבי אקספוננציאלי (מעריכי) לאלגוריתם פולינומי‬
.‫האלגוריתם ליעיל יותר‬

‫סדרת פיבונאצ'י‬
:‫ נשכתב את האלגוריתם על מנת ליעל אותו‬.)10 ‫נזכר באלגוריתם לחישוב סדרת פיבונאצ'י (הרצאה‬
public static int fib (int n){
int[] table = new int[n+1];
for(int i = 0; i < table.length; i = i+1){
table[i] = -1;
}
return fib (table, n);
}

private int fib (int[] table, int n){


if (n==0 | n==1)
return 1;
if (table[n] == -1)
table[n] = fib(table, n-1) + fib(table, n-1);
}
return fib (table, n);
}

‫חישוב מספר הצרופים‬


:‫ נשכתב את האלגוריתם על מנת ליעל אותו‬.)11 ‫נזכר באלגוריתם לחישוב מספר הצרופים(הרצאה‬
// assume k>=0, n>=0
public static int choice (int n, int k){
int[][] table = new int[n+1][k+1];
for (int i = 0; i < table.length; i = i+1)}
for (int j = 0; j <table[i].length; j = j+1)
table[i][j] = -1;
{
return choice(table, n, k);
{

// assume k>=0, n>=0


public static int choice(int[][] table, int n, int k) {
if (n<k)
return 0;
else if (n == k | k == 0)
return 1;
else if (table[n][k] == -1){
//without n and with n
table[n][k] = choice(table, n-1, k) + choice(table, n-1, k-1);

return table[n][k];
{

85
10.1.19

25 ‫הרצאה‬
memoization ‫המשך‬
EditDistance – 1 ‫בעיה‬
.Y-‫ ל‬X ‫ הוא המספר המינימלי של פעולות(מותרות) אשר הופכות את‬d(X,Y) ‫ המרחק‬,X,Y ‫בהינתן שתי מילים‬
:‫הפעולות המותרות הן‬
insertion ‫ הכנסת אות‬-
deletion ‫ מחיקת אות‬-
substitution ‫ החלפת אות‬-
:‫דוגמה‬
d("kitten","sitting")=3

‫רעיון הרקורסיה‬
:‫ כך‬X,Y ‫נסמן את המילים‬

public static int distance (String source, String target){


int[][] table = new int[source.length()+1][target.length()+1];
for (int i = 0; i < table.length; i = i+1)
for (int j = 0; j < table[i].length; j = j+1)
table[i][j] = -1;
return distance(table, source, target);
}
private static int distance (int[][] table, String source, String target){
if (source.length() == 0)
return target.length();
if (target.length() == 0)
return source.length();
if (table[source.length()][target.length()] == -1){
if (source.charAt(0) == target.charAt(0))
return distance(source.substring(1),target.substring(1));
else{
int sub = distance(source.substring(1), target.substring(1));
int del = distance(source.substring(1), target);
int ins = distance(source, target.substring(1));
table[source.length()][target.length()] =
1 + Math.min(Math.min(sub, del), ins);
}
}
return table[source.length()][target.length()];
}

86
‫דוגמת ריצה‪:‬‬

‫בעיה ‪ – MaxUse – 2‬ניצול אולם מקסימלי‬


‫קלט‪ :‬אולם הרצאות ואוסף של ‪ n‬הרצאות (קטעים פתוחים‪/‬אינטרוולים) – להן זמן התחלה וסיום‬
‫נאמר כי שתי הרצאות נחתכות אם יש להן נקודת זמן משותפת‪.‬‬ ‫‪-‬‬
‫נגדיר שיבוץ הרצאות חוקי כתת קבוצות של הרצאות הקלט שאינן נחתכות זו עם זו‪.‬‬ ‫‪-‬‬
‫משך זמן של שיבוץ כזה הינו סך אורכי ההרצאות‬ ‫‪-‬‬
‫פלט‪ :‬משך זמן מקסימלי של שיבוץ חוקי‬

‫נייצג הרצאה‪/‬קטע פתוח כ‪ ,Interval-‬עצם בעל השיטות הבאות‪:‬‬


‫)(‪int getStart‬‬
‫)(‪int getValue‬‬
‫)‪boolean intersects(Interval‬‬
‫)(‪int length‬‬

‫שיבוצים חוקיים לדוגמה‪:‬‬


‫}‪{2,3‬‬
‫}‪{1,4‬‬
‫}‪{2,4‬‬
‫{‪ – }1,5‬אופטימלי‬

‫‪87‬‬
‫רעיון הרקורסיה‬
:‫ אינטרוולים‬n ‫בהינתן‬
0 ‫ התשובה היא‬,n==0 ‫אם‬ -
X-‫ נסמן את האינטרוול האחרון ב‬,‫אחרת‬ -
with -‫ – נסמן אותו ב‬X ‫ נבדוק את משך הזמן המקסימלי של שיבוץ חוקי שמכיל את‬o
without -‫ – נסמן אותו ב‬X ‫ נבדוק את משך הזמן המקסימלי של שיבוץ חוקי שלא מכיל את‬o
:‫*בדוגמה שלנו‬
with = length of 5 + MaxUse({1})
without = MaxUse({1,2,3,4})
?X ‫כיצד נמצא בצורה יעילה את הקטעים שלא נחתכים עם‬
.‫נמיין את המקטעים לפי זמן הסיום שלהם ולמספרם לפי סדר המיון‬
‫ ולעצור ברגע‬, X ‫ ומטה ולכל מקטע לבדוק האם הוא מתנגש עם‬X-1 -‫כעת ניתן לרוץ על כל המקטעים החל מ‬
.‫שמוצאים את המקטע הראשון שלא מתנגש‬
)‫ האינטרוולים הראשונים במיון (השמאליים במערך‬i ‫ תכיל פתרון עבור‬table[i] :n+1 ‫ בגודל‬table ‫נשתמש בטבלה‬
//assumes segments is sorted
public static int maxUse (Interval[] segments){
int[] table = new int[source.length()+1][target.length()+1];
for (int i = 0; i < table.length; i = i+1)
table[i] = 0;
return maxUse(table, segments, segments.length-1);
}

private static int maxUse(table, segments, index){


//‫ ועד סוף הטבלה‬0 ‫מחזירה משך זמן מקסימלי של שיבוץ חוקי של אינטרוולים החל מתא‬
int output = 0;
if (index >= 0){
output = table[index];
if (output == 0){
int currentSegLen = segments[index].length();
int prevIndex = firstNonIntersecting(segments, index);
int with = maxUse(table, segments,prevIndex) + currentSegLen;
int without = maxUse(table, segments, index-1);
output = Math.max(with, without);
}
}
return output;
}

private static int firstNonIntersecting(Interval[] segments, int i){


Interval currentSeg = segments[i];
i = i - 1; // skip the current segment
while (i >= 0 && currentSeg.intersects(segments[i]))
i = i - 1;
return i;
}

88

You might also like