570 likes | 866 Views
תרגול 12:. ומחסנית Iterator רשימה מקושרת, תור,. רשימה מקושרת ( Linked List ). רשימה מקושרת הינה קבוצה סדורה של אובייקטים, כאשר כל אובייקט ברשימה מכיל הצבעה לאובייקט הבא. כל אובייקט מהווה חוליה בשרשרת: - קודקוד ברשימה הינו מסוג Link - כל הרשימה נקראת LinkedList. null. Data. Next. Link.
E N D
תרגול 12: • ומחסניתIteratorרשימה מקושרת, תור,
רשימה מקושרת (Linked List) רשימה מקושרת הינה קבוצה סדורה של אובייקטים, כאשר כל אובייקט ברשימה מכיל הצבעה לאובייקט הבא. כל אובייקט מהווה חוליה בשרשרת: - קודקוד ברשימה הינו מסוג Link - כל הרשימה נקראת LinkedList null Data Next Link
Data Next Link מחלקה שמייצגת חוליה Link publicclass 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 Object getData() { returndata; } publicvoid setData(Object data) { this.data = data; } public Link getNext() { returnnext; } publicvoid setNext(Link next) { this.next = next; } }
מחלקה שמייצגת רשימה משורשרת LinkedList publicclass LinkedList { private Link first; public LinkedList (){ first = null; } public LinkedList (Object data){ first = new Link(data, null); } ... }
מחלקה שמייצגת רשימה משורשרת LinkedList LinkedList first data next null data data next next 5
נוסיף למחלקה את השיטה: נרצה לדעת האם הרשימה שלנו מכילה איברים או לא? אז נוסיף את השיטה הבוליאניתisEmpty() אשר תחזיר true אם הרשימה ריקה, ו-false אחרת. publicboolean isEmpty() { return (first == null); }
נוסיף למחלקה את השיטה: publicvoid addLast(Object data) { Link newLink = new Link(data, null); if (isEmpty()) { first = newLink; } else { Link linkPointer = first; while (linkPointer.getNext() != null) { linkPointer = linkPointer.getNext(); } linkPointer.setNext(newLink); } }
דוגמת שימוש LinkedList lst = new LinkedList(); lst.addLast( "World!"); lst.addLast( "Hello" ); lst.addLast( "Great" ); next data data data next next LinkedList first null null
נוסיף גם למחלקה את השיטה: נוסיף שיטה אשר מסירה את החוליה הראשונה ברשימה ומחזירה את האובייקט המוצבע על ידה. public Object removeFirst(){ Object ans; if (isEmpty()) { ans = null; } else { ans = first.getData(); first = first.getNext(); } return ans; }
המשך דוגמת שימוש lst.removeFirst(); lst.removeFirst(); lst.removeFirst(); next data data data next next LinkedList null first "Hello" "Great" "World!"
המשך דוגמת שימוש lst.removeFirst(); lst.removeFirst(); lst.removeFirst(); next data data next LinkedList null first "Great" "World!"
המשך דוגמת שימוש lst.removeFirst(); lst.removeFirst(); lst.removeFirst(); next data LinkedList null first "World!"
המשך דוגמת שימוש lst.removeFirst(); lst.removeFirst(); lst.removeFirst(); LinkedList null first
נוסיף למחלקה LinkedList את השיטהgetMiddle : • getMiddle • - מהו האיבר האמצעי ברשימה? • - רעיונות לפתרון?
getMiddle public Object getMiddle(){ Object ans; if(isEmpty()) { ans = null; } else { Link current = first; Link jumper = first; while( (jumper != null) && (jumper.getNext() != null) ){ current = current.getNext(); jumper = jumper.getNext().getNext(); } ans = current.getData(); } return ans; } &&
… while( (jumper != null) && (jumper.getNext() != null) ){ current = current.getNext(); jumper = jumper.getNext().getNext(); } ans = current.getData(); …
נוסיף למחלקה LinkedList את השיטהcontainsCircles : 2. containsCircles – האם יש מעגלים ברשימה? - האם עלולים להיווצר מעגלים שכאלה? - חידה: כמה מעגלים לכל היותר יכולים להיות ברשימה מקושרת? 17
publicboolean containsCircles() { boolean ans; if(isEmpty()) { ans = false; } else { Link current = first; Link jumper = first; boolean start = true; while(jumper != null && ( current != jumper || start) ){ start = false; current = current.getNext(); jumper = jumper.getNext(); if (jumper != null) jumper = jumper.getNext(); } ans = (jumper != null); } return ans; }
while(jumper != null && ( current != jumper || start) ){ start = false; current = current.getNext(); jumper = jumper.getNext(); if (jumper != null) jumper = jumper.getNext(); }
while(jumper != null && ( current != jumper || start) ){ start = false; current = current.getNext(); jumper = jumper.getNext(); if (jumper != null) jumper = jumper.getNext(); }
while(jumper != null && ( current != jumper || start) ){ start = false; current = current.getNext(); jumper = jumper.getNext(); if (jumper != null) jumper = jumper.getNext(); }
while(jumper != null && ( current != jumper || start) ){ start = false; current = current.getNext(); jumper = jumper.getNext(); if (jumper != null) jumper = jumper.getNext(); }
while(jumper != null && ( current != jumper || start) ){ start = false; current = current.getNext(); jumper = jumper.getNext(); if (jumper != null) jumper = jumper.getNext(); }
while(jumper != null && ( current != jumper || start) ){ start = false; current = current.getNext(); jumper = jumper.getNext(); if (jumper != null) jumper = jumper.getNext(); }
Iterator • מידע ונתונים (data) הדרושים לתכנית מחשב נשמרים בתוך מבנה נתונים (data structure). • על מבנה נתונים יש צורך לבצע מספר פעולות, כמו הכנסת נתונים והוצאת נתונים. • אחת הדרכים להוציא נתונים היא לעבור על אוסף הנתונים פריט-מידע אחר פריט-מידע.
Iterator • נרצה שתכונה זו תהיה משותפת למבני נתונים רבים, למשל לרשימה, שראינו זה עתה. • לכן נגדיר ממשק, שאותו כל מבנה נתונים יממש. • נרצה להגיד שמבנה נתונים הוא ניתן למעבר פריט-אחר-פריט, ובאנגלית: Iterable. ב-JAVA קיים הממשק: publicinterface Iterable { /** *Returnsaniteratoroverasetofelements. * *@returnanIterator. */ Iterator iterator(); }
Iterator • יהיה לנו אובייקט שיעזור בתהליך. אובייקט זה יעבור על אוסף הנתונים פריט אחר פריט, לפי סדר מסוים, ובאנגלית:Iterator . ב-JAVA מוגדר ממשק לעבודה עם אובייקט כזה:
publicinterface Iterator { /** *Returnstrueiftheiterationhasmoreelements. *@returntrueiftheiteratorhasmoreelements. */ boolean hasNext(); /** *Returnsthenextelementintheiteration. *@returnthenextelementintheiteration. * @exception NoSuchElementException iteration has no more elements. */ Object next(); /** *Removesfromtheunderlyingcollectionthelastelementreturnedbythe *iterator(optionaloperation) *@exceptionUnsupportedOperationExceptionifthe"remove" *operationisnotsupportedbythisIterator. * *@exceptionIllegalStateExceptionifthe"next"methodhasnot *yetbeencalled,orthe"remove"methodhasalready *beencalledafterthelastcalltothe"next" *method. */ void remove(); }
שימוש ב-Iterator בהמשך נראה כיצד להפוך את המחלקה LinkedList כך שתתמוך ב-Iterator, כרגע נניח שהמחלקה כבר תומכת בו, ועל סמך זאת נעשה שימוש ב-Iterator שמתקבל מהשיטה iterator(), שמובטחת לנו מכיוון ש-LinkedList מממשת את Iterable. publicstaticvoid main(String[] args) { LinkedList lst = new LinkedList(); lst.addLast("Shnitsels"); lst.addLast(“Are"); lst.addLast("Tasty"); Iterator it = lst.iterator(); while (it.hasNext()) { Object currentData = it.next(); System.out.print(currentData); if (it.hasNext()) System.out.print(", "); } System.out.println(); }
דוגמת שימוש while (it.hasNext()) { Object currentData = it.next(); System.out.print(currentData); if (it.hasNext()) System.out.print(", "); } System.out.println(); data data next next LinkedList ListIterator Shnitsels, Are, Tasty currentLink first data next null
מימוש ה-Iterator עבור LinkedList publicclass ListIterator implements Iterator { private Link currentLink; public ListIterator(Link first) { currentLink = first; } publicboolean hasNext() { return ( currentLink != null ); } public Object next() { if (!hasNext()) { thrownew NoSuchElementException(); } Object data = currentLink.getData(); currentLink = currentLink.getNext(); return data; } publicvoid remove() { thrownewUnsupportedOperationException(); } }
מימוש ה-Iterable במחלקה LinkedList עתה נראה כיצד נתמוך ב Iterator במחלקה LinkedList: publicinterface Iterable { /** *Returnsaniteratoroverasetofelements. * *@returnanIterator. */ Iterator iterator(); }
מימוש ה-Iterable במחלקה LinkedList • עכשיו כל מה שנותר לעשות הוא לממש את השיטה iterator() של הממשק Iterable ב LinkedList: publicclass LinkedList implements Iterable{ private Link first; public LinkedList (){ first = null; } … // All methods remain the same public Iterator iterator(){ returnnew ListIterator(first); } }
הערות • Iterator מניח שלא נעשה שינוי באוסף עליו הוא עובר במהלך המעבר. אם נעשה שינוי – האיטרטור איננו במצב חוקי ואין הבטחה לפעולה תקינה שלו. • השיטה ()next מחוייבת לזרוק את החריגה NoSuchElementException במידה ואין יותר אלמנטים באוסף. ( אם לפני כל קריאה ל-next() נוודא ש hasNext() החזיר true אז לא נתקל בחריגה זו). • החריגה: UnsupportedOperationException נזרקת כאשר אנו מחוייבים להכריז שיטה מסויימת (למשל כי היא חלק מממשק) במחלקה שלנו, אך אנו לא תומכים בשיטה זו. לכן בגוף השיטה נזרוק את החריגה.
תור - Queue • תור (Queue) הוא מבנה נתונים המזכיר תור של בני אדם: האיבר שנכנס ראשון לתור- יוצא ממנו ראשון. • ניתן לראות כקופסה סגורה בעלת 2 פתחים: פתח הכנסה ופתח יציאה. • איבר שנכנס ראשון יוצא ראשון FIFO (FIFO - First In First Out). • שימושים: ניהל תהליכים ע"י מערכת ההפעלה, ניהול תור בבית מרקחת, ...
public interface Queue{ /** *isEmpty-checksifthequeueisemptyornot. *@returntrueifthequeueisempty */ publicboolean isEmpty(); /** *dequeue-removesanobjectfromtheheadofthequeue. * (FIFOorder) *@returnthenextobjectinthequeue. */ public Object dequeue(); /** *enqueue-insertsanobjectintothequeue. *@paramotheobjecttobeenqueued. */ publicvoid enqueue(Object o); }
מימוש תור ע"י רשימה מקושרת publicclass QueueAsList implements Queue { private LinkedList lst; public QueueAsList() { lst = new LinkedList(); } publicboolean isEmpty() { return lst.isEmpty(); } public Object dequeue() { return lst.removeFirst(); } publicvoid enqueue(Object o) { lst.addLast(o); } }
דוגמה נרצה לקלוט מהמשתמש מספר מחרוזות ורק כאשר הוא יסיים להקליד (למשל ע"י זיהוי שורה ריקה) נדפיס בחזרה את כל המחרוזות בדיוק לפי סדר ההכנסה.
המשך import java.util.Scanner; … publicstaticvoid main(String args[]) { Scanner sc = new Scanner(System.in); Queue q = new QueueAsList(); System.out.println("Insert few lines … "); while (sc.hasNextLine()) { String line = sc.nextLine(); q.enqueue( line ); } System.out.println("Printing all the lines back! "); while (!q.isEmpty()) { System.out.println(q.dequeue()); } }
דוגמה נוספת "'WIMBELDON" : 41 רוצים לבצע טורניר טניס
המשך 42 נרצה לממש את השיטה: publicstatic Player simulateTournament(LinkedList playersList) אשר מקבלת רשימה של שחקנים ומבצעת סימולציה של טורניר טניס באופן הבא: * בשלב הראשון השחקנים מתחלקים לזוגות. * מכל זוג עולה המנצח לשלב הבא ושוב השחקנים מתחלקים לזוגות עד שנותר שחקן בודד שהוא המנצח (טורניר נוק אוט).
המשך 43 נממש את השיטה ע"י שימוש בתור – כאשר נאתחל אותו באברי הרשימה, וכל עוד התור מכיל יותר מאיבר אחד נשלוף שני שחקנים מהתור, "ניתן" להם לשחק ואת המנצח נכניס לסוף התור (עבר לסיבוב הבא).
המשך שחקן 4 שחקן 4 שחקן 3 שחקן 2 שחקן 1 שלב 1: שחקן 1 שחקן 3 שחקן 2 שחקן 4 44
המשך שחקן 2 שלב 2: שחקן 4 שחקן 4 שחקן 3 שחקן 2 שחקן 1 שלב 1: שחקן 3 שחקן 4 שחקן 2 45
המשך שחקן 2 שחקן 4 שחקן 4 שלב 2: שחקן 4 שחקן 4 שחקן 3 שחקן 2 שחקן 1 שלב 1: שחקן 2 שחקן 4 46
המשך שלב 3 (המנצח): שחקן 2 שחקן 2 שחקן 4 שחקן 4 שלב 2: שחקן 4 שחקן 4 שחקן 3 שחקן 2 שחקן 1 שלב 1: שחקן 2 47
publicstatic Player simulateTournament( LinkedList playersList) { Queue q = new QueueAsList(); Iterator it = playersList.iterator(); Player winner = null; while (it.hasNext()) { q.enqueue( it.next() ); } while ( ! q.isEmpty() ) { Player first = (Player)q.dequeue(); if (q.isEmpty()) { winner = first; } else { Player second = (Player)q.dequeue(); Player matchWinner = getWinner(first,second); q.enqueue( matchWinner ); } } return winner; } 48
מחסנית (stack) • מחסנית (stack) היא מבנה נתונים שמזכיר מחסנית של רובה: האיבר שנכנס ראשון למחסנית יוצא ממנה אחרון (LIFO - Last In First Out). • ניתן לראות במחסנית קופסה סגורה בעלת פתח יחיד, שדרכו נעשות פעולות הדחיפה והשליפה. • הטיפול דרך פתח יחיד יוצר מצב שבו איבר שנכנס אחרון יוצא ראשון- LIFO . • מחסנית היא שימושית כאשר יש צורך לשמור נתונים בסדר מסוים ולשלוף אותם בסדר הפוך.
ממשק publicinterface Stack { /** *push-addsanelementtothestack. *@paramotheelementedtobeinsertedtothestack. */ publicvoid push (Object o); /** *pop-removesanelementformthestack(LIFOorder). *@returntheelementfromthetopofthestack. */ public Object pop (); /** *isEmpty-checksifthestackisemptyornot. *@returntrueifthereisnomoreelementsinthestack. */ publicboolean isEmpty(); }