Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Talib1996 patch 3 #459

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
156 changes: 156 additions & 0 deletions DSA/Python/BinarySearchTreeImplementation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
class Node:
def __init__(self, data):
"""Initialize a BST node with data and null left/right pointers"""
self.data = data
self.left = None
self.right = None

class BinarySearchTree:
def __init__(self):
"""Initialize an empty BST"""
self.root = None

def insert(self, data):
"""Insert a new node with given data"""
if not self.root:
self.root = Node(data)
else:
self._insert_recursive(self.root, data)

def _insert_recursive(self, node, data):
"""Helper method for recursive insertion"""
if data < node.data:
if node.left is None:
node.left = Node(data)
else:
self._insert_recursive(node.left, data)
else:
if node.right is None:
node.right = Node(data)
else:
self._insert_recursive(node.right, data)

def search(self, data):
"""Search for a value in the BST"""
return self._search_recursive(self.root, data)

def _search_recursive(self, node, data):
"""Helper method for recursive search"""
if node is None or node.data == data:
return node

if data < node.data:
return self._search_recursive(node.left, data)
return self._search_recursive(node.right, data)

def delete(self, data):
"""Delete a node with given data"""
self.root = self._delete_recursive(self.root, data)

def _delete_recursive(self, node, data):
"""Helper method for recursive deletion"""
if node is None:
return node

# Find the node to delete
if data < node.data:
node.left = self._delete_recursive(node.left, data)
elif data > node.data:
node.right = self._delete_recursive(node.right, data)
else:
# Node with only one child or no child
if node.left is None:
return node.right
elif node.right is None:
return node.left

# Node with two children
# Get the inorder successor (smallest in the right subtree)
temp = self._min_value_node(node.right)
node.data = temp.data
node.right = self._delete_recursive(node.right, temp.data)

return node

def _min_value_node(self, node):
"""Helper method to find the minimum value node in a subtree"""
current = node
while current.left:
current = current.left
return current

# Tree Traversal Methods
def inorder(self):
"""Inorder traversal: Left -> Root -> Right"""
result = []
self._inorder_recursive(self.root, result)
return result

def _inorder_recursive(self, node, result):
if node:
self._inorder_recursive(node.left, result)
result.append(node.data)
self._inorder_recursive(node.right, result)

def preorder(self):
"""Preorder traversal: Root -> Left -> Right"""
result = []
self._preorder_recursive(self.root, result)
return result

def _preorder_recursive(self, node, result):
if node:
result.append(node.data)
self._preorder_recursive(node.left, result)
self._preorder_recursive(node.right, result)

def postorder(self):
"""Postorder traversal: Left -> Right -> Root"""
result = []
self._postorder_recursive(self.root, result)
return result

def _postorder_recursive(self, node, result):
if node:
self._postorder_recursive(node.left, result)
self._postorder_recursive(node.right, result)
result.append(node.data)

def height(self):
"""Get the height of the tree"""
return self._height_recursive(self.root)

def _height_recursive(self, node):
if not node:
return 0
left_height = self._height_recursive(node.left)
right_height = self._height_recursive(node.right)
return max(left_height, right_height) + 1

# Create a new BST
bst = BinarySearchTree()

# Insert some values
bst.insert(50)
bst.insert(30)
bst.insert(70)
bst.insert(20)
bst.insert(40)
bst.insert(60)
bst.insert(80)

# Different tree traversals
print("Inorder traversal:", bst.inorder()) # [20, 30, 40, 50, 60, 70, 80]
print("Preorder traversal:", bst.preorder()) # [50, 30, 20, 40, 70, 60, 80]
print("Postorder traversal:", bst.postorder()) # [20, 40, 30, 60, 80, 70, 50]

# Search for a value
node = bst.search(30)
print("Found:", node.data if node else "Not found") # Found: 30

# Get tree height
print("Tree height:", bst.height()) # 3

# Delete a node
bst.delete(30)
print("After deletion:", bst.inorder()) # [20, 40, 50, 60, 70, 80]
207 changes: 207 additions & 0 deletions DSA/Python/GraphImplementationwithCommonAlgorithms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
from collections import defaultdict, deque
import heapq

class Graph:
def __init__(self, directed=False):
"""Initialize a graph with optional directed edges"""
self.graph = defaultdict(list)
self.directed = directed
self.vertices = set()

def add_vertex(self, vertex):
"""Add a vertex to the graph"""
self.vertices.add(vertex)

def add_edge(self, u, v, weight=1):
"""Add an edge between vertices u and v with optional weight"""
# Add vertices if they don't exist
self.vertices.add(u)
self.vertices.add(v)

# Add edge u -> v
self.graph[u].append((v, weight))

# If undirected, add edge v -> u
if not self.directed:
self.graph[v].append((u, weight))

def get_neighbors(self, vertex):
"""Get all neighbors of a vertex"""
return [v for v, _ in self.graph[vertex]]

def bfs(self, start_vertex):
"""Breadth-First Search traversal"""
if start_vertex not in self.vertices:
return []

visited = set()
traversal = []
queue = deque([start_vertex])
visited.add(start_vertex)

while queue:
vertex = queue.popleft()
traversal.append(vertex)

for neighbor, _ in self.graph[vertex]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)

return traversal

def dfs(self, start_vertex):
"""Depth-First Search traversal"""
if start_vertex not in self.vertices:
return []

visited = set()
traversal = []

def dfs_recursive(vertex):
visited.add(vertex)
traversal.append(vertex)

for neighbor, _ in self.graph[vertex]:
if neighbor not in visited:
dfs_recursive(neighbor)

dfs_recursive(start_vertex)
return traversal

def dijkstra(self, start_vertex):
"""Dijkstra's shortest path algorithm"""
if start_vertex not in self.vertices:
return {}

# Initialize distances
distances = {vertex: float('inf') for vertex in self.vertices}
distances[start_vertex] = 0

# Priority queue to store vertices and their distances
pq = [(0, start_vertex)]

# Dictionary to store the shortest path to each vertex
previous = {vertex: None for vertex in self.vertices}

while pq:
current_distance, current_vertex = heapq.heappop(pq)

# If we've found a longer path, skip
if current_distance > distances[current_vertex]:
continue

# Check all neighbors
for neighbor, weight in self.graph[current_vertex]:
distance = current_distance + weight

# If we've found a shorter path, update it
if distance < distances[neighbor]:
distances[neighbor] = distance
previous[neighbor] = current_vertex
heapq.heappush(pq, (distance, neighbor))

return distances, previous

def get_shortest_path(self, start_vertex, end_vertex):
"""Get the shortest path between two vertices using Dijkstra's algorithm"""
if start_vertex not in self.vertices or end_vertex not in self.vertices:
return None, None

distances, previous = self.dijkstra(start_vertex)

if distances[end_vertex] == float('inf'):
return None, None

# Reconstruct path
path = []
current_vertex = end_vertex

while current_vertex is not None:
path.append(current_vertex)
current_vertex = previous[current_vertex]

path.reverse()
return path, distances[end_vertex]

def has_cycle(self):
"""Detect if the graph has a cycle"""
visited = set()
rec_stack = set()

def dfs_cycle(vertex):
visited.add(vertex)
rec_stack.add(vertex)

for neighbor, _ in self.graph[vertex]:
if neighbor not in visited:
if dfs_cycle(neighbor):
return True
elif neighbor in rec_stack:
return True

rec_stack.remove(vertex)
return False

for vertex in self.vertices:
if vertex not in visited:
if dfs_cycle(vertex):
return True

return False

def topological_sort(self):
"""Perform topological sort (only for directed acyclic graphs)"""
if not self.directed or self.has_cycle():
return None

visited = set()
stack = []

def dfs_topological(vertex):
visited.add(vertex)

for neighbor, _ in self.graph[vertex]:
if neighbor not in visited:
dfs_topological(neighbor)

stack.append(vertex)

for vertex in self.vertices:
if vertex not in visited:
dfs_topological(vertex)

return stack[::-1]



# Create a new undirected graph
graph = Graph(directed=False)

# Add edges (automatically adds vertices)
graph.add_edge('A', 'B', 4)
graph.add_edge('A', 'C', 2)
graph.add_edge('B', 'C', 1)
graph.add_edge('B', 'D', 5)
graph.add_edge('C', 'D', 8)
graph.add_edge('C', 'E', 10)
graph.add_edge('D', 'E', 2)

# Perform BFS traversal starting from vertex 'A'
print("BFS traversal:", graph.bfs('A')) # ['A', 'B', 'C', 'D', 'E']

# Perform DFS traversal starting from vertex 'A'
print("DFS traversal:", graph.dfs('A')) # ['A', 'B', 'D', 'E', 'C']

# Find shortest path from 'A' to 'E'
path, distance = graph.get_shortest_path('A', 'E')
print(f"Shortest path from A to E: {path}") # ['A', 'B', 'D', 'E']
print(f"Distance: {distance}") # 11

# Create a directed graph for topological sort
dag = Graph(directed=True)
dag.add_edge('A', 'B')
dag.add_edge('B', 'C')
dag.add_edge('A', 'C')

print("Topological sort:", dag.topological_sort()) # ['A', 'B', 'C']
Loading