Presentation is loading. Please wait.

Presentation is loading. Please wait.

מבני נתונים עצים קרן כליף.

Similar presentations


Presentation on theme: "מבני נתונים עצים קרן כליף."— Presentation transcript:

1 מבני נתונים עצים קרן כליף

2 ביחידה זו נלמד: הגדרות יצירת עץ מעברים על עצים חיפוש בעץ ממערכים לעץ
מעץ לרשימה

3 הגדרות עץ הוא מבנה נתונים המכיל נתונים היררכיים
למשל: אילן יוחסין, מבנה אירגוני מבנה-נתונים זה מורכב מצמתים (העיגולים הירוקים) ומקשתות (החצים) "שורש" הוא הצומת העליון, אף צומת אחר אינו מכיל קשת אליו "אבא": צומת שיש ממנו קשת לצומת אחר "בן" (או: ילד, או: צומת פנימי): צומת שיש לו אבא כל הצמתים הם בנים פרט לשורש "עלה": צומת שאין לו בנים 1  שורש 3 8 2  בן  אבא 4 9 6 1  בן 5 5 7  עלה

4 הגדרות (2) רמה: המרחק של צומת מהשורש מסלול: רצף של צמתים שמתחיל בשורש
השורש נמצא ברמה 0 מסלול: רצף של צמתים שמתחיל בשורש למשל: 1 39 אורך מסלול: מס' הקשתות במסלול שקול לרמה של הצומת שבסוף המסלול אורך המסלול 1 39 הוא 2 גובה העץ: אורך המסלול הארוך ביותר, הרמה המקסימלית גובה עץ זה הוא 3 רמה 1 2 3 1 3 8 2 4 9 6 5 7

5 הגדרות (3) תת-עץ הינו עץ הנפרש מצומת מסויים X אב קדמון של Y אם:
תת-העץ הנ"ל נפרש מהצומת עם הערך 3 X אב קדמון של Y אם: Y נפרש בתת-העץ שיוצא מ- X. כלומר, במסלול מהשורש אל Y עוברים דרך X למשל, 3 הוא אב קדמון של 4, 9, 5 ו- 7 1 3 8 2 4 9 6 5 7

6 אנחנו נתמקד בעצים בינאריים
עץ בינארי עץ בינארי הוא עץ שלכל צומת יש מקסימום 2 ילדים 1 3 2 4 9 6 8 7 typedef int type; typedef struct TreeNode { type data; struct TreeNode* left; struct TreeNode* right; } TNode; typedef struct TNode* root; } Tree; אנחנו נתמקד בעצים בינאריים

7 פונקציה היוצרת צומת חדשה
TNode* createNewTNode(type data, TNode* left, TNode* right) { TNode* newNode = (TNode*)calloc(1, sizeof(TNode)); newNode->data = data; newNode->left = left; newNode->right = right; return newNode; } void main() Tree tr; TNode* left = createNewTNode(1, NULL, NULL); TNode* right = createNewTNode(2, NULL, NULL); TNode* root = createNewTNode(3, left, right); tr.root = root; root tr 3 root 1 left 2 right

8 וכדי לייצר את העץ שלנו: root tr 1 3 2 4 9 6 8 7 Tree buildTree () {
Tree t; t.root = (TNode*)calloc(1, sizeof(TNode)); t.root->data = 1; 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; t.root->left->right = (TNode*)calloc (1, sizeof(TNode)); t.root->left->right->data = 9; 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; 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; return t; } root tr 1 3 2 4 9 6 8 7

9 עצים ורקורסיות הרעיון מאחורי כל הפונקציות הקשורות לעצים הוא לבצע את העבודה עבור כל אחד מתתי-העצים של צומת, וכנ"ל עבור תתי-העצים שלו, ולכן יהיה שימוש רב ברקורסיות

10 פונקציה המחזירה את מספר הצמתים בעץ
1 3 2 int numOfNodes(const Tree* t) { return numOfNodesRec(t->root); } int numOfNodesRec(const TNode* root) if (root == NULL) return 0; return 1 + numOfNodesRec(root->left) + numOfNodesRec(root->right); 4 9 6 8 7 4 3 8

11 פונקציה המחשבת גובה העץ
תזכורת:: root root עפ"י ההגדרה זהו עץ בגובה 0, מאחר ואורך המסלול הארוך ביותר הוא 0... 3 עפ"י ההגדרה גובהו של עץ שהוא NULL אינו מוגדר... int height(const Tree* t) { return heightRec(t->root); } int heightRec(const TNode* root) if (root == NULL) return 0; return 1 + max(heightRec(root->left), heightRec(root->right)); עבור עץ עם שורש בלבד הפונקציה תחזיר 1, ועבור עץ ריק הפונקציה תחזיר 0.

12 אבל... הבעייתיות: גובה של עץ המכיל רק שורש הוא 0
יחד עם זאת, תנאי העצירה של הרקורסיה הוא כאשר הצומת הוא NULL, ואם נחזיר גם במקרה זה 0, למעשה גם עץ עם שורש בלבד וגם עץ ללא צמתים בלבד מחזירים תוצאה זהה.. 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)); הערך 1- גורם לתיקון התוצאה כך שאם יש רק שורש, התוצאה תהיה 0, ואם קיבלנו 1- משמע השורש ריק

13 פתרון נוסף – החזרת ERROR: תיקון במעטפת
3 3 3 3 תת עץ תת עץ תת עץ תת עץ int height(const Tree* t) { if (t->root == NULL) return ERROR; return heightRec(t->root); } int heightRec(const TNode* root) if (root == NULL) return 0; if (root->left== NULL && root->right==NULL) return 0; return 1 + max(heightRec(root->left), heightRec(root->right));

14 פתרון נוסף 3 3 3 3 תת עץ תת עץ תת עץ תת עץ
int heightRec(const TNode* root) { if (root == NULL) return ERROR; 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) ); }

15 הדפסת ערכי העץ כמו כל מעבר על עצים, נעשה זאת ברקורסיה:
InOrder: תת-עץ שמאלי, שורש, תת-עץ ימני (LDR) PreOrder: שורש, תת-עץ שמאלי, תת-עץ ימני (DLR) PostOrder: תת-עץ שמאלי, תת-עץ ימני, שורש (LRD) 1 3 2 4 9 6 8 7 InOrder: PreOrder: PostOrder:

16 הדפסת ערכי העץ - InOrder
InOrder: תת-עץ שמאלי, שורש, תת-עץ ימני (LDR) 1 3 2 4 9 6 8 7 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); InOrder: L D D R L D R R L R D

17 הדפסת ערכי העץ - PreOrder
PreOrder: שורש, תת-עץ שמאלי, תת-עץ ימני (DLR) 1 3 2 4 9 6 8 7 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); PreOrder: D R D L R D L R D L R

18 הדפסת ערכי העץ - PostOrder
PostOrder: תת-עץ שמאלי, תת-עץ ימני, שורש (LRD) 1 3 2 4 9 6 8 7 void printPostOrder(Tree t) { printPostOrderRec(t.root); } void printPostOrderRec(TNode *t) if (t == NULL) return; printPostOrderRec(t->left); printPostOrderRec(t->right); printf ("%d ",t->data); PostOrder: R D L R D L D R L R D

19 שחרור הזכרון של עץ שחרור איברי העץ יהיה בסדר PostOrder
השורש חייב להשתחרר בסוף מאחר והוא זה שמכיל את ההצבעות לתתי-העצים שתחתיו... void freeTree(Tree t) { freeTreeRec(t.root); } void freeTreeRec(TNode *t) if (t == NULL) return; freeTreeRec(t->left); freeTreeRec(t->right); free (t);

20 הדפסת העץ לפי רמות רמה 1 2 3 1 3 2 4 9 6 8 7 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); רמה 1 2 3 1 3 2 4 9 6 8 7 void printLevel(TNode* root, int level) { if (root == NULL) return; if (level == 0) printf("%d ", root->data); else printLevel(root->left, level-1); printLevel(root->right, level-1); }

21 חיפוש בעץ TNode* find(Tree t, type val) { return findRec(t.root, val); } TNode* findRec(TNode* root, type val) TNode* item; 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); הפונקציה מחפשת ערך מסויים בעץ, ומחזירה את הצומת שבו הוא נמצא, או NULL אם הערך אינו קיים בעץ

22 העתקת עץ 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));

23 מציאת הערכים המינימלי והמקסימלי בעץ (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); } … אם יש רק שורש, אז הוא המינימום והמקסימום אם יש רק בן ימני, נחפש בו את המינימום והמקסימום ונשווה אותם מול השורש אם יש רק בן שמאלי, נחפש בו את המינימום והמקסימום ונשווה אותם מול השורש

24 מציאת הערכים המינימלי והמקסימלי בעץ (2)
מציאת הערכים המינימלי והמקסימלי בעץ (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)); } נמצא את המינימום והמקסימום של כל אחד מתתי-העצים, ונחזיר את המינימום והמקסימום בינהם לבין הערך שבשורש

25 מציאת אורך המסלול הארוך ביותר שאיבריו עוקבים
הרעיון: לבדוק בכל תת-עץ מה אורך המסלול הארוך ביותר שאיבריו עוקבים לבדוק מה המסלול הארוך ביותר המתחיל מהשורש שאיבריו עוקבים להחזיר את המקסימום מבין 3 הערכים שנמצאו המסלול הרציף הארוך ביותר הוא באורך 2 1 3 2 4 9 6 8 7 1 3 2 4 9 6 8 7 5 המסלול הרציף הארוך ביותר הוא באורך 1

26 מציאת אורך המסלול הארוך ביותר שאיבריו עוקבים
תחזיר את אורך המסלול הארוך ביותר תחזיר כפרמטר פלט את אורך המסלול הארוך ביותר המתחיל מהשורש int longestSequenceRec(TNode* root, int* longestFromRoot); int longestSequence(Tree tr) { int temp; if (tr.root == NULL) return ERROR; return longestSequenceRec(tr.root, &temp); } 1 3 2 4 9 6 8 7

27 מציאת אורך המסלול הארוך ביותר שאיבריו עוקבים (1)
int longestSequenceRec(TNode* root, int* longestFromRoot) { if (root->left == NULL && root->right == NULL) *longestFromRoot = 0; return 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 return max(innerPathLen, *longestFromRoot); } אם לשורש אין בנים, אז גם אורך המסלול הרציף הארוך ביותר המתחיל מהשורש הוא 0, וגם המסלול הפנימי הוא 0 1 2 6 8

28 מציאת אורך המסלול הארוך ביותר שאיבריו עוקבים (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); } 1 3 4 9 7

29 מציאת אורך המסלול הארוך ביותר שאיבריו עוקבים (3)
1 3 2 4 9 6 8 7 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) leftRootLen++; else leftRootLen = 0; if (root->data + 1 == root->right->data) rightRootLen++; else rightRootLen = 0; *longestFromRoot = max(leftRootLen, rightRootLen); return max(*longestFromRoot, max(innerFromLeft, innerFromRight)); }

30 יצירת רשימה מעץ ע"י מעבר InOrder (1)
נשתמש בפונקצית העזר המקבלת 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;

31 יצירת רשימה מעץ ע"י מעבר InOrder (2)
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; } ניתוח יעילות: insertValueToTail ו- concatLists ממומשות ביעילות של O(1) בכל קריאה רקורסיבית יש פעולות קבועות לכן היעילות הכוללת של הפונקציה היא כמספר הקריאות הרקורסיביות, כלומר כמספר הצמתים בעץ

32 מילוי עץ מנתוני מערכים בהינתן מערך המייצג נתוני עץ במעבר InOrder ומערך המייצג נתוני אותו עץ במעבר PreOrder יש לבנות את העץ. ידוע כי כל ערך מופיע מקסימום פעם אחת בלבד. למשל, בהינתן המערכים הבאים וגודלם יש לייצר את העץ המתאים: 1 3 2 4 9 6 8 7 InOrder: PreOrder:

33 מילוי עץ מנתוני מערכים - הרעיון
1 3 2 4 9 6 8 7 ידוע כי השורש הוא האיבר הראשון במערך ה- preorder נמצא את מיקומו במערך ה- InOrder : מכך נדע מהי חלוקת האיברים בין 2 תתי-העצים וכן את מספר האיברים בכל תת-עץ (עבור תת-העץ השמאלי זה הגודל הכולל פחות האינדקס של השורש) (עבור תת-העץ הימני זה הגודל הכללי פחות 1, פחות האינדקס של השורש) InOrder: PreOrder: L R

34 הקוד InOrder: 4 3 9 7 1 6 2 8 PreOrder: 1 3 4 9 7 2 6 8
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* preArr, int* inArr, int size) TNode* root; int index; if (size == 0) return NULL; root = createNewTNode(preArr[0], NULL, NULL); index = findIndex(inArr, size, preArr[0]); root->left = createTreeFromPreOrderAndInOrder(preArr+1, inArr, index); root->right = createTreeFromPreOrderAndInOrder(preArr+index+1, inArr+index+1, size-index-1); return root; InOrder: PreOrder: L R

35 פונקציה המדפיסה את כל הצמתים שלהם בן אחד בלבד
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 printOneSonRec(root->left); else // has both sons 1 3 2 4 9 6 8 7 1 3 2 9 6 8 7

36 פונקציה המחזירה את מספר תתי-העצים מגובה מסויים (1)
void main() { int i; Tree tr = buildTree(); printf("Count sub trees of height:\n"); for (i=0 ; i < 6 ; i++) printf("sub trees of height %d: %d\n", i, countSubTreesOfHeight(tr, i)); freeTree(tr); } 1 3 2 4 9 6 8 7 שימו לב: לפעמים פונקציית העזר תזדקק לפרמטר נוסף כדי לבצע את העבודה.. int countSubTreesOfHeight (Tree tr, int h) { int treeHeight; if (tr.root == NULL) return ERROR; return countSubTreesOfHeightRec(tr.root, h, &treeHeight); } פונקציית העזר תצטרך בכל שלב לדעת מה גובה תת-העץ, על מנת לדעת האם הוא מתאים לתנאי

37 פונקציה המחזירה את מספר תתי-העצים מגובה מסויים (2)
int countSubTreesOfHeightRec(TNode* root, int h, int* treeHeight) { int leftHeight, rightHeight, countLeft, countRight; if (root->left == NULL && root->right == NULL) *treeHeight = 0; return h == 0 ? 1 : 0; } else if (root->right == NULL) // only left child countLeft = countSubTreesOfHeightRec(root->left, h, &leftHeight); rightHeight = 0; countRight = 0; אם אין בנים, אזי גובה העץ הוא 0. במידה והגובה המבוקש הוא 0, נחזיר שיש עץ 1 כזה, אחרת נחזיר 0 אם יש רק בן שמאלי, נבדוק מה גובהו וכמה בנים שלו עונים לתנאי, ונאתחל את נתוני צד ימין ב- 0

38 המשך... int countSubTreesOfHeightRec(TNode* root, int h, int* treeHeight) { int leftHeight, rightHeight, countLeft, countRight; else if (root->left == NULL) // only right child countRight = countSubTreesOfHeightRec(root->right, h, &rightHeight); leftHeight = 0; countLeft = 0; } else // has both sons countLeft = countSubTreesOfHeightRec(root->left, h, &leftHeight); *treeHeight = max(leftHeight, rightHeight) + 1; if (*treeHeight == h) return 1; else return countLeft + countRight; אם יש רק בן ימני, נבדוק מה גובהו וכמה בנים שלו עונים לתנאי, ונאתחל את נתוני צד שמאל ב- 0 אם יש את 2 הבנים, נבקש את הנתונים עבור שניהם נעדכן את גובה העץ ונחזיר את הערך שחישבנו

39 האם הערך בכל צומת גדול מכל הערכים שבתתי-העצים שלו
sum=20√ 21 sum=6√ sum=3√ 7 4 2 3 1 2 1 פונקציית העזר תצטרך בכל שלב לדעת מה סכום הצמתים בעץ, על מנת שהאב יוכל לבדוק התאמה לתנאי, וכן האם תת-העץ בפני עצמו עומד בקריטריון הבדיקה

40 הקוד... 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; ….

41 המשך... 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); leftSum = 0; leftRes = 1; *subTreesSum = root->data + leftSum + rightSum; return rightRes && leftRes && (root->data > leftSum + rightSum);

42 נשים לב.. 2 הדוגמאות האחרונות הן מאותו סגנון:
הפונקציה הרקורסיבית מקבלת נתון נוסף שעוזר לה לבצע את העבודה הראשונה עדכנה כפרמטר את גובה העץ השניה עדכנה כפרמטר את סכום הצמתים בעץ במקרה זה יש להגדיר משתנים לוקאליים שיעודכנו עם הערכים המתאימים בו תת-עץ ימין ותת-עץ שמאל, ולבסוף לבצע אינטגרציה של התוצאות יחד עם זאת, שתיהן כתובות בסגנונות כתיבה שונים: בראשונה טיפלנו בנפרד בכל אחד מן המקרים בהם יש בנים אם יש רק תת-עץ שמאלי אם יש רק תת-עץ ימני אם יש שני תתי-עצים בשניה טיפלנו ביחד ב- 3 המקרים הנ"ל: חישבנו את התוצאה כאשר יש תת-עץ שמאלי חישבנו את התוצאה כאשר יש תת-עץ ימני איחדנו בין התוצאות 2 הצורות תקינות, לבחירתכם...

43 ביחידה זו למדנו: הגדרות יצירת עץ מעברים על עצים חיפוש בעץ ממערכים לעץ
מעץ לרשימה

44 תרגיל 1 כתוב פונקציה המקבלת עץ בינארי, ומחזירה את מספר הקודקודים שההפרש בין סכום הצמתים בתת-עץ שמאל שלו לסכום הצמתים בתת-עץ ימין שלו קטן מערך הקודקוד קישור לקובץ הסטרטר: - starter.c

45 פתרון


Download ppt "מבני נתונים עצים קרן כליף."

Similar presentations


Ads by Google