Skip to content

Commit

Permalink
Merge pull request #230 from anirudh-248/main
Browse files Browse the repository at this point in the history
Added AVL Tree Visualizer project
  • Loading branch information
UTSAVS26 authored Oct 7, 2024
2 parents 6d001ed + cfb3724 commit 9a2eb3e
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
env
41 changes: 41 additions & 0 deletions Algorithms_and_Data_Structures/avl_tree_visualizer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# AVL Tree Visualizer

## 🎯 Goal
The main goal of this project is to provide an interactive tool for visualizing AVL tree construction in real-time. The purpose is to help users understand how AVL trees are built and maintained, including rotations, balancing, and traversal methods. This tool is ideal for learners and educators looking to explore AVL tree concepts.

## 🧵 Dataset
No dataset is required for this project, as the tree is constructed from user input (either file uploads or manual input).

## 🧾 Description
This project is an interactive web app developed using Streamlit. It allows users to build and visualize an AVL tree step-by-step. Users can either upload a file with numbers or manually enter them, and the app dynamically updates the tree structure. The visualizer provides real-time feedback on the balancing operations (rotations) performed to maintain the AVL properties. The app also displays tree traversals and helps users learn how an AVL tree operates.

## 🧮 What I had done!

Built an interactive web app using Streamlit.
Developed two modes of input: file upload and manual entry.
Implemented real-time tree visualization, where users can observe the AVL tree being constructed dynamically.
Added features to show tree traversals.
Provided feedback on user input and AVL tree balancing operations.

## 🚀 Models Implemented
No machine learning models were used in this project. However, the AVL tree is implemented using a self-balancing binary search tree algorithm. The AVL tree is chosen because of its strict balancing property, which ensures that the tree maintains O(log n) height and guarantees efficient search, insertion, and deletion operations.

## 📚 Libraries Needed

`streamlit` (for creating the web app)

`graphviz` (for graph/tree structure visualization)

## 📊 Exploratory Data Analysis Results
No exploratory data analysis (EDA) is required for this project, as it does not involve any datasets. However, images of the AVL tree visualizations generated in real-time are included to demonstrate the app's functionality.

## 📈 Performance of the Models based on the Accuracy Scores
As this project does not involve machine learning models, there are no accuracy scores to report. The correctness of the AVL tree is demonstrated through the accurate representation of tree structure and balanced height after each insertion, along with correct traversal results.

## 📢 Conclusion
The AVL Tree Visualizer provides an effective and interactive way to understand the construction and balancing of AVL trees. Through real-time visualization, users can easily follow the operations performed on the tree, including rotations and height balancing, making it an excellent tool for learning AVL trees.

## ✒️ Your Signature
Anirudh P S

LinkedIn: www.linkedin.com/in/anirudh248
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
23 79 74 80 11 43 54 17 51 28 24 95 39 63 20 38 34 64 87 50 96 71 33 12 7
221 changes: 221 additions & 0 deletions Algorithms_and_Data_Structures/avl_tree_visualizer/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import streamlit as st
from graphviz import Digraph

class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
self.height = 1 # Every new node starts with height 1

def height(node):
if node is None:
return 0
return node.height

def max(a, b):
return a if a > b else b

def newNode(data):
return Node(data)

def getBalance(node):
if node is None:
return 0
return height(node.left) - height(node.right)

# Right rotation for Left-Left case (balance > 1 and data < node.left.data)
def rightRotate(y):
x = y.left
T2 = x.right

# Perform rotation
x.right = y
y.left = T2

# Update heights of the rotated nodes
y.height = max(height(y.left), height(y.right)) + 1
x.height = max(height(x.left), height(x.right)) + 1

return x

# Left rotation for Right-Right case (balance < -1 and data > node.right.data)
def leftRotate(x):
y = x.right
T2 = y.left

# Perform rotation
y.left = x
x.right = T2

# Update heights of the rotated nodes
x.height = max(height(x.left), height(x.right)) + 1
y.height = max(height(y.left), height(y.right)) + 1

return y

# Insert a node and balance the AVL tree
def insert(node, data):
# Standard BST insertion
if node is None:
return newNode(data)

if data < node.data:
node.left = insert(node.left, data)
elif data > node.data:
node.right = insert(node.right, data)
else:
# No duplicates allowed
return node

# Update height of the current node after insertion
node.height = 1 + max(height(node.left), height(node.right))

# Get balance factor to check for imbalance
balance = getBalance(node)

# Left-Left case: single right rotation
if balance > 1 and data < node.left.data:
return rightRotate(node)

# Right-Right case: single left rotation
if balance < -1 and data > node.right.data:
return leftRotate(node)

# Left-Right case: first left-rotate the left child, then right-rotate
if balance > 1 and data > node.left.data:
node.left = leftRotate(node.left)
return rightRotate(node)

# Right-Left case: first right-rotate the right child, then left-rotate
if balance < -1 and data < node.right.data:
node.right = rightRotate(node.right)
return leftRotate(node)

# Return the (potentially rotated) root node
return node

def inOrder(root, ls):
if root is not None:
inOrder(root.left, ls)
ls.append(root.data)
inOrder(root.right, ls)

def preOrder(root, ls):
if root is not None:
ls.append(root.data)
preOrder(root.left, ls)
preOrder(root.right, ls)

# Visualizing the tree using Graphviz
def visualize_tree(node, graph=None):
if graph is None:
graph = Digraph() # Create new Digraph object
graph.attr('node', shape='circle')

if node is not None:
# Add the current node to the graph
graph.node(str(node.data), str(node.data))

# Recursively add left and right children
if node.left:
graph.edge(str(node.data), str(node.left.data)) # Add edge between parent and left child
visualize_tree(node.left, graph)
if node.right:
graph.edge(str(node.data), str(node.right.data)) # Add edge between parent and right child
visualize_tree(node.right, graph)

return graph # Return the graph

def custom_write(ls):
return ', '.join(f'{item}' for item in ls)

st.title("AVL Tree Visualizer")

# Tabs for file upload and manual entry
tab1, tab2 = st.tabs(["File Upload", "Manual Entry"])

with tab1:
st.subheader("Upload AVL Tree Data")
uploaded_file = st.file_uploader("Upload the input file", type="txt")

if uploaded_file is not None:
content = uploaded_file.read().decode("utf-8") # Read file content as text
numbers = [int(num) for num in content.strip().split() if num.isdigit()] # Extract numbers

root = None # Initialize the AVL tree root
steps = [] # Store each step of the tree construction

# Insert each number and visualize the tree after each insertion
for key in numbers:
root = insert(root, key)
current_graph = visualize_tree(root, Digraph()) # Capture tree structure at this step
steps.append(current_graph.source) # Save graph source

ino = [] # Store inorder traversal result
pre = [] # Store preorder traversal result

inOrder(root, ino)
preOrder(root, pre)

st.subheader("Construction of AVL tree:")
if steps:
# Slider to navigate through different tree construction steps
step_index = st.slider('Step', 0, len(steps)-1, 0)
st.graphviz_chart(steps[step_index])

st.subheader("Inorder Traversal of the AVL tree:")
st.write(custom_write(ino))

st.subheader("Preorder Traversal of the AVL tree:")
st.write(custom_write(pre))

st.subheader("Final AVL Tree Structure:")
final_graph = visualize_tree(root, Digraph()) # Final tree structure
st.graphviz_chart(final_graph.source)

# Write inorder traversal to output file
output_file = 'output.txt'
try:
with open(output_file, 'w') as f:
for item in ino:
f.write(str(item)+' ')
except FileNotFoundError:
st.error(f"Error: File '{output_file}' does not exist.")

with tab2:
st.subheader("Enter AVL Tree Data")

# Initialize session state for tree root and numbers
if 'root' not in st.session_state:
st.session_state.root = None
st.session_state.numbers = []

input_number = st.text_input("Enter a number:")
if st.button("Add Number"):
if input_number.isdigit():
number = int(input_number)
# Insert the number into the tree and update session state
st.session_state.root = insert(st.session_state.root, number)
st.session_state.numbers.append(number)
st.success(f"Number {number} added.")
else:
st.error("Please enter a valid number.")

# If the tree has been built, show its structure
if st.session_state.root:
ino = [] # Inorder traversal result
pre = [] # Preorder traversal result

inOrder(st.session_state.root, ino)
preOrder(st.session_state.root, pre)

st.subheader("AVL Tree Structure:")
final_graph = visualize_tree(st.session_state.root, Digraph()) # Display tree structure
st.graphviz_chart(final_graph.source)

st.subheader("Inorder Traversal of the AVL tree:")
st.write(custom_write(ino))

st.subheader("Preorder Traversal of the AVL tree:")
st.write(custom_write(pre))
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
7 11 12 17 20 23 24 28 33 34 38 39 43 50 51 54 63 64 71 74 79 80 87 95 96
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
graphviz
streamlit

0 comments on commit 9a2eb3e

Please sign in to comment.