Transcript Document
הרצאה 07
עצים
קרן כליף
ביחידה זו נלמד:
הגדרות
יצירת עץ
מעברים על עצים
חיפוש בעץ
ממערכים לעץ
מעץ לרשימה
2
© Keren Kalif
הגדרות
עץ הוא מבנה נתונים המכיל נתונים היררכיים
למשל :אילן יוחסין ,מבנה אירגוני
מבנה-נתונים זה מורכב מצמתים (העיגולים הירוקים) ומקשתות
(החצים)
" שורש" הוא הצומת העליון ,אף צומת אחר אינו מכיל קשת אליו
" אבא":
שורש
1
צומת שיש ממנו קשת לצומת אחר
" בן" (או :ילד ,או :צומת פנימי):
צומת שיש לו אבא
כל הצמתים הם בנים פרט לשורש
אבא 2
1
8בן 3
6
4
בן 9
" עלה":
צומת שאין לו בנים
3
© Keren Kalif
עלה
7
5
5
הגדרות ()2
רמה :המרחק של צומת מהשורש
השורש נמצא ברמה 0
מסלול :רצף של צמתים שמתחיל בשורש
למשל93 1 :
רמה
אורך מסלול :מס' הקשתות במסלול
שקול לרמה של הצומת שבסוף המסלול
אורך המסלול 93 1הוא 2
גובה העץ :אורך המסלול הארוך ביותר,
הרמה המקסימלית
גובה עץ זה הוא 3
4
© Keren Kalif
1
0
1
8
2
6
1
3
9
7
5
4
5
2
3
הגדרות ()3
תת-עץ הינו עץ הנפרש מצומת מסויים
תת-העץ הנ"ל נפרש מהצומת עם הערך 3
X אב קדמון של Yאם:
Y נפרש בתת-העץ שיוצא מ .X -כלומר ,במסלול מהשורש אל Y
עוברים דרך X
למשל 3 ,הוא אב קדמון של 5 ,9 ,4ו7 -
1
2
1
5
© Keren Kalif
8
6
3
9
7
5
4
5
עץ בינארי
ילדים2 עץ בינארי הוא עץ שלכל צומת יש מקסימום
1
3
typedef int type;
typedef struct TreeNode
{
type data;
struct TreeNode* left;
struct TreeNode* right;
} TNode;
typedef struct
{
TNode* root;
} Tree;
4
2
9
6
8
7
אנחנו נתמקד בעצים בינאריים
© Keren Kalif
6
פונקציה היוצרת צומת חדשה
TNode* createNewTNode(type data, TNode* left, TNode* right)
{
TNode* newNode = (TNode*)calloc(1, sizeof(TNode));
tr
root
newNode->data = data;
newNode->left = left;
newNode->right = right;
root
return newNode;
3
}
right
left
void main()
{
Tree tr;
1
2
TNode* left = createNewTNode(1, NULL, NULL);
TNode* right = createNewTNode(2, NULL, NULL);
TNode* root = createNewTNode(3, left, right);
tr.root = root;
}
© Keren Kalif
7
Tree buildTree ()
{
Tree t;
:וכדי לייצר את העץ שלנו
t.root = (TNode*)calloc(1, sizeof(TNode));
t.root->data = 1;
tr
root
t.root->left = (TNode*)calloc(1, sizeof(TNode));
t.root->left->data = 3;
t.root->left->left = (TNode*)calloc(1, sizeof(TNode));
t.root->left->left->data = 4;
1
t.root->left->right = (TNode*)calloc (1, sizeof(TNode));
t.root->left->right->data = 9;
3
t.root->left->right->right = (TNode*)calloc (1, sizeof(TNode));
t.root->left->right->right->data = 7;
t.root->right = (TNode*)calloc(1, sizeof(TNode));
t.root->right->data = 2;
4
2
9
6
8
t.root->right->left = (TNode*)calloc(1, sizeof(TNode));
t.root->right->left->data = 6;
t.root->right->right = (TNode*)calloc(1, sizeof(TNode));
t.root->right->right->data = 8;
7
return t;
}
© Keren Kalif
8
עצים ורקורסיות
הרעיון מאחורי כל הפונקציות הקשורות לעצים הוא לבצע את
העבודה עבור כל אחד מתתי-העצים של צומת ,וכנ"ל עבור תתי-
העצים שלו ,ולכן יהיה שימוש רב ברקורסיות
9
© Keren Kalif
פונקציה המחזירה את מספר הצמתים בעץ
1
3
int numOfNodes(const Tree* t)
{
return numOfNodesRec(t->root);
}
2
4
int numOfNodesRec(const TNode* root)
{
if (root == NULL)
return 0;
4
9
6
8
7
3
return 1 + numOfNodesRec(root->left) + numOfNodesRec(root->right);
}
8
© Keren Kalif
10
פונקציה המחשבת גובה העץ
תזכורת::
root
root
3
עפ"י ההגדרה זהו עץ בגובה
,0מאחר ואורך המסלול
הארוך ביותר הוא ...0
עפ"י ההגדרה גובהו של עץ
שהוא NULLאינו מוגדר...
)int height(const Tree* t
{
;)return heightRec(t->root
}
עבור עץ עם שורש בלבד הפונקציה תחזיר ,1
ועבור עץ ריק הפונקציה תחזיר .0
)int heightRec(const TNode* root
{
)if (root == NULL
;return 0
;))return 1 + max(heightRec(root->left), heightRec(root->right
11
© Keren Kalif
}
אבל...
הבעייתיות :גובה של עץ המכיל רק שורש הוא 0
יחד עם זאת ,תנאי העצירה של הרקורסיה הוא כאשר הצומת הוא
,NULLואם נחזיר גם במקרה זה ,0למעשה גם עץ עם שורש
בלבד וגם עץ ללא צמתים בלבד מחזירים תוצאה זהה..
הערך -1אינו באמת גובה העץ אלא
מהווה אינדיקציה שהעץ ריק.
ערך זה נבחר שרירותית ויכל להיות
כל ערך אחר ,אבל אז כמובן הפונקציה
לא הייתה מחזירה את הערך האמיתי..
)int height(const Tree* t
{
;)return heightRec(t->root
}
)int heightRec(const TNode* root
{
)if (root == NULL
;return -1
;))return 1 + max(heightRec(root->left), heightRec(root->right
12
© Keren Kalif
}
פתרון שאינו מגביל את הערך המוחזר עבור עץ
-1 ריק להיות
3
3
3
3
תת
עץ
תת
עץ
תת
עץ
תת
עץ
int heightRec(const TNode* root)
{
if (root == NULL)
return -700; // can be any other value
else if (root->left == NULL && root->right == NULL)
return 0;
else if (root->right == NULL) // only left son
return 1 + heightRec(root->left);
else if (root->left == NULL) // only right son
return 1 + heightRec(root->right);
else // has both sons
return 1 + max( heightRec(root->left), heightRec(root->right) );
}
© Keren Kalif
13
הדפסת ערכי העץ
כמו כל מעבר על עצים ,נעשה זאת ברקורסיה:
:InOrder תת-עץ שמאלי ,שורש ,תת-עץ ימני ()LDR
:PreOrder שורש ,תת-עץ שמאלי ,תת-עץ ימני ()DLR
:PostOrder תת-עץ שמאלי ,תת-עץ ימני ,שורש ()LRD
1
4 3 9 7 1 6 2 8
InOrder:
3
2
PreOrder: 1 3 4 9 7 2 6 8
8
9
6
PostOrder: 4 7 9 3 6 8 2 1
7
14
© Keren Kalif
4
InOrder - הדפסת ערכי העץ
)LDR( עץ ימני- תת, שורש,עץ שמאלי- תת:InOrder
1
3
void printInOrder(Tree t)
{
printInOrderRec(t.root);
}
void printInOrderRec(TNode *t)
{
if (t == NULL)
return;
printInOrderRec(t->left);
printf ("%d ",t->data);
printInOrderRec(t->right);
4
2
9
6
8
7
InOrder:
4 3 9 7 1 6 2 8
L D D R
R
L
L D R
D
R
}
© Keren Kalif
15
PreOrder - הדפסת ערכי העץ
)DLR( עץ ימני- תת,עץ שמאלי- תת, שורש:PreOrder
1
3
void printPreOrder(Tree t)
{
printPreOrderRec(t.root);
}
void printPreOrderRec(TNode *t)
{
if (t == NULL)
return;
printf ("%d ",t->data);
printPreOrderRec(t->left);
printPreOrderRec(t->right);
2
4
9
6
8
7
PreOrder: 1 3 4 9 7 2 6 8
D R D L R
D L
D
L
R
R
}
© Keren Kalif
16
PostOrder - הדפסת ערכי העץ
)LRD( שורש,עץ ימני- תת,עץ שמאלי- תת:PostOrder
1
3
void printPostOrder(Tree t)
{
printPostOrderRec(t.root);
}
void printPostOrderRec(TNode *t)
{
if (t == NULL)
return;
2
4
9
6
7
PostOrder: 4 7 9 3 6 8 2 1
R D
printPostOrderRec(t->left);
printPostOrderRec(t->right);
printf ("%d ",t->data);
8
L
R
L
L
R D
D
R
D
}
© Keren Kalif
17
שחרור הזכרון של עץ
PostOrder ניתן לראות כי איברי העץ משוחררים בסדר
השורש חייב להשתחרר בסוף מאחר והוא זה שמכיל את ההצבעות
...העצים שתחתיו-לתתי
void freeTree(Tree t)
{
freeTreeRec(t.root);
}
void freeTreeRec(TNode *t)
{
if (t == NULL)
return;
freeTreeRec(t->left);
freeTreeRec(t->right);
free (t);
}
© Keren Kalif
18
void main() {
int i;
Tree tr = buildTree();
הדפסת העץ לפי רמות
רמה
for (i=0 ; i < 6 ; i++) {
printf("\nLevel %d: ", i);
printLevel(tr.root, i);
}
printf("\n");
freeTree(tr);
0
1
3
2
1
}
void printLevel(TNode* root, int level)
{
if (root == NULL)
return;
4
9
6
8
7
2
3
if (level == 0)
printf("%d ", root->data);
else {
printLevel(root->left, level-1);
printLevel(root->right, level-1);
}
}
© Keren Kalif
19
TNode* find(Tree t, type val)
{
return findRec(t.root, val);
}
TNode* findRec(TNode* root, type val)
{
TNode* item;
חיפוש בעץ
הפונקציה מחפשת ערך
ומחזירה את,מסויים בעץ
או,הצומת שבו הוא נמצא
אם הערך אינוNULL
קיים בעץ
if (root == NULL)
return NULL;
if (root->data == val)
return root;
item = findRec(root->left, val);
if (item != NULL)
return item;
else
return findRec(root->right, val);
}
© Keren Kalif
20
העתקת עץ
TNode* copyTree(TNode* root)
{
TNode* newRoot;
if (root == NULL)
return NULL;
newRoot = createNewTNode(root->data, NULL, NULL);
newRoot->left = copyTree(root->left);
newRoot->right = copyTree(root->right);
return newRoot;
}
newRoot = createNewTNode(root->data,
copyTree(root->left),
copyTree(root->right));
© Keren Kalif
21
)1( מציאת הערכים המינימלי והמקסימלי בעץ
void getMinAndMaxFromTree(TNode* root, int* minimum, int* maximum)
{
אז הוא,אם יש רק שורש
if (root->left == NULL && root->right == NULL)
המינימום והמקסימום
*minimum = *maximum = root->data;
else if (root->left == NULL) // has only right son
{
getMinAndMaxFromTree(root->right, minimum, maximum);
if (root->data < *minimum)
נחפש בו,אם יש רק בן ימני
*minimum = root->data;
את המינימום והמקסימום
if (root->data > *maximum)
ונשווה אותם מול השורש
*maximum = root->data;
}
else if (root->right == NULL) // has only left son
{
getMinAndMaxFromTree(root->left, minimum, maximum);
if (root->data < *minimum)
נחפש,אם יש רק בן שמאלי
*minimum = root->data;
בו את המינימום והמקסימום
if (root->data > *maximum)
ונשווה אותם מול השורש
*maximum = root->data;
}
© Keren Kalif 22
…
)2( מציאת הערכים המינימלי והמקסימלי בעץ
void getMinAndMaxFromTree(TNode* root, int* minimum, int* maximum)
{
…
נמצא את המינימום והמקסימום של כל
ונחזיר את המינימום,העצים-אחד מתתי
else // has both sons
והמקסימום בינהם לבין הערך שבשורש
{
int leftMin, rightMin, leftMax, rightMax;
getMinAndMaxFromTree(root->left, &leftMin, &leftMax);
getMinAndMaxFromTree(root->right, &rightMin, &rightMax);
*minimum = min(root->data, min(leftMin, rightMin));
*maximum = max(root->data, max(leftMax, rightMax));
}
}
© Keren Kalif
23
מציאת אורך המסלול הארוך ביותר שאיבריו עוקבים
1
1
2
8
3
9
6
7
המסלול הרציף הארוך
ביותר הוא באורך 2
2
4
המסלול הרציף הארוך
ביותר הוא באורך 1
8
3
6
9
7
4
5
הרעיון:
לבדוק בכל תת-עץ מה אורך המסלול הארוך ביותר שאיבריו עוקבים
לבדוק מה המסלול הארוך ביותר המתחיל מהשורש שאיבריו עוקבים
להחזיר את המקסימום מבין 2הערכים שנמצאו
24
© Keren Kalif
מציאת אורך המסלול הארוך ביותר שאיבריו עוקבים
תחזיר את אורך המסלול הארוך
ביותר שאינו מתחיל מהשורש
תחזיר כפרמטר פלט את אורך המסלול
הארוך ביותר המתחיל מהשורש
Int longestSequenceRec(TNode* root, int* longestFromRoot);
int longestSequence(Tree tr)
{
int temp;
if (tr.root == NULL)
return ERROR;
return longestSequenceRec(tr.root, &temp);
}
© Keren Kalif
25
)1(
מציאת אורך המסלול הארוך ביותר שאיבריו עוקבים
int longestSequenceRec(TNode* root, int* longestFromRoot)
{
if (root->left == NULL && root->right == NULL)
{
אז גם אורך המסלול,אם לשורש אין בנים
*longestFromRoot = 0;
הרציף הארוך ביותר המתחיל מהשורש
return 0;
0 וגם המסלול הפנימי הוא,0 הוא
}
else if (root->left == NULL) // only right son
{
int innerPathLen, pathFromRootLen;
innerPathLen = longestSequenceRec(root->right, &pathFromRootLen);
// check if the root contains a longer sequence
if (root->data + 1 == root->right->data)
*longestFromRoot = 1 + pathFromRootLen;
else
*longestFromRoot = 0;
return max(innerPathLen, *longestFromRoot);
}
...
© Keren Kalif
26
)2(
מציאת אורך המסלול הארוך ביותר שאיבריו עוקבים
int longestSequenceRec(TNode* root, int* longestFromRoot)
{
...
else if (root->right == NULL) // only left son
{
int innerPathLen, pathFromRootLen;
innerPathLen = longestSequenceRec(root->left, &pathFromRootLen);
// check if the root contains a longer sequence
if (root->data + 1 == root->left->data)
*longestFromRoot = 1 + pathFromRootLen;
else
*longestFromRoot = 0;
return max(innerPathLen, *longestFromRoot);
}
...
© Keren Kalif
27
)3(
מציאת אורך המסלול הארוך ביותר שאיבריו עוקבים
int longestSequenceRec(TNode* root, int* longestFromRoot)
{
...
else // has the 2 sons
{
int leftRootLen, rightRootLen;
int innerFromLeft = longestSequenceRec(root->left, &leftRootLen);
int innerFromRight = longestSequenceRec(root->right, &rightRootLen);
if (root->data + 1 == root->left->data)
else
leftRootLen++;
leftRootLen = 0;
if (root->data + 1 == root->right->data)
else
rightRootLen++;
rightRootLen = 0;
*longestFromRoot = max(leftRootLen, rightRootLen);
return max(*longestFromRoot, max(innerFromLeft, innerFromRight));
}
}
© Keren Kalif
28
)1( InOrder יצירת רשימה מעץ ע"י מעבר
רשימות ומשרשרת את2 נשתמש בפונקצית העזר המקבלת
הרשימה השניה לסוף הרשימה הראשונה
void concatLists(List* l1, List* l2)
{
if (isEmpty(l1))
{
*l1 = *l2;
//l1->head = l2->head;
//l1->tail = l2->tail;
}
else if (!isEmpty(l2))
{
l1->tail->next = l2->head;
l1->tail = l2->tail;
}
}
© Keren Kalif
29
)2( InOrder יצירת רשימה מעץ ע"י מעבר
List treeToListInOrder(TNode* root)
{
if (root == NULL)
return makeEmptyList();
else
{
List left = treeToListInOrder(root->left);
List right = treeToListInOrder(root->right);
insertValueToTail(&left, root->data);
concatLists(&left, &right);
return left;
}
}
:ניתוח יעילות
O(1) ממומשות ביעילות שלconcatLists - וinsertValueToTail •
• בכל קריאה רקורסיבית יש פעולות קבועות
,• לכן היעילות הכוללת של הפונקציה היא כמספר הקריאות הרקורסיביות
כלומר כמספר הצמתים בעץ
© Keren Kalif
30
מילוי עץ מנתוני מערכים
בהינתן מערך המייצג נתוני עץ במעבר InOrderומערך המייצג
נתוני אותו עץ במעבר PreOrderיש לבנות את העץ .ידוע כי כל
ערך מופיע מקסימום פעם אחת בלבד.
למשל ,בהינתן המערכים הבאים וגודלם יש לייצר את העץ המתאים:
4 3 9 7 1 6 2 8
InOrder:
PreOrder: 1 3 4 9 7 2 6 8
1
2
8
31
3
9
6
7
© Keren Kalif
4
מילוי עץ מנתוני מערכים -הרעיון
4 3 9 7 1 6 2 8
R
1
InOrder:
2
L
3
PreOrder: 1 3 4 9 7 2 6 8
8
9
6
7
הרעיון:
ידוע כי השורש הוא האיבר הראשון במערך הpreorder -
נמצא את מיקומו במערך ה: InOrder -
מכך נדע מהי חלוקת האיברים בין 2תתי-העצים
וכן את כמות האיברים בכל תת-עץ
( עבור תת-העץ השמאלי זה הגודל הכולל פחות האינדקס של השורש)
( עבור תת-העץ הימני זה הגודל הכללי פחות ,1פחות האינדקס של השורש)
32
© Keren Kalif
4
int findIndex(int* arr, int size, int value)
{
int i;
for (i=0 ; i < size ; i++)
if (arr[i] == value)
return i;
return -1; // should never get here..
}
הקוד
TNode* createTreeFromPreOrderAndInOrder(int* pre, int* in, int size)
{
TNode* root;
int index;
if (size == 0) return NULL;
root
= createNewTNode(pre[0], NULL, NULL);
index
= findIndex(in, size, pre[0]);
root->left = createTreeFromPreOrderAndInOrder(pre+1, in, index);
root->right = createTreeFromPreOrderAndInOrder(pre+1+index,
in+1+index, size-index-1);
return root;
}
© Keren Kalif
33
פונקציה המדפיסה את כל הצמתים שלהם
בן אחד בלבד
void printOneSonRec(TNode* root)
{
if (root->left == NULL && root->right == NULL)
return;
else if (root->left == NULL) // only right son
{
printf("%d ", root->data);
printOneSonRec(root->right);
}
else if (root->right == NULL) // only left son
{
printf("%d ", root->data);
printOneSonRec(root->left);
3
}
else // has both sons
{
9
printOneSonRec(root->left);
printOneSonRec(root->right);
}
}
1
3
4
2
9
6
1
8
7
2
6
8
7
© Keren Kalif
34
העצים-פונקציה המחזירה את כמות תתי
)1( מגובה מסויים
void main()
לפעמים פונקציית העזר תזדקק:שימו לב
{
..לפרמטר נוסף כדי לבצע את העבודה
int i;
Tree tr = buildTree();
1
3
4
2
9
6
8
printf("Count sub trees of height:\n");
7
for (i=0 ; i < 6 ; i++)
printf("sub trees of height %d: %d\n", i, countSubTreesOfHeight(tr, i));
freeTree(tr);
}
int countSubTreesOfHeight (Tree tr, int h)
{
int treeHeight;
פונקציית העזר תצטרך בכל שלב לדעת מה גובה
if (tr.root == NULL)
על מנת לדעת האם הוא מתאים לתנאי,העץ-תת
return -700; // error!
return countSubTreesOfHeightRec(tr.root, h, &treeHeight);
}
© Keren Kalif 35
העצים-פונקציה המחזירה את כמות תתי
)2( מגובה מסויים
int countSubTreesOfHeightRec(TNode* root, int h, int* treeHeight)
{
int leftHeight, rightHeight, countLeft, countRight;
if (root->left == NULL && root->right == NULL)
{
.0 אזי גובה העץ הוא,אם אין בנים
*treeHeight = 0;
נחזיר,0 במידה והגובה המבוקש הוא
return h == 0 ? 1 : 0;
0 אחרת נחזיר, כזה1 שיש עץ
}
else if (root->right == NULL) // only left child
{
countLeft = countSubTreesOfHeightRec(root->left, h, &leftHeight);
rightHeight = 0;
נבדוק מה גובהו,אם יש רק בן שמאלי
countRight = 0;
ונאתחל,וכמה בנים שלו עונים לתנאי
}
0 -את נתוני צד ימין ב
…
}
© Keren Kalif
36
...המשך
int countSubTreesOfHeightRec(TNode* root, int h, int* treeHeight)
{
int leftHeight, rightHeight, countLeft, countRight;
נבדוק מה גובהו,אם יש רק בן ימני
…
else if (root->left == NULL) // only right child ונאתחל,וכמה בנים שלו עונים לתנאי
0 -את נתוני צד שמאל ב
{
countRight = countSubTreesOfHeightRec(root->right, h, &rightHeight);
leftHeight = 0;
countLeft = 0;
}
נבקש, הבנים2 אם יש את
else // has both sons
את הנתונים עבור שניהם
{
countRight = countSubTreesOfHeightRec(root->right, h, &rightHeight);
countLeft = countSubTreesOfHeightRec(root->left, h, &leftHeight);
}
נעדכן את גובה העץ ונחזיר
*treeHeight = max(leftHeight, rightHeight) + 1;
את הירך שחישבנו
if (*treeHeight == h) return 1;
else
return countLeft + countRight;
}
© Keren Kalif
37
האם הערך בכל צומת גדול מכל הערכים
שבתתי-העצים שלו
√
sum=20
√
21
√
4
sum=6
7
sum=3
2
1
3
1
38
2
√
פונקציית העזר תצטרך בכל שלב לדעת
מה סכום הצמתים בעץ ,על מנת שהאב
יוכל לבדוק התאמה לתנאי ,וכן האם תת-
העץ בפני עצמו עומד בקריטריון הבדיקה
© Keren Kalif
int isNodeValueBiggerThanSubTrees(Tree tr)
{
int sum;
return isNodeValueBiggerThanSubTreesRec(tr.root, &sum);
}
...הקוד
int isNodeValueBiggerThanSubTreesRec(TNode* root, int* subTreesSum)
{
int leftSum, rightSum, leftRes, rightRes;
if (root == NULL)
{
*subTreesSum = 0;
return 1;
}
if (root->left == NULL && root->right == NULL)
{
*subTreesSum = root->data;
return 1;
}
….
}
© Keren Kalif
39
int isNodeValueBiggerThanSubTreesRec(TNode* root, int* subTreesSum)
{
int leftSum, rightSum, leftRes, rightRes;
…
// here means has at least one son
if (root->right) // has right son
rightRes = isNodeValueBiggerThanSubTreesRec(root->right, &rightSum);
else
{
rightSum = 0;
rightRes = 1;
}
...המשך
if (root->left) // has left son
leftRes = isNodeValueBiggerThanSubTreesRec(root->left, &leftSum);
else
{
leftSum = 0;
leftRes = 1;
}
*subTreesSum = root->data + leftSum + rightSum;
return rightRes && leftRes && (root->data > leftSum + rightSum);
}
© Keren Kalif
40
נשים לב..
2 הדוגמאות האחרונות הן מאותו סגנון:
הפונקציה הרקורסיבית מקבלת עוד נתון שעוזר לה לבצע את העבודה
הראשונה עדכנה כפרמטר את גובה העץ
השניה עדכנה כפרמטר את סכום הצמתים בעץ
יחד עם זאת ,שתיהן כתובות בסגנונות כתיבה שונים:
בראשונה טיפלנו בנפרד בכל אחד מן המקרים בהם יש בנים
.1
.2
.3
אם יש רק תת-עץ שמאלי
אם יש רק תת-עץ ימני
אם יש שני תתי-עצים
בשניה טיפלנו ביחד ב 3 -המקרים הנ"ל:
.1
.2
.3
חישבנו את התוצאה כאשר יש תת-עץ שמאלי
חישבנו את התוצאה כאשר יש תת-עץ ימני
איחדנו בין התוצאות
2 הצורות תקינות ,לבחירתכם...
41
© Keren Kalif
ביחידה זו למדנו:
הגדרות
יצירת עץ
מעברים על עצים
חיפוש בעץ
ממערכים לעץ
מעץ לרשימה
42
© Keren Kalif