Notes On BST Operations

Download as pdf or txt
Download as pdf or txt
You are on page 1of 17

Notes on Binary Search Tree operations (pointer – based implementation)

By Phạm Nguyên Khang

Attention: It is just the idea. You must check yourself the below code.

1. Introduction
Node = struct of members: parent
• label: LabelType
• left, right, parent: Node pointer left right

Tree = Node pointer A


label

Tree T;
T = buildATree(); //Build a tree and let T point to the root of the tree

T A

B C

D E F

Two ways to pass a tree to a function as parameters:


• Pass as a value
• Pass as an address

a. Pass a tree as a value


void foo(Tree root) {
//Do something
}

int main() {
Tree T;
T = buildATree(); //Build a tree and
//let T point to the root of the tree
foo(T); //Pass value of T (address of node) to root
...
}

1/17
In this case, root and T are two different pointers (two different arrows) which point to a
same node (i.e., root).
root
T
A

B C

D E F

Because T and root point to the same node, if we change the content of the node using
root, for example, make set the label of the node to ‘X’, T->label will change.

void foo(Tree root) {


root->label = ‘X’;
...
}

int main() {
Tree T;
T = buildATree(); //Build a tree and
//let T point to the root of the tree
foo(T);
...
}

root
T
X

B C

D E F

2/17
However, if we change root, for example, make root point to another node, T rests
unchanged!

void foo(Tree root) {


root = (Tree)malloc(sizeof(Node));
...
}

int main() {
Tree T;
T = buildATree(); //Build a tree and
//let T point to the root of the tree
foo(T);
...
}

root

T
A
x

A C

D E F

3/17
b. Pass a tree as an address
“Address can be considered as another name (alias) of a variable.”

void foo(Tree *pT) {


//Do something
}

int main() {
Tree T;
T = buildATree(); //Build a tree and
//let T point to the root of the tree
foo(&T); //Pass address of T to pT

}

Now, pT and T are different pointers but T and (*pT) are the same pointer. So, we can say
that (*pT) is another name of T.

(*pT) pT

T
A

A C

D E F

If we change (*pT), T will change!

4/17
1. makeNull(T) – makes T be an empty tree

In this function, we want to make the pointer T point to NULL. So we must change T in the
makeNull() function. There is only one way to do this, that is passing T by address!

void makeNull(Tree *pT) {


(*pT) = NULL;
}

int main() {
Tree T;
T = buildATree(); //Build a tree and
//let T point to the root of the tree
makeNull(&T); //Pass address of T to root

}

(*pT) pT (*pT) pT

T T
A NULL

A C

D E F

Before After

5/17
If we try to pass T as value, the result is not what we want.

void makeNull(Tree root) {


root = NULL;
}

int main() {
Tree T;
T = buildATree(); //Build a tree and
//let T point to the root of the tree
makeNull(T); //Pass value of T to root
...
}

root root

T T
A A
NULL

A C A C

D E F D E F

Before After

6/17
2. insert(x, T) – inserts x into tree T
• If T is a null tree (a null pointer), inserting a new node makes T point to the new
node. Therefore, T must be changed.
• If T is not null, inserting a new node will change the content of some node.

So, T must be passed by address!

void insert(LabelType x, Tree *pT) {


Case 1: If *pT is a null tree,
if (*pT == NULL) {
//1a. create a node,
Node *root = (Node*)malloc(sizeof(Node);
//1b. place x and
root->label = x;
//1c. make *pT point to the new node
(*pT) = root;
return;
}

Case 2:
//2a. Find a right position to insert
//2b. Insert a new node
}

Find a right position to insert x into tree T, can be done by loop or recursion.

a. Using loop
We start from root of T (i.e., *pT).

Node *node = *pT;

Loop until node is NULL (we left the tree) or node contains x. In the loop we compare x to
node->label and go left or right according to comparison result.

while (node != NULL) {


parent = node
if x == node->label => break
if x < node->label
node = node->left
else
node = node->right
}

When the loop finishes, “parent” is the last node we meet before leaving the tree.
Depending on relation between x and parent->label, we will insert x into the left or the right
of parent.

7/17
insert 7

1
0
parent
5 2
0

4 8 1
5
1 1
4 8
If x = parent->label, x has been existed in T, just return.

The next step is to create a new node, place x, and set parent->left or parent->right point to
the new node.

Node *child = (Node*)malloc(sizeof(Node));


child->label = x;
if (x < parent)
parent->left = child;
else
parent->right = child;

8/17
void insert(LabelType x, Tree *pT) {
Case 1: If *pT is a null tree,
if (*pT == NULL) {
//1a. create a node,
Node *root = (Node*)malloc(sizeof(Node);
//1b. place x and
root->label = x;
//1c. make *pT point to the new node
(*pT) = root;
return;
}

Case 2:
//2a. Find a right position to insert
Node *node = *pT, *parent;
while (node != NULL) {
parent = node;
if (x == node->label) break;
if (x < node->label)
node = node->left;
else
node = node->right;
}
if (x == parent->label) return; //x existed in T

//2b. Insert a new node


Node *child = (Node)malloc(sizeof(Node));
child->label = x;
if (x < parent)
parent->left = child;
else
parent->right = child;
}

b. Using recursion
Recursive version is a bit difficult to understand in compared to loop version, but it’s very
compact and clean. 😉

Just compare x to label of the root (i.e., (*pT)->label) and use recursion
If x == (*pT)->label => return
If x < ((*pT)->label) => recursively insert x into the left subtree of (*pT)
else => recursively insert x into the right subtree of (*pT)

The left subtree of (*pT) is (*pT)->left.


So, we will pass the address of the left subtree: &((*pT)->left).

9/17
void insert(LabelType x, Tree *pT) {
Case 1: If *pT is a null tree,
if (*pT == NULL) {
//1a. create a node,
Node *root = (Node*)malloc(sizeof(Node);
//1b. place x and
root->label = x;
//1c. make *pT point to the new node
(*pT) = root;
return;
}

Case 2: Compare x to (*pT)->label


//2a. x == (*pT)->label
if (x == (*pT)->label)
return;

//2b. x < (*pT)->label => recursively insert to left subtree


if (x == (*pT)->label)
insert(x, &((*pT)->left));

//2c. x > (*pT)->label => recursively insert to right subtree


else
insert(x, &((*pT)->right));
}

10/17
3. delete(x, T) – deletes x from tree T
• If the deleted node is root, T must be changed.
• Otherwise, we change the content of some node.

So, T must be passed by address!

void delete(LabelType x, Tree *pT) {


Step 1: find the node content x
Step 2: delete the node and adjust the tree
}

Suppose *pT is not empty (if *pT is empty just do nothing).

Find the node content x can be done by loop or recursion.

a. Using loop
We will find the node containing x and its parent.

We start from root of T (i.e., *pT).

Node *node = *pT;

Loop until node is NULL (we left the tree) or node contains x. In the loop we compare x to
node->label and go left or right according to comparison result.

Node *parent = NULL;


while (node != NULL) {
if (x == node->label)
break;
parent = node;
if (x < node->label)
node = node->left;
else
node = node->right;
}

When the loop finishes, if node == NULL, x does not appear in T, just return.

Otherwise, x == node->label and “parent” is the parent of node.

11/17
delete 8
parent
1
0
node
5 2
0

4 8 1
5
1 1
4 8
To delete the node “node”, there are 3 cases to consider:

Case 1: “node” has no child


• Set parent->left or parent->right to NULL. If parent is NULL, the node to be deleted is
root.

if (parent == NULL)
*pT = NULL; //Tree has only one node
else if (node == parent->left)
parent->left = NULL;
else
parent->right = NULL;

Case 2: “node” has two children


• Delete the smallest node in the right subtree of “node”
• Copy label of the deleted node to node->label

node->label = deleteMin(&(node->right));

12/17
Case 3: “node” has one child
• Take the unique child of “node”
• Set parent->left or parent->right to the child. Beware of the case parent == NULL.

Node *child;
if (node->left == NULL)
child = node->right;
else
child = node->left;

if (parent == NULL)
(*pT) = child;
else if (node == parent->left)
parent->left = child;
else
parent->right = child;

13/17
void delete(LabelType x, Tree *pT) {
if ((*pT) == NULL) return;

Step 1: find the node containing x


Node *node = *pT, *parent;
while (node != NULL) {
if (x == node->label)
break;
parent = node; //keep parent before moving node
if (x < node->label)
node = node->left;
else
node = node->right;
}
if (node == NUL)
return; //x does not appear in tree

Step 2: delete the node and adjust the tree


Case 1: node has no child
if (node->left == NULL && node->right == NULL) {
if (parent == NULL)
(*pT) = NULL;
else if (node == parent->left)
parent->left = NULL;
else
parent->right = NULL;
return;
}
Case 2: node has two children
if (node->left != NULL && node->right != NULL) {
node->label = deleteMin(&(node->right));
return;
}
Case 3: node has only one child
Node *child;
if (node->left == NULL) child = node->right;
else child = node->left;

if (parent == NULL)
(*pT) = child;
else if node = parent->left)
parent->left = child;
else
parent->right = child;

14/17
b. Using recursion
Instead of using loop to find the node to be deleted, we compare x to (*pT)->label and
recursively delete x from the left subtree or the right subtree.

if (*pT == NULL) => return


if (x < (*pT)->label)
delete x from the left subtree
else if (x > (*pT)->label)
delete x from the right subtree
else {
//*pT is the node to be deleted
Case 1
Case 2
Case 3
}

void delete(LabelType x, Tree *pT) {


if ((*pT) == NULL) return;

if (x < (*pT)->label) //delete x from the left subtree


delete(x, &((*pT)->left));
else if (x > (*pT)->label) //delete x from the right subtree
delete(x, &((*pT)->right));
else {

//*pT is the node to be deleted


Case 1
if ((*pT)->left == NULL && (*pT)->right == NULL) {
*pT == NULL;
return;
}

Case 2: *pT has two children


if ((*pT)->left != NULL && (*pT)->right != NULL) {
(*pT)->label = deleteMin(&((*pT)->right));
return;
}

Case 3: *pT has only one child


Node *child;
if ((*pT)->left == NULL) child = (*pT)->right;
else child = (*pT)->left;

(*pT) = child;
}
}

15/17
4. deleteMin(T) – deletes the smallest node from T (T is not empty)

LabelType deleteMin(Tree *pT) {


Step 1: find the smallest node

Step 2: delete it
}

a. Using loop
To find the smallest node, from the root, we go left until node->left == NULL. We must also
keep the parent of the node.

Node *node = *pT;


Node *parent = NULL;
while (node->left != NULL) {
parent = node;
node = node->left;
}

Because node->left == NULL (as case 3 in delete function), we will set parent->left or parent-
>right to node->right.

LabelType deleteMin(Tree *pT) {

Step 1: find the smallest node


Node *node = *pT;
Node *parent = NULL;
while (node->left != NULL) {
parent = node;
node = node->left;
}

Step 2: delete it, and return its label


if (parent == NULL) //node to be delete is the root
(*pT) = node->right;
else if (node == parent->left)
parent->left = node->right;
else
parent->right = node->right;

return node->label;
}

16/17
b. Using recursion
• If (*pT)->left != NULL, (*pT) is the node to be delete
• Otherwise, recursively delete the left subtree

LabelType deleteMin(Tree *pT) {


Case 1: if (*pT) has no left child
if ((*pT)->left == NULL) {
LabelType label = (*pT)->label;
(*pT) = (*pT)->right;
return label;
}

Case 2:
return deleteMin(&((*pT)->left));
}

17/17

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy