570 likes | 587 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 עתה נראה כיצד נתמוך ב Iterator במחלקה LinkedList: • ראשית נגדיר את המחלקה ListIterator שמממשת את הממשק Iterator:
מימוש ה-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() של הממשק 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 נזרקת כאשר אנו מחוייבים להכריז שיטה מסויימת (למשל כי היא חלק מממשק) במחלקה שלנו, אך אנו לא תומכים בשיטה זו. לכן בגוף השיטה נזרוק את החריגה.
השיטה filter • לסינון איברים העונים על תנאי מתוך כלל איברי הרשימה. • עתה נתכנן שיטה לסינון איברי הרשימה. • נשאיר רק איברים העונים על תנאי כלשהו. • אך מהו התנאי? • קשה לקבוע תנאי שכן רשימה היא מבנה נתונים כללי היכול להכיל איברים מסוגים שונים.
השיטה filter • לסינון איברים העונים על תנאי מתוך כלל איברי הרשימה. • אז מה נעשה? • נגדיר ממשק Filter המכיל שיטה אחת והיא accept, המקבלת אובייקט ומחליטה האם הוא עובר את הסינון או לא. • ראשית נגדיר את הממשק Filter:
publicinterface Filter { • /** • *accept–definesthefiltrationcriterion. • *@paramotheobjecttobeexamined. • *@returntrueiftheobjectshould not be * filtered out. • */ • publicboolean accept(Object o); • } • אנו נממש Filter פשוט עבור המחלקה String, המשאיר רק את המחרוזות הלא ריקות: • publicclass EmptyStringFilter implements Filter { • publicboolean accept(Object o) { • boolean ans = false; • if (o instanceof String) { • String s = (String) o; • ans = s.length() > 0; • } • return ans; • } • }
היינו רוצים לממש שיטה במחלקה LinkedList, המקבלת מסנן ומחזירה רשימה מסוננת. כך שנוכל להשתמש בה באופן הבא: LinkedList lst = new LinkedList(); lst.addLast( "Hello" ); lst.addLast( "" ); lst.addLast( "" ); lst.addLast( "Great" ); lst.addLast( "" ); lst.addLast( "World" ); LinkedList ans = lst.filter(new EmptyStringFilter()); //ans = ["Hello" , "Great" , "World" ]
כל שנותר הוא לממש את אלגוריתם הסינון ב-LinkedList: האלגוריתם מקבל כקלט מסנן, ומחזיר רשימה חדשה שתכיל את כל אברי הרשימה הנוכחית שעברו את המסנן. public LinkedList filter(Filter filter) { LinkedList filteredList = new LinkedList() ; Iterator it = this.iterator(); while (it.hasNext()) { Object data = it.next(); if(filter.accept(data)) { filteredList.addLast(data) ; } } return filteredList; }
LinkedList lst = new LinkedList(); lst.addLast( "Hello" ); lst.addLast( "" ); lst.addLast( "" ); lst.addLast( "Great" ); lst.addLast( "" ); lst.addLast( "World" ); LinkedList ans = lst.filter(new EmptyStringFilter()); //ans = ["Hello" , "Great" , "World" ]
תור - 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" : 47 רוצים לבצע טורניר טניס
המשך 48 נרצה לממש את השיטה: publicstatic Player simulateTournament(LinkedList playersList) אשר מקבלת רשימה של שחקנים ומבצעת סימולציה של טורניר טניס באופן הבא: * בשלב הראשון השחקנים מתחלקים לזוגות. * מכל זוג עולה המנצח לשלב הבא ושוב השחקנים מתחלקים לזוגות עד שנותר שחקן בודד שהוא המנצח (טורניר נוק אוט).
המשך 49 נממש את השיטה ע"י שימוש בתור – כאשר נאתחל אותו באברי הרשימה, וכל עוד התור מכיל יותר מאיבר אחד נשלוף שני שחקנים מהתור, "ניתן" להם לשחק ואת המנצח נכניס לסוף התור (עבר לסיבוב הבא).
המשך שחקן 4 שחקן 4 שחקן 3 שחקן 2 שחקן 1 שלב 1: שחקן 1 שחקן 3 שחקן 2 שחקן 4 50