Chapter 6 AVL Search Trees - Lakehead Universityccc.cs.lakeheadu.ca/cs2412/slides/cs2412-s6.pdf ·...
Transcript of Chapter 6 AVL Search Trees - Lakehead Universityccc.cs.lakeheadu.ca/cs2412/slides/cs2412-s6.pdf ·...
CS 2412 Data Structures
Chapter 6
AVL Search Trees
The efficiency of binary search tree depends on the shape of the
tree.
• If an ordered or almost ordered sequence of data are inserted to
a binary tree, then the search is not efficient. (O(n)).
• If the inserted data is random, the tree is more efficient.
• An idea binary search tree is a “balanced” tree, for that the
search complexity is O(log n).
Data Structure 2016 R. Wei 2
Definition
An AVL tree is a binary tree that either is empty or consists of two
AVL subtrees, TL and TR, whose heights differ by no more than 1.
Let HL, HR denote the height of TL, TR respectively, then
|HL −HR| ≤ 1.
An AVL tree is also know as a height-balanced binary search tree.
An AVL tree with HL = HR + 1 is called left high (LH), with
HL = HR − 1 is called right high (RH), and with HL = HR is
called equal (or even) high (EH).
Data Structure 2016 R. Wei 3
Data Structure 2016 R. Wei 4
Balancing trees
When a node is inserted into a tree or deleted from a balanced tree,
the resulting tree may be unbalanced. Some rotating methods are
used in AVL to balance the tree.
1. Left of left: a subtree of a tree that is left high has also become
left high.
2. Right of right: a subtree of a tree that is right high has also
become right high.
3. Right of left: a subtree of a tree that is left high has become
right high.
4. Left of right: a subtree of a tree that is right high has become
left high.
Data Structure 2016 R. Wei 5
Data Structure 2016 R. Wei 6
Data Structure 2016 R. Wei 7
Left of left
One simple case is that the lowest subtree is not balanced. This
case needs a rotation to right.
Another case is that the subtree is balanced, but the whole tree is
not balanced. In this case, we need to rotate the root to right. And
we also need to move the right subtree of the subtree as a left
subtree of the old root.
Right of right is similar.
Data Structure 2016 R. Wei 8
Data Structure 2016 R. Wei 9
Data Structure 2016 R. Wei 10
Right of left
Suppose the root is left high and the left tree is right high. To
balance this tree, we need first to do a left rotation and then do a
right rotation making the left node the new root.
Suppose an internal node is left high and its left subtree is right
high. First rotate the left subtree of the node. Now the node in a
left of left situation, and we do the right rotation as the left of left
case.
Left of right is similar.
Data Structure 2016 R. Wei 11
Data Structure 2016 R. Wei 12
Data Structure 2016 R. Wei 13
Algorithms for AVL
AVL tree is a special kind BST. So some algorithms for BST can be
used for AVL: such as search, retrieval, traversal etc.
The insertion and deletion algorithm should be changed since the
balance need to be checked constantly. Before insertion, AVL tree
is balance. After inserting a node, a balance check may required.
• If the original tree is EH, then no rotation is needed.
• If the original tree is LH and the node is inserted to right
subtree, or if the original tree is RH and the node is inserted to
left subtree, then no rotation is needed.
• Otherwise, we need to check if the resulting tree is balanced.
Data Structure 2016 R. Wei 14
Data Structure 2016 R. Wei 15
If the root is LH and a node has been inserted to the left subtree
which is now taller (higher). Then the following algorithm is called.
The algorithm for right balance is similar to left balance (mirrors of
each other).
Data Structure 2016 R. Wei 16
rotateRight: Make left subtree new root and the right subtree of
the left subtree the left subtree of the original root.
Data Structure 2016 R. Wei 17
Rotate left is similar.
Data Structure 2016 R. Wei 18
Deletion
Deletion is similar to that in BST. First find out the node needs to
be delete. If the node is a leaf, then simply delete it. If the node is
not a leaf, then we need to find out a node which can replace the
deleting node.
Similar to insertion of AVL, we also need to check balance after
deleting a node. For example, if the deleted node is at left subtree,
and the original tree is RH, then we need to check if the left tree is
shorter after deletion.
Data Structure 2016 R. Wei 19
Data Structure 2016 R. Wei 20
Data Structure 2016 R. Wei 21
Data Structure 2016 R. Wei 22
Data Structure 2016 R. Wei 23
Delete left balance is similar.
Data Structure 2016 R. Wei 24
Adjusting the balance factor
After an insertion, we need to adjust the balance factors for the
nodes.
• If the root was EH before an insert, it is now high on the side
in witch the insert was made.
• If an insert was in the shorter subtree of a tree that was not
EH, the root is now EH.
• If an insert was in the higher subtree of a tree that was not
EH, the root must be rotated.
The balance factor need to adjust after deletion in a similar way.
Data Structure 2016 R. Wei 25
Implement AVL in C
#define LH +1 // Left High
#define EH 0 // Even High
#define RH -1 // Right High
typedef struct node
{
void* dataPtr;
struct node* left;
int bal; //LH of EH or RH
struct node* right;
} NODE;
typedef struct
{
int count;
int (*compare) (void* argu1, void* argu2);
NODE* root;
} AVL_TREE;
Data Structure 2016 R. Wei 26
AVL_TREE* AVL_Create
(int (*compare) (void* argu1, void* argu2))
{
AVL_TREE* tree;
tree = (AVL_TREE*) malloc (sizeof (AVL_TREE));
if (tree)
{
tree->root = NULL;
tree->count = 0;
tree->compare = compare;
} // if
return tree;
} // AVL_Create
Data Structure 2016 R. Wei 27
bool AVL_Insert (AVL_TREE* tree, void* dataInPtr)
{
NODE* newPtr;
bool forTaller;
newPtr = (NODE*)malloc(sizeof(NODE));
if (!newPtr)
return false;
newPtr->bal = EH;
newPtr->right = NULL;
newPtr->left = NULL;
newPtr->dataPtr = dataInPtr;
tree->root = _insert(tree, tree->root,
newPtr, &forTaller);
(tree->count)++;
return true;
} // AVL_Insert
Data Structure 2016 R. Wei 28
NODE* _insert (AVL_TREE* tree, NODE* root,
NODE* newPtr, bool* taller)
{
if (!root)
{
root = newPtr;
*taller = true;
return root;
} // if NULL tree
if (tree->compare(newPtr->dataPtr,
root->dataPtr) < 0)
{
root->left = _insert(tree, root->left,
newPtr, taller);
if (*taller)
switch (root->bal)
{
case LH: // Was left high--rotate
Data Structure 2016 R. Wei 29
root = insLeftBal (root, taller);
break;
case EH: // Was balanced--now LH
root->bal = LH;
break;
case RH: // Was right high--now EH
root->bal = EH;
*taller = false;
break;
} // switch
return root;
} // new < node
else
{
root->right = _insert (tree, root->right,
newPtr, taller);
if (*taller)
switch (root->bal)
Data Structure 2016 R. Wei 30
{
case LH: // Was left high--now EH
root->bal = EH;
*taller = false;
break;
case EH: // Was balanced--now RH
root->bal = RH;
break;
case RH: // Was right high--rotate
root = insRightBal
(root, taller);
break;
} // switch
return root;
} // else new data >= root data
return root;
} // _insert
Data Structure 2016 R. Wei 31
NODE* insLeftBal (NODE* root, bool* taller)
{
NODE* rightTree;
NODE* leftTree;
leftTree = root->left;
switch (leftTree->bal)
{
case LH: // Left High--Rotate Right
root->bal = EH;
leftTree->bal = EH;
root = rotateRight (root);
*taller = false;
break;
case EH: // This is an error
printf ("\n\aError in insLeftBal\n");
exit (100);
case RH: // Right High-Requires double
rightTree = leftTree->right;
Data Structure 2016 R. Wei 32
switch (rightTree->bal)
{
case LH: root->bal = RH;
leftTree->bal = EH;
break;
case EH: root->bal = EH;
leftTree->bal = EH;
break;
case RH: root->bal = EH;
leftTree->bal = LH;
break;
} // switch rightTree
rightTree->bal = EH;
root->left = rotateLeft (leftTree);
root = rotateRight (root);
*taller = false;
} // switch
return root; }
Data Structure 2016 R. Wei 33
NODE* rotateLeft (NODE* root)
{
NODE* tempPtr;
tempPtr = root->right;
root->right = tempPtr->left;
tempPtr->left = root;
return tempPtr;
} // rotateLeft
The program for rotateRight is similar.
Data Structure 2016 R. Wei 34
bool AVL_Delete (AVL_TREE* tree, void* dltKey)
{
bool shorter;
bool success;
NODE* newRoot;
newRoot = _delete (tree, tree->root, dltKey,
&shorter, &success);
if (success)
{
tree->root = newRoot;
(tree->count)--;
return true;
} // if
else
return false;
} // AVL_Delete
Data Structure 2016 R. Wei 35
NODE* _delete (AVL_TREE* tree, NODE* root,
void* dltKey, bool* shorter,
bool* success)
{
NODE* dltPtr;
NODE* exchPtr;
NODE* newRoot;
if (!root)
{
*shorter = false;
*success = false;
return NULL;
} // if
if (tree->compare(dltKey, root->dataPtr) < 0)
{
root->left = _delete (tree,
root->left, dltKey,
shorter, success);
Data Structure 2016 R. Wei 36
if (*shorter)
root = dltRightBal (root, shorter);
} // if less
else if (tree->compare(dltKey, root->dataPtr) > 0)
{
root->right = _delete (tree,
root->right, dltKey,
shorter, success);
if (*shorter)
root = dltLeftBal (root, shorter);
} // if greater
else
{
dltPtr = root;
if (!root->right)
// Only left subtree
{
newRoot = root->left;
Data Structure 2016 R. Wei 37
*success = true;
*shorter = true;
free (dltPtr);
return newRoot; // base case
} // if true
else
if (!root->left)
{
newRoot = root->right;
*success = true;
*shorter = true;
free (dltPtr);
return newRoot; // base case
} // if
else
{
exchPtr = root->left;
while (exchPtr->right)
Data Structure 2016 R. Wei 38
exchPtr = exchPtr->right;
root->dataPtr = exchPtr->dataPtr;
root->left = _delete (tree,
root->left, exchPtr->dataPtr,
shorter, success);
if (*shorter)
root = dltRightBal (root, shorter);
} // else
} // equal node
return root;
} // _delete
Data Structure 2016 R. Wei 39
NODE* dltRightBal (NODE* root, bool* shorter)
{
NODE* rightTree;
NODE* leftTree;
switch (root->bal)
{
case LH: // Deleted Left--Now balanced
root->bal = EH;
break;
case EH: // Now Right high
root->bal = RH;
*shorter = false;
break;
case RH: // Right High - Rotate Left
rightTree = root->right;
if (rightTree ->bal == LH)
{letTree = rightTree->left;
switch (leftTree->bal)
Data Structure 2016 R. Wei 40
{
case LH: rightTree->bal = RH;
root->bal = EH;
break;
case EH: root->bal = EH;
rightTree->bal = EH;
break;
case RH: root->bal = LH;
rightTree->bal = EH;
break;
} // switch
leftTree->bal = EH;
root->right =
rotateRight (rightTree);
root = rotateLeft (root);
} // if rightTree->bal == LH
else
{
Data Structure 2016 R. Wei 41
switch (rightTree->bal)
{
case LH:
case RH: root->bal = EH;
rightTree->bal = EH;
break;
case EH: root->bal = RH;
rightTree->bal = LH;
*shorter = false;
break;
} // switch rightTree->bal
root = rotateLeft (root);
} // else
} // switch
return root;
} // dltRightBal
Data Structure 2016 R. Wei 42
void* AVL_Retrieve (AVL_TREE* tree, void* keyPtr)
{
if (tree->root)
return _retrieve (tree, keyPtr, tree->root);
else
return NULL;
} // AVL_Retrieve
Data Structure 2016 R. Wei 43
void* _retrieve (AVL_TREE* tree,
void* keyPtr, NODE* root)
{
if (root)
{
if (tree->compare(keyPtr, root->dataPtr) < 0)
return _retrieve(tree, keyPtr, root->left);
else if (tree->compare(keyPtr, root->dataPtr) > 0)
return _retrieve(tree, keyPtr, root->right);
else
return root->dataPtr;
} // if root
else
return NULL;
} // _retrieve
Data Structure 2016 R. Wei 44
void AVL_Traverse (AVL_TREE* tree,
void (*process) (void* dataPtr))
{
// Statements
_traversal (tree->root, process);
return;
} // end AVL_Traverse
Data Structure 2016 R. Wei 45
void _traversal (NODE* root,
void (*process) (void* dataPtr))
{
// Statements
if (root)
{
_traversal (root->left, process);
process (root->dataPtr);
_traversal (root->right, process);
} // if
return;
} // _traversal
Data Structure 2016 R. Wei 46
AVL_TREE* AVL_Destroy (AVL_TREE* tree)
{
// Statements
if (tree)
_destroy (tree->root);
// All nodes deleted. Free structure
free (tree);
return NULL;
} // AVL_Destroy
Data Structure 2016 R. Wei 47
void _destroy (NODE* root)
{
// Statements
if (root)
{
_destroy (root->left);
free (root->dataPtr);
_destroy (root->right);
free (root);
} // if
return;
} // _destroy
Data Structure 2016 R. Wei 48
NODE* insRightBal (NODE* root, bool* taller)
{
NODE *rightTree;
NODE *leftTree;
rightTree = root->right;
switch (rightTree->bal )
{
case LH: // Left High - Requires double rotation:
leftTree = rightTree-> left;
switch (leftTree->bal )
{
case RH: root->bal = LH;
rightTree->bal = EH;
break;
case EH: root->bal = EH;
rightTree->bal = EH;
break;
case LH: root->bal = EH;
rightTree->bal = RH;
break;
} // switch
Data Structure 2016 R. Wei 49
leftTree->bal = EH;
root->right = rotateRight (rightTree );
root = rotateLeft (root );
*taller = false;
break;
case EH: printf( "\n\a\aError in rightBalance\n" );
break;
root->bal = EH;
taller = false;
break;
case RH: root->bal = EH;
rightTree->bal = EH;
root = rotateLeft ( root );
*taller = false;
break;
} // switch
return root;
} // insRightBal
Data Structure 2016 R. Wei 50
NODE* dltLeftBal ( NODE* root, bool* smaller )
{
NODE *rightTree;
NODE *leftTree;
switch ( root->bal )
{
case RH: root->bal = EH;
break;
case EH: root->bal = LH;
*smaller = false;
break;
case LH: leftTree = root->left;
switch ( leftTree->bal )
{
case LH:
case EH: if ( leftTree->bal == EH )
{
root->bal = LH;
Data Structure 2016 R. Wei 51
leftTree->bal = RH;
*smaller = false;
}
else
{
root->bal = EH;
leftTree->bal = EH;
}
root = rotateRight (root);
break;
case RH: // Double Rotation
rightTree = leftTree->right;
switch ( rightTree->bal )
{
case LH: root->bal = RH;
leftTree->bal = EH;
break;
case EH: root->bal = EH;
Data Structure 2016 R. Wei 52
leftTree->bal = EH;
break;
case RH: root->bal = EH;
leftTree->bal = LH;
break;
} // switch
rightTree->bal = EH;
root->left = rotateLeft (leftTree);
root = rotateRight (root);
break;
} // switch : leftTree->bal
} // switch : root->bal
return root;
} // dltLeftBalance
Data Structure 2016 R. Wei 53
void AVL_Print (AVL_TREE* tree);
void _print (NODE* root, int level);
void AVL_Print (AVL_TREE* tree)
{
void _print (NODE* root, int level);
_print (tree->root, 0);
return;
} // AVL_PRINT
void _print (NODE* root, int level)
{
int i;
if ( root )
{
_print ( root->right, level + 1 );
Data Structure 2016 R. Wei 54
printf( "bal %3d: Level: %3d", root->bal, level );
for (i = 0; i <= level; i++ )
printf ("...." );
printf( "%3d", *(int *)(root->dataPtr) );
if (root->bal == LH)
printf( " (LH)\n");
else if (root->bal == RH)
printf( " (RH)\n");
else
printf( " (EH)\n");
_print ( root->left, level + 1 );
} // if
return;
} // print *
Data Structure 2016 R. Wei 55
Example Count words
Uses an AVL tree to count the number of times each words in a
document. For each time a word is read, search the tree to see if
the tree has an node containing the word. If the word is found,
then increase the count of the word. Otherwise insert a new node
containing the word.
We want to use the ADT AVL C program introduced previously.
Data Structure 2016 R. Wei 56
First we define the data of the node as below.
typedef struct
{
char word[51]; // One word
int count;
} DATA;
Then we construct the system. We can use a structured method to
construct the system, i.e., construct it top-down. So the main
function is simple, which contains main steps. Then for each step,
we give more detailed construction.
Data Structure 2016 R. Wei 57
int main (void)
{
AVL_TREE* wordList;
printf("Start count words in document\n");
wordList = AVL_Create (compareWords);
buildList (wordList);
printList (wordList);
printf("End count words\n");
return 0;
} // main
Data Structure 2016 R. Wei 58
void buildList (AVL_TREE* wordList)
{
char fileName[25];
FILE* fpWords;
bool success;
DATA* aWord;
DATA newWord;
printf("Enter name of file to be processed: ");
scanf ("%24s", fileName);
fpWords = fopen (fileName, "r");
if (!fpWords)
{
printf("%-s could not be opened\a\n",fileName);
printf("Please verify name and try again.\n");
exit (100);
} // !fpWords
Data Structure 2016 R. Wei 59
while (getWord (&newWord, fpWords))
{
aWord = AVL_Retrieve(wordList, &(newWord));
if (aWord)
(aWord->count)++;
else
{
aWord = (DATA*) malloc (sizeof (DATA));
if (!aWord)
{
printf("Error 120 in buildList\n");
exit (120);
} // if
*aWord = newWord;
aWord->count = 1;
success = AVL_Insert (wordList, aWord);
Data Structure 2016 R. Wei 60
if (!success)
{
printf("Error 121 in buildList\a\n");
exit (121);
} // if overflow test
} // else
} // while
printf("End AVL Tree\n");
return;
} // buildList
Data Structure 2016 R. Wei 61
bool getWord (DATA* aWord, FILE* fpWords)
{
char strIn[51];
int ioResult;
int lastChar;
ioResult = fscanf(fpWords, "%50s", strIn);
if (ioResult != 1)
return false;
// Copy and remove punctuation at end of word.
strcpy (aWord->word, strIn);
lastChar = strlen(aWord->word) - 1;
if (ispunct(aWord->word[lastChar]))
aWord->word[lastChar] = ’\0’;
return true;
} // getWord
Data Structure 2016 R. Wei 62
int compareWords (void* arguPtr, void* listPtr)
{
DATA arguValue;
DATA listValue;
arguValue = *(DATA*)arguPtr;
listValue = *(DATA*)listPtr;
return (strcmp(arguValue.word, listValue.word));
} // compare
Data Structure 2016 R. Wei 63
void printList (AVL_TREE* wordList)
{
printf("\nWords found in list\n");
AVL_Traverse (wordList, printWord);
printf("\nEnd of word list\n");
return;
} // printList
void printWord (void* aWord)
{
// Statements
printf("%-25s %3d\n",
((DATA*)aWord)->word, ((DATA*)aWord)->count);
return;
} // printWord
Data Structure 2016 R. Wei 64