Bah Abdulai Ibrahim D.S. & A

You might also like

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 22

1 SILICON HILL MILE 91

TASK: ASSIGNMENT

MODULE: DATA STRUCTURE AND ALGORITHMS

DEPARTMENT: COMPUTER SCIENCE (C.S)

NAME: BAH ABDULAI IBRAHIM

ID NO: 2023126

LEVEL: YEAR TWO (2)

LECTURER: MR. Isaac M. Sesay


Q1 A

1B

1C
1D

i. In-order Traversal (Left, Root, Right)


 Visit left subtree of 36:
 Visit left subtree of 24:
 Visit left subtree of 10:
 Visit left subtree of 3 (empty).
 Visit root 3.
 Visit right subtree of 3 (empty).
 Visit root 10.
 Visit right subtree of 10:
 Visit left subtree of 16 (empty).
 Visit root 16.
 Visit right subtree of 16 (empty).
 Visit root 24.
 Visit right subtree of 24:
 Visit left subtree of 25 (empty).
 Visit root 25.
 Visit right subtree of 25 (empty).
 Visit root 36.
 Visit right subtree of 36:
 Visit left subtree of 44 (empty).
 Visit root 44.
 Visit right subtree of 44 (empty).

In-order Traversal: 3, 10, 16, 24, 25, 36, 44

ii. Post-order Traversal (Left, Right, Root)


 Visit left subtree of 36:
 Visit left subtree of 24:
 Visit left subtree of 10:
 Visit left subtree of 3 (empty).
 Visit right subtree of 3 (empty).
 Visit root 3.
 Visit right subtree of 10:
 Visit left subtree of 16 (empty).
 Visit right subtree of 16 (empty).
 Visit root 16.
 Visit root 10.
 Visit right subtree of 24:
 Visit left subtree of 25 (empty).
 Visit right subtree of 25 (empty).
 Visit root 25.
 Visit root 24.
 Visit right subtree of 36:
 Visit left subtree of 44 (empty).
 Visit right subtree of 44 (empty).
 Visit root 44.
 Visit root 36.

Post-order Traversal: 3, 16, 10, 25, 24, 44, 36

iii. Pre-order Traversal (Root, Left, Right)


 Visit root 36.
 Visit left subtree of 36:
 Visit root 24.
 Visit left subtree of 24:
 Visit root 10.
 Visit left subtree of 10:
 Visit root 3.
 Visit left subtree of 3 (empty).
 Visit right subtree of 3 (empty).
 Visit right subtree of 10:
 Visit root 16.
 Visit left subtree of 16 (empty).
 Visit right subtree of 16 (empty).
 Visit right subtree of 24:
 Visit root 25.
 Visit left subtree of 25 (empty).
 Visit right subtree of 25 (empty).
 Visit right subtree of 36:
 Visit root 44.
 Visit left subtree of 44 (empty).
 Visit right subtree of 44 (empty).

Pre-order Traversal: 36, 24, 10, 3, 16, 25, 44

So, the order in which the node values are reached in each traversal type for the BST of part (c) is:

In-order Traversal: 3, 10, 16, 24, 25, 36, 44

Post-order Traversal: 3, 16, 10, 25, 24, 44, 36

Pre-order Traversal: 36, 24, 10, 3, 16, 25, 44

1E
The height of a tree is the length of the longest path from the root to a leaf.

Therefore Height of the Tree: 3


Q2 A
AVL Tree Implementation in Java

import java.util.Random;

class AVLTreeNode {

int key;

int height;

AVLTreeNode left;

AVLTreeNode right;

AVLTreeNode(int key) {

this.key = key;

this.height = 1;

class AVLTree {

private AVLTreeNode root;

// Function to get the height of the tree

private int height(AVLTreeNode node) {

return node == null ? 0 : node.height;

// Function to right rotate subtree rooted with y

private AVLTreeNode rightRotate(AVLTreeNode y) {

AVLTreeNode x = y.left;

AVLTreeNode T2 = x.right;

x.right = y;

y.left = T2;

y.height = Math.max(height(y.left), height(y.right)) + 1;

x.height = Math.max(height(x.left), height(x.right)) + 1;

return x;

}
// Function to left rotate subtree rooted with x

private AVLTreeNode leftRotate(AVLTreeNode x) {

AVLTreeNode y = x.right;

AVLTreeNode T2 = y.left;

y.left = x;

x.right = T2;

x.height = Math.max(height(x.left), height(x.right)) + 1;

y.height = Math.max(height(y.left), height(y.right)) + 1;

return y;

// Get balance factor of node

private int getBalance(AVLTreeNode node) {

return node == null ? 0 : height(node.left) - height(node.right);

// Function to insert a key into the tree

public void insert(int key) {

root = insertNode(root, key);

private AVLTreeNode insertNode(AVLTreeNode node, int key) {

if (node == null) {

return new AVLTreeNode(key);

if (key < node.key) {

node.left = insertNode(node.left, key);

} else if (key > node.key) {

node.right = insertNode(node.right, key);

} else {

return node; // Duplicate keys are not allowed

node.height = 1 + Math.max(height(node.left), height(node.right));


int balance = getBalance(node);

// Left Left Case

if (balance > 1 && key < node.left.key) {

return rightRotate(node);

// Right Right Case

if (balance < -1 && key > node.right.key) {

return leftRotate(node);

// Left Right Case

if (balance > 1 && key > node.left.key) {

node.left = leftRotate(node.left);

return rightRotate(node);

// Right Left Case

if (balance < -1 && key < node.right.key) {

node.right = rightRotate(node.right);

return leftRotate(node);

return node;

// Function to delete a key from the tree

public void delete(int key) {

root = deleteNode(root, key);

private AVLTreeNode deleteNode(AVLTreeNode root, int key) {

if (root == null) {

return root;

if (key < root.key) {


root.left = deleteNode(root.left, key);

} else if (key > root.key) {

root.right = deleteNode(root.right, key);

} else {

if ((root.left == null) || (root.right == null)) {

AVLTreeNode temp = null;

if (temp == root.left) {

temp = root.right;

} else {

temp = root.left;

if (temp == null) {

root = null;

} else {

root = temp;

} else {

AVLTreeNode temp = getMinValueNode(root.right);

root.key = temp.key;

root.right = deleteNode(root.right, temp.key);

if (root == null) {

return root;

root.height = Math.max(height(root.left), height(root.right)) + 1;

int balance = getBalance(root);

// Left Left Case

if (balance > 1 && getBalance(root.left) >= 0) {

return rightRotate(root);
}

// Left Right Case

if (balance > 1 && getBalance(root.left) < 0) {

root.left = leftRotate(root.left);

return rightRotate(root);

// Right Right Case

if (balance < -1 && getBalance(root.right) <= 0) {

return leftRotate(root);

// Right Left Case

if (balance < -1 && getBalance(root.right) > 0) {

root.right = rightRotate(root.right);

return leftRotate(root);

return root;

// Function to get the node with the minimum value

private AVLTreeNode getMinValueNode(AVLTreeNode node) {

AVLTreeNode current = node;

while (current.left != null) {

current = current.left;

return current;

// Function to search for a key in the tree

public boolean search(int key) {

return searchNode(root, key) != null;

private AVLTreeNode searchNode(AVLTreeNode root, int key) {


if (root == null || root.key == key) {

return root;

if (key < root.key) {

return searchNode(root.left, key);

return searchNode(root.right, key);

// Function for preorder traversal of the tree

public void preOrder() {

preOrderTraversal(root);

System.out.println();

private void preOrderTraversal(AVLTreeNode node) {

if (node != null) {

System.out.print(node.key + " ");

preOrderTraversal(node.left);

preOrderTraversal(node.right);

public static void main(String[] args) {

AVLTree tree = new AVLTree();

// Generate a random dataset of integers

Random rand = new Random();

int[] data = rand.ints(10, 1, 100).toArray();

System.out.println("Inserting values: ");

for (int key : data) {

System.out.print(key + " ");


tree.insert(key);

System.out.println();

System.out.println("Preorder traversal of the AVL tree after insertion:");

tree.preOrder();

// Randomly choose some keys to delete

int[] keysToDelete = new int[3];

for (int i = 0; i < 3; i++) {

keysToDelete[i] = data[rand.nextInt(data.length)];

System.out.println("Deleting values: ");

for (int key : keysToDelete) {

System.out.print(key + " ");

tree.delete(key);

System.out.println();

System.out.println("Preorder traversal of the AVL tree after deletion:");

tree.preOrder();

// Search for a random key in the tree

int searchKey = data[rand.nextInt(data.length)];

System.out.println("Searching for " + searchKey + " in the AVL tree:");

boolean searchResult = tree.search(searchKey);

if (searchResult) {

System.out.println("Node with key " + searchKey + " found.");

} else {

System.out.println("Node with key " + searchKey + " not found.");

}
Analysis of Time Complexity

Insertion: The time complexity of inserting a node in an AVL tree is O(logn) because the tree is balanced and the
height is kept logarithmic.

Deletion: The time complexity of deleting a node is also O(logn) for the same reason as insertion.

Search: The time complexity of searching for a node is O(logn) since the tree height is O(logn).

2B
GRAPH IMPLEMENTATION IN JAVA

import java.util.*;

class Graph {

private Map<Integer, List<Integer>> adjacencyList;

public Graph() {

adjacencyList = new HashMap<>();

// Add a vertex to the graph

public void addVertex(int vertex) {

adjacencyList.putIfAbsent(vertex, new ArrayList<>());

// Remove a vertex from the graph

public void removeVertex(int vertex) {

adjacencyList.values().forEach(e -> e.remove(Integer.valueOf(vertex)));

adjacencyList.remove(vertex);

// Add an edge to the graph

public void addEdge(int source, int destination) {

adjacencyList.putIfAbsent(source, new ArrayList<>());

adjacencyList.putIfAbsent(destination, new ArrayList<>());

adjacencyList.get(source).add(destination);

// Remove an edge from the graph


public void removeEdge(int source, int destination) {

List<Integer> edges = adjacencyList.get(source);

if (edges != null) {

edges.remove(Integer.valueOf(destination));

// Perform Depth-First Search (DFS)

public void dfs(int startVertex) {

Set<Integer> visited = new HashSet<>();

dfsRecursive(startVertex, visited);

private void dfsRecursive(int vertex, Set<Integer> visited) {

if (visited.contains(vertex)) {

return;

visited.add(vertex);

System.out.print(vertex + " ");

for (int neighbor : adjacencyList.getOrDefault(vertex, new ArrayList<>())) {

dfsRecursive(neighbor, visited);

// Perform Breadth-First Search (BFS)

public void bfs(int startVertex) {

Set<Integer> visited = new HashSet<>();

Queue<Integer> queue = new LinkedList<>();

queue.add(startVertex);

visited.add(startVertex);

while (!queue.isEmpty()) {

int vertex = queue.poll();

System.out.print(vertex + " ");


for (int neighbor : adjacencyList.getOrDefault(vertex, new ArrayList<>())) {

if (!visited.contains(neighbor)) {

visited.add(neighbor);

queue.add(neighbor);

// Check if the graph contains a cycle and return the cycle path if found

public List<Integer> findCycle() {

Set<Integer> visited = new HashSet<>();

Set<Integer> stack = new HashSet<>();

List<Integer> path = new ArrayList<>();

for (int vertex : adjacencyList.keySet()) {

if (findCycleUtil(vertex, visited, stack, path)) {

return path;

return null;

private boolean findCycleUtil(int vertex, Set<Integer> visited, Set<Integer> stack, List<Integer> path) {

if (stack.contains(vertex)) {

path.add(vertex);

return true;

if (visited.contains(vertex)) {

return false;

visited.add(vertex);

stack.add(vertex);
path.add(vertex);

for (int neighbor : adjacencyList.getOrDefault(vertex, new ArrayList<>())) {

if (findCycleUtil(neighbor, visited, stack, path)) {

return true;

path.remove(path.size() - 1);

stack.remove(vertex);

return false;

// Test the graph with various operations

public static void main(String[] args) {

Graph graph = new Graph();

// Add vertices and edges

graph.addVertex(0);

graph.addVertex(1);

graph.addVertex(2);

graph.addVertex(3);

graph.addEdge(0, 1);

graph.addEdge(1, 2);

graph.addEdge(2, 3);

graph.addEdge(3, 1); // This edge creates a cycle

// Perform DFS and BFS traversals

System.out.print("DFS traversal starting from vertex 0: ");

graph.dfs(0);

System.out.println();

System.out.print("BFS traversal starting from vertex 0: ");

graph.bfs(0);

System.out.println();

// Check for cycles


List<Integer> cycle = graph.findCycle();

if (cycle != null) {

System.out.print("Cycle detected: ");

for (int vertex : cycle) {

System.out.print(vertex + " ");

} else {

System.out.println("No cycle detected.");

Cycle Detection Logic

o Visited Set: Tracks all the nodes that have been visited.
o Recursion Stack Set: Tracks nodes in the current path of the recursion stack.
o Path List: Stores the sequence of nodes in the current path. If a cycle is detected, this list will be used to
return the cycle sequence.

Testing

o Graph Construction: A graph is constructed with vertices and edges, including an edge that creates a cycle.
o DFS and BFS: Perform DFS and BFS traversals starting from vertex 0.
o Cycle Detection: The findCycle method is used to detect a cycle and return the sequence of tasks that form
the cycle.

Time Complexity Analysis

Add Vertex: O(1)

Remove Vertex: O(V+E) (where V is the number of vertices and E is the number of edges)

Add Edge: O(1)

Remove Edge: O(E) (in the worst case)

DFS and BFS Traversals: O(V+E)

Cycle Detection: O(V+E)


Q3
Hash Table Implementation

import java.util.LinkedList;

import java.util.List;

class HashTable<K, V> {

private static final int INITIAL_CAPACITY = 16;

private static final double LOAD_FACTOR_THRESHOLD = 0.75;

private List<Entry<K, V>>[] table;

private int size;

public HashTable() {

table = new LinkedList[INITIAL_CAPACITY];

size = 0;

private static class Entry<K, V> {

K key;

V value;

Entry(K key, V value) {

this.key = key;

this.value = value;

private int hash(K key) {

return Math.abs(key.hashCode()) % table.length;

public void insert(K key, V value) {

int index = hash(key);

if (table[index] == null) {

table[index] = new LinkedList<>();

}
for (Entry<K, V> entry : table[index]) {

if (entry.key.equals(key)) {

entry.value = value; // Update existing key

return;

table[index].add(new Entry<>(key, value));

size++;

if ((double) size / table.length > LOAD_FACTOR_THRESHOLD) {

resize();

public V search(K key) {

int index = hash(key);

if (table[index] != null) {

for (Entry<K, V> entry : table[index]) {

if (entry.key.equals(key)) {

return entry.value;

return null;

public void delete(K key) {

int index = hash(key);

if (table[index] != null) {

for (Entry<K, V> entry : table[index]) {

if (entry.key.equals(key)) {

table[index].remove(entry);

size--;
return;

private void resize() {

List<Entry<K, V>>[] oldTable = table;

table = new LinkedList[oldTable.length * 2];

size = 0;

for (List<Entry<K, V>> bucket : oldTable) {

if (bucket != null) {

for (Entry<K, V> entry : bucket) {

insert(entry.key, entry.value);

Spell Checker Implementation

import java.io.*;

import java.util.*;

class SpellChecker {

private HashTable<String, String> dictionary;

public SpellChecker() {

dictionary = new HashTable<>();

public void loadDictionary(String dictionaryPath) throws IOException {

BufferedReader br = new BufferedReader(new FileReader(dictionaryPath));

String word;
while ((word = br.readLine()) != null) {

dictionary.insert(word.toLowerCase(), word);

br.close();

public List<String> checkSpelling(String documentPath) throws IOException {

List<String> misspelledWords = new ArrayList<>();

BufferedReader br = new BufferedReader(new FileReader(documentPath));

String line;

while ((line = br.readLine()) != null) {

String[] words = line.split("\\W+");

for (String word : words) {

if (!word.isEmpty() && dictionary.search(word.toLowerCase()) == null) {

misspelledWords.add(word);

br.close();

return misspelledWords;

public List<String> getSuggestions(String misspelledWord) {

List<String> suggestions = new ArrayList<>();

for (int i = 0; i < misspelledWord.length(); i++) {

char[] chars = misspelledWord.toCharArray();

for (char c = 'a'; c <= 'z'; c++) {

chars[i] = c;

String newWord = new String(chars);

if (dictionary.search(newWord) != null) {

suggestions.add(newWord);

}
}

return suggestions;

public static void main(String[] args) {

try {

SpellChecker spellChecker = new SpellChecker();

spellChecker.loadDictionary("dictionary.txt");

List<String> misspelledWords = spellChecker.checkSpelling("document.txt");

System.out.println("Misspelled words: " + misspelledWords);

for (String word : misspelledWords) {

List<String> suggestions = spellChecker.getSuggestions(word);

System.out.println("Suggestions for \"" + word + "\": " + suggestions);

} catch (IOException e) {

e.printStackTrace();

Testing

To test the spell checker:

a. Create a dictionary.txt file with a list of correctly spelled words, one per line.
b. Create a document.txt file with the text to check for spelling errors.
c. Run the main method in the SpellChecker class.

Time Complexity Analysis

 Insert: Average case O(1), worst case O(n) due to chaining.


 Search: Average case O(1), worst case O(n).
 Delete: Average case O(1), worst case O(n).
 Resize: O(n), but this happens infrequently when the load factor threshold is exceeded.

Potential Optimizations

 Better Hash Functions: Implement a more sophisticated hash function to reduce collisions.
 Dynamic Resizing: Resize the table dynamically based on more complex heuristics to balance memory
usage and performance.
 Cache Recent Searches: Implement a caching mechanism for recently searched words to speed up repeated
lookups.
 Phonetic Algorithms: Use phonetic algorithms (e.g., Soundex) for generating better suggestions for
misspelled words.

You might also like