null LinkedList

‫תרגול ‪12‬‬
‫מבני נתונים‬
‫‪1‬‬
‫היום בתרגול‬
‫רשימה מקושרת‬
‫תור‬
‫‪Iterator‬‬
‫מחסנית‬
‫‪2‬‬
‫רשימה מקושרת (‪)Linked List‬‬
‫רשימה מקושרת הינה קבוצה סדורה של אובייקטים‪ ,‬כאשר כל‬
‫אובייקט ברשימה מכיל הצבעה לאובייקט הבא‪ .‬כל אובייקט מהווה‬
‫חוליה בשרשרת‪:‬‬
‫ קודקוד ברשימה הינו מסוג ‪Link‬‬‫‪ -‬הרשימה כולה נקראת ‪LinkedList‬‬
‫‪null‬‬
‫‪Next‬‬
‫‪Data‬‬
‫‪Link‬‬
‫‪3‬‬
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 Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Link getNext() {
return next;
}
public void setNext(Link next) {
this.next = next;
}
}
4
Link ‫המחלקה‬
Link
Data
Next
LinkedList ‫המחלקה‬
public class LinkedList {
private Link first;
public LinkedList (){
first = null;
}
public LinkedList (Object data){
first = new Link(data, null);
}
...
}
5
LinkedList ‫המחלקה‬
LinkedList
first
data
6
next
data next
data
next
null
‫נוסיף למחלקה את השיטה)(‪isEmpty‬‬
‫נרצה לדעת האם הרשימה שלנו מכילה איברים או לא‪.‬‬
‫נוסיף את השיטה הבוליאנית )(‪ isEmpty‬אשר תחזיר ‪ true‬אם‬
‫הרשימה ריקה‪ ,‬ו‪ false-‬אחרת‪.‬‬
‫{ )(‪public boolean isEmpty‬‬
‫;)‪return (first == null‬‬
‫}‬
‫‪7‬‬
addLast ‫נוסיף למחלקה את השיטה‬
public void 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);
}
}
8
‫דוגמת שימוש‬
LinkedList lst = new LinkedList();
lst.addLast("Hello");
lst.addLast("Beautiful");
lst.addLast("World!");
LinkedList
first
null
data next
9
data
next
data
next
null
‫נוסיף למחלקה גם את השיטה‬
removeFirst
‫השיטה מסירה את החוליה הראשונה ברשימה ומחזירה את האובייקט‬
.‫המוצבע על ידה‬
public Object removeFirst(){
Object ans;
if (isEmpty()) {
ans = null;
}
else {
ans = first.getData();
first = first.getNext();
}
return ans;
}
10
‫המשך דוגמת שימוש‬
lst.removeFirst();
lst.removeFirst();
lst.removeFirst();
LinkedList
first
data next
"Hello"
11
data
next
"Beautiful"
data
next
"World!"
null
‫המשך דוגמת שימוש‬
lst.removeFirst();
lst.removeFirst();
lst.removeFirst();
LinkedList
first
data
next
“Beautiful"
12
data
next
"World!"
null
‫המשך דוגמת שימוש‬
lst.removeFirst();
lst.removeFirst();
lst.removeFirst();
LinkedList
first
data
next
"World!"
13
null
‫המשך דוגמת שימוש‬
lst.removeFirst();
lst.removeFirst();
lst.removeFirst();
LinkedList
first
null
14
‫נוסיף למחלקה את השיטה ‪getMiddle‬‬
‫השיטה ‪ getMiddle‬מחזירה את האיבר האמצעי‬
‫ברישמה‪.‬‬
‫‪ -‬רעיונות לפתרון?‬
‫‪15‬‬
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;
}
16
…
while(jumper != null && jumper.getNext() != null) {
current = current.getNext();
jumper = jumper.getNext().getNext();
}
ans = current.getData();
…
17
‫נוסיף את השיטה ‪containsCycle‬‬
‫השיטה ‪ containsCycle‬בודקת האם הרשימה מכילה‬
‫מעגל‪.‬‬
‫•‬
‫האם בכלל עלולים להיווצר מעגלים שכאלו?‬
‫•‬
‫חידה‪ :‬כמה מעגלים לכל היותר יכולים להיות ברשימה‬
‫מקושרת?‬
‫‪18‬‬
public boolean containsCycle(){
boolean ans;
if(isEmpty()) {
ans = false;
}
else {
Link current = first;
Link jumper = first;
boolean isStart = true;
while(jumper != null && (current != jumper || isStart)){
isStart = false;
current = current.getNext();
jumper = jumper.getNext();
if (jumper != null)
jumper = jumper.getNext();
}
ans = (jumper != null);
}
return ans;
}
19
while(jumper != null && (current != jumper || start)) {
start = false;
current = current.getNext();
jumper = jumper.getNext();
if (jumper != null)
jumper = jumper.getNext();
}
20
while(jumper != null && (current != jumper || start)) {
start = false;
current = current.getNext();
jumper = jumper.getNext();
if (jumper != null)
jumper = jumper.getNext();
}
21
while(jumper != null && (current != jumper || start)) {
start = false;
current = current.getNext();
jumper = jumper.getNext();
if (jumper != null)
jumper = jumper.getNext();
}
22
while(jumper != null && (current != jumper || start)) {
start = false;
current = current.getNext();
jumper = jumper.getNext();
if (jumper != null)
jumper = jumper.getNext();
}
23
while(jumper != null && (current != jumper || start)) {
start = false;
current = current.getNext();
jumper = jumper.getNext();
if (jumper != null)
jumper = jumper.getNext();
}
24
while(jumper != null && (current != jumper || start)) {
start = false;
current = current.getNext();
jumper = jumper.getNext();
if (jumper != null)
jumper = jumper.getNext();
}
25
Iterator
26
‫‪Iterator‬‬
‫מידע ונתונים (‪ )data‬הדרושים לתכנית מחשב‬
‫נשמרים בתוך מבנה נתונים (‪.)data structure‬‬
‫על מבנה נתונים יש צורך לבצע מספר פעולות‪ ,‬כמו‬
‫הכנסת נתונים והוצאת נתונים‪.‬‬
‫אחת הדרכים להוציא נתונים היא לעבור על אוסף‬
‫הנתונים פריט‪-‬מידע אחר פריט‪-‬מידע‪.‬‬
‫‪27‬‬
‫‪Iterator‬‬
‫נרצה שתכונה זו תהיה משותפת למבני נתונים רבים‪ ,‬למשל לרשימה‪,‬‬
‫שראינו זה עתה ‪.‬לכן נגדיר ממשק‪ ,‬שאותו כל מבנה נתונים יממש‪.‬‬
‫נרצה להגיד שמבנה נתונים הוא ניתן למעבר פריט‪-‬אחר‪-‬פריט‪,‬‬
‫ובאנגלית‪ .Iterable :‬ב‪ JAVA-‬קיים הממשק‪:‬‬
‫{ ‪public interface Iterable‬‬
‫**‪/‬‬
‫‪* Returns an iterator over a set of elements.‬‬
‫*‬
‫‪* @return an Iterator.‬‬
‫‪*/‬‬
‫;)(‪Iterator iterator‬‬
‫}‬
‫‪28‬‬
‫‪Iterator‬‬
‫יהיה לנו אובייקט שיעזור בתהליך‪ .‬אובייקט זה יעבור‬
‫על אוסף הנתונים פריט אחר פריט‪ ,‬לפי סדר מסוים‪,‬‬
‫ובאנגלית‪.Iterator :‬‬
‫נרצה להפריד בין אופן המעבר על הפריטים‪,‬‬
‫למחלקה עליה הוא פועל‪.‬‬
‫ב‪ JAVA-‬מוגדר ממשק לעבודה עם אובייקט כזה‪:‬‬
‫‪29‬‬
public interface Iterator {
/**
* Returns true if the iteration has more elements.
* @return true if the iterator has more elements.
*/
boolean hasNext();
/**
* Returns the next element in the iteration.
* @return the next element in the iteration.
* @exception NoSuchElementException iteration has no more elements.
*/
Object next();
/**
* Removes from the underlying collection the last element returned by the
* iterator (optional operation)
* @exception UnsupportedOperationException if the "remove"
*
operation is not supported by this Iterator.
*
* @exception IllegalStateException if the "next" method has not
*
yet been called, or the "remove" method has already
*
been called after the last call to the "next"
*
method.
*/
void remove();
}
30
Iterator-‫שימוש ב‬
.Iterator-‫ כך שתתמוך ב‬LinkedList ‫בהמשך נראה כיצד לשנות את המחלקה‬
Iterator-‫ ועל סמך זאת נעשה שימוש ב‬,‫כרגע נניח שהמחלקה כבר תומכת בו‬
‫ מממשת את‬LinkedList-‫ שמובטחת לנו מכיוון ש‬,iterator() ‫שמתקבל מהשיטה‬
.Iterable
public static void 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();
31
}
‫דוגמת שימוש‬
while (it.hasNext()) {
Object currentData = it.next();
System.out.print(currentData);
if (it.hasNext())
System.out.print(", ");
}
System.out.println();
LinkedList
ListIterator
Shnitsels, Are, Tasty
currentLink
first
data next
32
data
next
data
next
null
LinkedList ‫ עבור‬Iterator ‫מימוש‬
public class ListIterator implements Iterator {
private Link currentLink;
public ListIterator(Link first) {
currentLink = first;
}
public boolean hasNext() {
return currentLink != null;
}
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Object data = currentLink.getData();
currentLink = currentLink.getNext();
return data;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
33
LinkedList ‫ במחלקה‬Iterable ‫מימוש‬
‫ של הממשק‬iterator() ‫יש לממש את השיטה‬
LinkedList ‫ ב‬Iterable
public class LinkedList implements Iterable{
… // All methods and fields unchanged
public Iterator iterator(){
return new ListIterator(first);
}
…
}
34
‫‪ - Iterator‬הערות‬
‫• ‪ Iterator‬מניח שלא נעשה שינוי באוסף עליו הוא עובר במהלך‬
‫המעבר‪ .‬אם נעשה שינוי – האיטרטור אינו במצב חוקי ואין הבטחה‬
‫לפעולה תקינה שלו‪.‬‬
‫• השיטה ()‪ next‬מחויבת לזרוק את החריגה‬
‫‪ NoSuchElementException‬במידה ואין יותר אלמנטים‬
‫באוסף‪( .‬אם לפני כל קריאה ל‪ next()-‬נוודא ש )(‪hasNext‬‬
‫החזירה ‪ true‬אזי לא נתקל בחריגה זו)‪.‬‬
‫• החריגה ‪ UnsupportedOperationException‬משמשת‬
‫כאשר אנו מחויבים לספק מימוש של שיטה מסוימת (למשל כי היא‬
‫מוגדרת בממשק) במחלקה שלנו‪ ,‬אך אין ברצוננו‪/‬יכולתנו לתמוך‬
‫בה‪ .‬במקרה כזה בגוף השיטה נזרוק את החריגה‪.‬‬
‫‪35‬‬
‫תור ‪Queue -‬‬
‫תור )‪ (Queue‬הוא מבנה נתונים המזכיר תור של אנשים‪ :‬האיבר‬
‫שנכנס ראשון לתור‪ -‬יוצא ראשון‪.‬‬
‫ניתן להסתכל כעל כקופסה סגורה בעלת ‪ 2‬פתחים‪ -‬פתח הכנסה ופתח‬
‫הוצאה‪.‬‬
‫איבר שנכנס ראשון יוצא ראשון ‪.)FIFO - First In First Out( FIFO -‬‬
‫שימושים‪ :‬ניהל תהליכים ע"י מערכת ההפעלה‪ ,‬ניהול גישות למשאב‬
‫משותף‪... ,‬‬
‫‪36‬‬
public interface Queue{
/**
* isEmpty - checks if the queue is empty or not.
* @return true if the queue is empty
*/
public boolean isEmpty();
/**
* dequeue - removes an object from the head of the queue.
* (FIFO order)
* @return the next object in the queue.
*/
public Object dequeue();
/**
* enqueue - inserts an object into the queue.
* @param o the object to be enqueued.
*/
public void enqueue(Object o);
}
37
‫מימוש תור ע"י רשימה מקושרת‬
‫מימוש כזה יהיה פשוט מאוד באמצעות הרשימה המקושרת שהגדרנו‬
.‫בתחילת התרגול‬
public class QueueAsList implements Queue {
private LinkedList lst;
public QueueAsList() { lst = new LinkedList(); }
public boolean isEmpty() { return lst.isEmpty(); }
public Object dequeue() { return lst.removeFirst(); }
public void enqueue(Object o) { lst.addLast(o); }
}
38
‫דוגמה‬
‫נרצה לקלוט מהמשתמש מספר מחרוזות‪ ,‬ורק‬
‫כאשר הוא יסיים להקליד (למשל ע"י זיהוי שורה‬
‫ריקה) נדפיס בחזרה את כל המחרוזות בדיוק לפי‬
‫סדר ההכנסה‪.‬‬
‫‪39‬‬
import java.util.Scanner;
…
public static void 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());
}
}
40
‫דוגמה נוספת‪Wimbeldon -‬‬
‫נרצה לסמלץ (לדמות) מהלך של טורניר טניס‬
‫בשיטת פלייאוף‪.‬‬
‫‪41‬‬
‫המשך‬
‫נרצה לממש את השיטה‪:‬‬
‫(‪public static Player simulateTournament‬‬
‫)‪LinkedList playersList‬‬
‫אשר מקבלת רשימה של שחקנים ומבצעת סימולציה של טורניר טניס‬
‫באופן הבא‪:‬‬
‫• בשלב הראשון השחקנים מתחלקים לזוגות‪.‬‬
‫• מכל זוג המנצח עולה לשלב הבא‪ ,‬ושוב השחקנים מתחלקים לזוגות‬
‫עד שנותר שחקן בודד שהוא המנצח (טורניר נוק‪-‬אאוט)‪.‬‬
‫‪42‬‬
‫הרעיון‬
‫נממש את השיטה ע"י שימוש בתור‪:‬‬
‫• נאתחל את התור באיברי הרשימה‪.‬‬
‫• כל עוד התור מכיל יותר מאיבר אחד נשלוף שני שחקנים‬
‫מהתור‪.‬‬
‫• "ניתן" להם לשחק ביניהם‪ ,‬ואת המנצח נכניס לסוף התור‬
‫(עבר לסיבוב הבא)‪.‬‬
‫‪43‬‬
‫הרעיון‬
‫שלב ‪:1‬‬
‫שחקן ‪2‬‬
‫שחקן ‪1‬‬
‫שחקן ‪4‬‬
‫שחקן ‪3‬‬
‫שחקן ‪4‬‬
‫שחקן ‪4‬‬
‫שחקן ‪3‬‬
‫שחקן ‪2‬‬
‫שחקן ‪1‬‬
‫‪44‬‬
‫הרעיון‬
‫שלב ‪:2‬‬
‫שלב ‪:1‬‬
‫שחקן ‪2‬‬
‫שחקן ‪2‬‬
‫שחקן ‪1‬‬
‫שחקן ‪2‬‬
‫שחקן ‪4‬‬
‫שחקן ‪4‬‬
‫שחקן ‪3‬‬
‫שחקן ‪4‬‬
‫שחקן ‪3‬‬
‫‪45‬‬
‫הרעיון‬
‫שלב ‪:2‬‬
‫שלב ‪:1‬‬
‫שחקן ‪2‬‬
‫שחקן ‪1‬‬
‫שחקן ‪4‬‬
‫שחקן ‪2‬‬
‫שחקן ‪4‬‬
‫שחקן ‪4‬‬
‫שחקן ‪4‬‬
‫שחקן ‪3‬‬
‫שחקן ‪2‬‬
‫‪46‬‬
‫הרעיון‬
‫שלב ‪( 3‬המנצח)‪:‬‬
‫שלב ‪:2‬‬
‫שלב ‪:1‬‬
‫שחקן ‪2‬‬
‫שחקן ‪2‬‬
‫שחקן ‪1‬‬
‫שחקן ‪4‬‬
‫שחקן ‪2‬‬
‫שחקן ‪3‬‬
‫שחקן ‪4‬‬
‫שחקן ‪4‬‬
‫שחקן ‪2‬‬
‫‪47‬‬
public static 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 = simulateMatch(first, second);
q.enqueue(matchWinner);
}
}
return winner;
}
48
‫מחסנית ‪Stack -‬‬
‫•‬
‫•‬
‫•‬
‫•‬
‫מחסנית (‪ )stack‬היא מבנה נתונים שמזכיר מחסנית של רובה‪-‬‬
‫האיבר שנכנס ראשון יוצא אחרון (‪.)LIFO - Last In First Out‬‬
‫ניתן להסתכל על מחסנית כעל קופסה סגורה בעלת פתח יחיד‪ -‬הן‬
‫עבור ההכנסה והן עבור ההוצאה‪.‬‬
‫הגישה דרך פתח יחיד יוצרת מצב שבו איבר שנכנס אחרון יוצא‬
‫ראשון‪.LIFO -‬‬
‫מחסנית היא שימושית כאשר יש צורך לשמור נתונים בסדר מסוים‬
‫ולשלוף אותם בסדר ההפוך‪.‬‬
‫‪49‬‬
public interface Stack {
/**
* push - adds an element to the stack.
* @param o the elemented to be inserted to the stack.
*/
public void push (Object o);
/**
* pop - removes an element form the stack (LIFO order).
* @return the element from the top of the stack.
*/
public Object pop ();
/**
* isEmpty - checks if the stack is empty or not.
* @return true if there is no more elements in the stack.
*/
public boolean isEmpty();
}
50
‫דוגמה – העתקת מחסנית‬
.‫ תוך שמירת סדר האיברים המקורי‬,‫ להעתקת מחסנית‬,‫יש לממש את השיטה הבאה‬
.‫לא ניתן להעזר במבני נתונים נוספים מלבד מחסנית‬
public static Stack copy(Stack s) {
Stack temp = new StackImpl();
Stack ans = new StackImpl();
while (!s.isEmpty()) {
temp.push(s.pop());
}
while (!temp.isEmpty()) {
Object o = temp.pop();
ans.push(o);
s.push(o);
}
return ans;
51
}
‫השיטה ‪filter‬‬
‫עתה נתכנן שיטה לסינון איברי רשימה‪.‬‬
‫המטרה‪ -‬להשאיר רק איברים העונים על תנאי כלשהו‪.‬‬
‫אך מהו התנאי?‬
‫קשה לקבוע תנאי שכן רשימה היא מבנה נתונים כללי היכול‬
‫להכיל איברים מסוגים שונים‪.‬‬
‫‪52‬‬
‫השיטה ‪filter‬‬
‫אז מה נעשה?‬
‫נגדיר את הממשק ‪ Filter‬המכיל שיטה אחת והיא‬
‫‪ ,accept‬המקבלת אובייקט ומחליטה האם הוא עובר את‬
‫הסינון או לא‪.‬‬
‫ראשית‪ ,‬נגדיר את הממשק ‪:Filter‬‬
‫‪53‬‬
public interface Filter {
/**
* accept – defines the filtration criterion.
* @param o the object to be examined.
* @return true if the object should not be
* filtered out.
*/
public boolean accept(Object o);
}
:‫ המשאיר רק את המחרוזות הלא ריקות‬,String ‫ פשוט עבור המחלקה‬Filter ‫אנו נממש‬
public class EmptyStringFilter implements Filter {
public boolean accept(Object o) {
boolean ans = false;
if (o instanceof String) {
String s = (String) o;
ans = s.length() > 0;
}
return ans;
}
}
54
‫ המקבלת מסנן‬,LinkedList ‫היינו רוצים לממש שיטה במחלקה‬
:‫ כך שנוכל להשתמש בה באופן הבא‬,‫ומחזירה רשימה מסוננת‬
LinkedList lst = new LinkedList();
lst.addLast("Hello");
lst.addLast("");
lst.addLast("");
lst.addLast("Beautiful");
lst.addLast("");
lst.addLast("World");
LinkedList ans =
lst.filter(new EmptyStringFilter());
// ans = ["Hello", "Beautiful", "World" ]
55
: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;
}
56