Transcript Document

‫הרצאה ‪07‬‬
‫עצים‬
‫קרן כליף‬
‫ביחידה זו נלמד‪:‬‬
‫‪ ‬הגדרות‬
‫‪ ‬יצירת עץ‬
‫‪ ‬מעברים על עצים‬
‫‪ ‬חיפוש בעץ‬
‫‪ ‬ממערכים לעץ‬
‫‪ ‬מעץ לרשימה‬
‫‪2‬‬
‫‪© Keren Kalif‬‬
‫הגדרות‬
‫‪ ‬עץ הוא מבנה נתונים המכיל נתונים היררכיים‬
‫‪ ‬למשל‪ :‬אילן יוחסין‪ ,‬מבנה אירגוני‬
‫‪ ‬מבנה‪-‬נתונים זה מורכב מצמתים (העיגולים הירוקים) ומקשתות‬
‫(החצים)‬
‫‪" ‬שורש" הוא הצומת העליון‪ ,‬אף צומת אחר אינו מכיל קשת אליו‬
‫‪" ‬אבא"‪:‬‬
‫‪ ‬שורש‬
‫‪1‬‬
‫‪ ‬צומת שיש ממנו קשת לצומת אחר‬
‫‪" ‬בן" (או‪ :‬ילד‪ ,‬או‪ :‬צומת פנימי)‪:‬‬
‫‪ ‬צומת שיש לו אבא‬
‫‪ ‬כל הצמתים הם בנים פרט לשורש‬
‫‪ ‬אבא ‪2‬‬
‫‪1‬‬
‫‪  8‬בן ‪3‬‬
‫‪6‬‬
‫‪4‬‬
‫‪ ‬בן ‪9‬‬
‫‪" ‬עלה"‪:‬‬
‫‪ ‬צומת שאין לו בנים‬
‫‪3‬‬
‫‪© Keren Kalif‬‬
‫‪ ‬עלה‬
‫‪7‬‬
‫‪5‬‬
‫‪5‬‬
‫הגדרות (‪)2‬‬
‫‪ ‬רמה‪ :‬המרחק של צומת מהשורש‬
‫‪ ‬השורש נמצא ברמה ‪0‬‬
‫‪ ‬מסלול‪ :‬רצף של צמתים שמתחיל בשורש‬
‫‪ ‬למשל‪93 1 :‬‬
‫רמה‬
‫‪ ‬אורך מסלול‪ :‬מס' הקשתות במסלול‬
‫‪ ‬שקול לרמה של הצומת שבסוף המסלול‬
‫‪ ‬אורך המסלול ‪ 93 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‬‬