B*Tree - Timlin.net

advertisement
Chapter 12, Continued
Objectives:
1. Trees
2. Stacks
Trees:
Linked list, stacks, and queues are linear data structures. A tree is a nonlinear, two
dimensional data structure with special properties. Tree nodes contain two or more links.
The most common type of tree is a binary-tree, which uses exactly two nodes, a right
node and a left node. The root node is the first node in a tree. Each link in the root node
refers to a child. The left child is the first node in the left sub-tree, and the right child is
the first node in the right sub-tree. The children of the nodes are called the children.
Computer trees are usually drawn from the top down, exactly the opposite of real trees.
We will discuss binary-search trees. A binary search tree has the characteristic that the
values in the left sub-tree are less than the value in its parent node, and the values in any
right sub-tree are greater than the value in its parent node. Binary trees have two main
functions, insert and delete. We will add two additional ones, one for searching and one
for displaying the list. Binary search trees have all of the benefits of a binary search
against an array and the dynamic memory ability of a linked list, making it the best of
both worlds.
Inserting a node:
If ptrTree is NULL, create a new node.
Call malloc assign the allocated memory to *ptrTree.
Memcpy the structure data.
Set the right and left pointers of the new node to NULL.
Else If ptrTree is NOT NULL
If the name to insert is less than the name ptrTree
Recursively call Insert with the address of ptrTree->Left
Else
Recursively call Insert with the address of ptrTree->Right
End if
End if
1 of 13
C-Code:
typedef struct {
char lname[41]; /* 40 spaces for last name plus the \0 string terminator. */
char fname[41]; /* 40 spaces for first name plus the \0 string terminator. */
char phone[15]; /*15 spaces for phone # plus the \0 string terminator */
} PERSON;
typedef struct lnkPerson {
struct lnkPerson ptrLeft;
PERSON demographics;
struct lnkPerson ptrRight;
} lnkPERSON;
/* Right Child */
/* Left Child */
* Notice our link structure now has two pointers, one for the left and one for the right.
void Insert (PERSON *ptrPerson, lnkPERSON **lnkPerson)
{
lnkPERSON *lnkTemp, *lnkPrior, *lnkTop = *lnkPerson;
if (*lnkPerson == NULL)
{
*lnkPerson = (lnkPERSON *) malloc (sizeof (lnkPERSON));
memcpy (&((*lnkPerson)->demographics), ptrPerson, sizeof (PERSON));
(*lnkPerson)->ptrLeft = NULL;
(*lnkPerson)->ptrRight = NULL;
return;
}
if (strcmp (ptrPerson->lname, (*lnkPerson)->demographics.lname) < 0)
Insert (ptrPerson, &((*lnkPerson)->ptrLeft));
else
Insert (ptrPerson, &((*lnkPerson)->ptrRight));
return;
}
2 of 13
Deleting a Node:
Removing a node is by far the most difficult of the task because you have to reconnect
the remaining sub-trees to form a valid tree. Before attempting to program this task, it’s a
good idea to develop a visual picture of what is to be done.
The simplest case is where the node to be deleted has no children. Such a node is called a
leaf node. All that has to be done in this case is to reset a pointer in the parent node to
NULL and to use the free() function to reclaim the memory used by the deleted node.
Next in complexity is deleting a node with one chile. Deleting the node leaves the child
sub-tree separated from the rest of the tree. To fix this, the address of the child subtree
needs to be stored in the parent node at the location formerly occupied by the address of
the deleted node.
Finally, you have deleting a node with two subtrees. One subtree, say the left can be
attached to where the deleted node was formerly attached. But where should the
remaining subtree go? Keep in mind the basic design of a tree. Every item in a left
3 of 13
subtree precedes the item in the parent node. This means that every item in the right
subtrees comes after every item in the left subtree. Also, because the right subtree once
was part of the subtree headed by the deleted node, every item in the right subtree comes
before the parent node of the deleted node. Imagine coming down the tree looking for
where to place the head of the right sub-tree. It comes before the parent node, so you
haveto go down the left sub-tree from there. However it comes after every item in the
left sub-tree, so you have to take the right branch of the left sub-tree and see whether it
has an opening for a new node. If not, you must go down the right side of the left subtree until you do find an opening.
Now we are ready to begin planning the necessary functions, separating the job into two
tasks. One is associating a particular item with the node to be delted, and the second is
actually deleting the node. One point to note is that all the cases involve modifying a
pointer in the parent node. Which has two important consequences.
1. The program has to identify the node to be deleted.
2. To modify the pointer, the code must pass the address of that pointer to the deleting
function.
The first function will act much like our find function, except that because we will
modify the pointer address, we must receive the address of the pointer. Mean while the
pointer to be modified is itself of type Node* or pointer to Node. Because the function
argument is the address of the pointer, the argument will be of type Node** or pointer-topointer-to-Node. Assuming you have the proper address available, you can write the
deletion function as the following.
C-Code:
void DeleteItem (char strLname[], lnkPERSON **lnkPerson)
{
int intCompare;
if (lnkPerson != NULL)
{
intCompare = strcmp (strLname, (*lnkPerson)->demographics.lname);
printf ("comparing %s to %s, result is %d\n", strLname,
(*lnkPerson)->demographics.lname, intCompare);
if (intCompare == 0)
Delete (lnkPerson);
else if (intCompare < 0)
DeleteItem (strLname, &((*lnkPerson)->ptrLeft));
4 of 13
else
DeleteItem (strLname, &((*lnkPerson)->ptrRight));
}
else
printf ("Find is NULL\n");
}
void Delete (lnkPERSON **lnkPerson)
{
lnkPERSON *lnkTemp;
if ((*lnkPerson)->ptrLeft == NULL)
{
lnkTemp = *lnkPerson;
*lnkPerson = (*lnkPerson)->ptrRight;
free (lnkTemp);
}
else if ((*lnkPerson)->ptrRight == NULL)
{
lnkTemp = *lnkPerson;
*lnkPerson = (*lnkPerson)->ptrLeft;
free (lnkTemp);
}
else /* deleted node has two children */
{
/* Find where to reattach right subtree */
for (lnkTemp = (*lnkPerson)->ptrLeft; lnkTemp->ptrRight != NULL;
lnkTemp = lnkTemp->ptrRight)
continue;
lnkTemp->ptrRight = (*lnkPerson)->ptrRight;
lnkTemp = *lnkPerson;
*lnkPerson = (*lnkPerson)->ptrLeft;
free (lnkTemp);
}
}
5 of 13
The Find Function:
The find function will traverse the binary tree to find the person we want. Binary Search
Trees are very fast, similar to a binary search on an array. On average it will take only 20
hits to find a record in a one million node tree.
If lnkPerson IS NULL Then
The list either empty or we reached the end of the list without finding match.
Return NULL
Else
If Person we are searching for matches the person at this node Then
Return the address of this node.
Else If Person we are search for is less than person at this node Then
Recursively call Find with the Left node.
Else If Person we are search for is less than person at this node Then
Recursively call Find with the Right node.
End If
End If
C-Code:
lnkPERSON* Find (char strLname[], lnkPERSON *lnkPerson)
{
int intCompare;
if (lnkPerson != NULL)
{
intCompare = strcmp (strLname, lnkPerson->demographics.lname);
printf ("comparing %s to %s, result is %d\n", strLname,
lnkPerson->demographics.lname, intCompare);
if (intCompare == 0)
return lnkPerson;
else if (intCompare < 0)
Find (strLname, lnkPerson->ptrLeft);
else
Find (strLname, lnkPerson->ptrRight);
}
else
printf ("Find is NULL\n");
}
6 of 13
Displaying the list in order:
Traversing a tree is more involved than traversing a linked list because each node has two
branches to follow. This branching nature makes divide and conquer recursion a natural
choice for handling the problem. At each node, the function should to the following
 Process the left sub-tree with a recursive call.
 Process the item in the node.
 Process the right sub-tree with a recursive call.
C-Code:
void Display (lnkPERSON *lnkPerson)
{
if (lnkPerson != NULL)
{
Display (lnkPerson->ptrLeft);
printf("[%s %s Phone is %s]\n", lnkPerson->demographics.fname,
lnkPerson->demographics.lname, lnkPerson->demographics.phone);
Display (lnkPerson->ptrRight);
}
return;
}
Stacks:
A stack is a constrained version of a linked list. New nodes can be added to a stack and
removed from a stack only at the top. For this reason, a stacked is referred to as a last-in,
first-out (LIFO) data structure. A stack is referenced via a pointer to the top element of
the stack. The link member in the last node of the stack is set to NULL to indicate the
bottom of the stack.
The primary functions used to manipulate a stack are push and pop. Functions push
creates a new node and places it on top of the stack. Function pop removes a node from
the top of the stack, frees the memory that was allocated to the popped node and returns
the popped value.
Lab Assignment:
Redo the link list lab, this time using the binary tree example below.
7 of 13
Full Source Code for Binary Tree Example:
/* lab chapter 14b */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char lname[41]; /* 40 spaces for last name plus the \0 string terminator. */
char fname[41]; /* 40 spaces for first name plus the \0 string terminator. */
char phone[15]; /*15 spaces for phone # plus the \0 string terminator */
} PERSON;
typedef struct lnkPerson {
struct lnkPerson *ptrLeft;
PERSON demographics;
struct lnkPerson *ptrRight;
} lnkPERSON;
void Insert (PERSON *ptrPerson, lnkPERSON **lnkPerson);
lnkPERSON* Find (char strLname[], lnkPERSON *lnkPerson);
void Display (lnkPERSON *lnkPerson);
void Delete (lnkPERSON **lnkPerson);
void DeleteItem (char strLname[], lnkPERSON **lnkPerson);
int main (void)
{
PERSON MyFriend;
lnkPERSON *ptrCurrent = NULL, *ptrTop = NULL;
int iCounter = 0, intChoice;
FILE *stream;
if ((stream = fopen("myfriends.dat", "r")) == NULL)
{
fprintf(stderr, "Cannot open output file.\n");
return 1;
}
while (fread (&MyFriend, sizeof(PERSON), 1, stream) != NULL)
8 of 13
Insert (&MyFriend, &ptrTop);
do {
printf ("1. Display List\n"
"2. Insert Into List\n"
"3. Find from list\n"
"4. Delete from list\n"
"5. Quit\n\n"
"Enter Choice:");
scanf ("%d", &intChoice);
fflush (stdin);
switch (intChoice) {
case 1:
Display (ptrTop);
break;
case 2:
printf ("\nEnter Last Name: ");
gets (MyFriend.lname);
printf ("\nEnter First Name: ");
gets (MyFriend.fname);
printf ("\nEnter Phone #: ");
gets (MyFriend.phone);
Insert (&MyFriend, &ptrTop);
break;
case 3:
printf ("\nEnter Last Name: ");
gets (MyFriend.lname);
if (strlen(MyFriend.lname) > 0)
{
ptrCurrent = Find (MyFriend.lname, ptrTop);
if (ptrCurrent != NULL)
printf ("%s %s is %s\n",
ptrCurrent->demographics.fname,
ptrCurrent->demographics.lname,
ptrCurrent->demographics.phone);
}
break;
case 4:
printf ("\nEnter Last Name: ");
9 of 13
gets (MyFriend.lname);
if (strlen(MyFriend.lname) > 0)
DeleteItem (MyFriend.lname, &ptrTop);
break;
}
} while (intChoice != 5);
fclose (stream);
return 0;
}
void Display (lnkPERSON *lnkPerson)
{
if (lnkPerson != NULL)
{
Display (lnkPerson->ptrLeft);
printf("[%s %s Phone is %s]\n", lnkPerson->demographics.fname,
lnkPerson->demographics.lname, lnkPerson->demographics.phone);
Display (lnkPerson->ptrRight);
}
return;
}
void Insert (PERSON *ptrPerson, lnkPERSON **lnkPerson)
{
lnkPERSON *lnkTop = *lnkPerson;
if (*lnkPerson == NULL)
{
*lnkPerson = (lnkPERSON *) malloc (sizeof (lnkPERSON));
memcpy (&((*lnkPerson)->demographics), ptrPerson, sizeof (PERSON));
(*lnkPerson)->ptrLeft = NULL;
(*lnkPerson)->ptrRight = NULL;
return;
}
10 of 13
if (strcmp (ptrPerson->lname, (*lnkPerson)->demographics.lname) < 0)
Insert (ptrPerson, &((*lnkPerson)->ptrLeft));
else
Insert (ptrPerson, &((*lnkPerson)->ptrRight));
return;
}
void DeleteItem (char strLname[], lnkPERSON **lnkPerson)
{
int intCompare;
if (lnkPerson != NULL)
{
intCompare = strcmp (strLname, (*lnkPerson)->demographics.lname);
printf ("comparing %s to %s, result is %d\n", strLname,
(*lnkPerson)->demographics.lname, intCompare);
if (intCompare == 0)
Delete (lnkPerson);
else if (intCompare < 0)
DeleteItem (strLname, &((*lnkPerson)->ptrLeft));
else
DeleteItem (strLname, &((*lnkPerson)->ptrRight));
}
else
printf ("Find is NULL\n");
}
void Delete (lnkPERSON **lnkPerson)
{
lnkPERSON *lnkTemp;
if ((*lnkPerson)->ptrLeft == NULL)
{
lnkTemp = *lnkPerson;
*lnkPerson = (*lnkPerson)->ptrRight;
free (lnkTemp);
}
11 of 13
else if ((*lnkPerson)->ptrRight == NULL)
{
lnkTemp = *lnkPerson;
*lnkPerson = (*lnkPerson)->ptrLeft;
free (lnkTemp);
}
else /* deleted node has two children */
{
/* Find where to reattach right subtree */
for (lnkTemp = (*lnkPerson)->ptrLeft; lnkTemp->ptrRight != NULL;
lnkTemp = lnkTemp->ptrRight)
continue;
lnkTemp->ptrRight = (*lnkPerson)->ptrRight;
lnkTemp = *lnkPerson;
*lnkPerson = (*lnkPerson)->ptrLeft;
free (lnkTemp);
}
}
lnkPERSON* Find (char strLname[], lnkPERSON *lnkPerson)
{
int intCompare;
if (lnkPerson != NULL)
{
intCompare = strcmp (strLname, lnkPerson->demographics.lname);
printf ("comparing %s to %s, result is %d\n", strLname,
lnkPerson->demographics.lname, intCompare);
if (intCompare == 0)
return lnkPerson;
else if (intCompare < 0)
Find (strLname, lnkPerson->ptrLeft);
else
Find (strLname, lnkPerson->ptrRight);
}
else
printf ("Find is NULL\n");
12 of 13
return NULL;
}
13 of 13
Download