תרגול 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
© Copyright 2026 Paperzz