All DSA Problems
Find the element with the highest repeats in an array
# Online Python compiler (interpreter) to run Python online
maxFre = 0
maxEle = 0
arr = [5, 6, 5, 6, 9, 6]
map = {}
# iterate through arr and populate map {} tp track how many times something was repeated
for num in arr:
if num in map: # if already exists
map[num] += 1
print(map) #optional testing
else:
map[num] = 1
print(map) #optional testing
# for each key of hashmap, get the one with max value
for ele, fre in map.items():
if fre > maxFre:
maxFre = fre
maxEle = ele
elif fre == maxFre:
maxEle = min(ele, maxEle)
print("higest", maxFre, maxEle)
Find Highest Value
# Online Python compiler (interpreter) to run Python online
maxFre = 0
arr = [5, 6, 5, 6, 9, 6]
map = {}
for i in arr:
if i>maxFre:
maxFre = i
else:
continue
print("higest", maxFre)
# Output: 9
find the nth highest appearing element
# Online Python compiler (interpreter) to run Python online
maxFre = 0
maxEle = 0
arr = [5, 6, 5, 6, 9, 6]
map = {}
for num in arr:
if num in map:
map[num] +=1
else:
map[num] = 1
# we will get third smallest number
narr=[0]*3
for val in map.values():
for i in range(len(narr)):
if val > narr[i]:
narr.insert(i, val)
narr.pop()
break
# now getting the third smallest appearence's key
if narr[-1]== 0:
print([])
else:
print([k for k,v in map.items() if v == narr[-1]])
print("higest", map, narr)
sum of two digits
def sumofdigits(n):
if n == 0:
return 0
else:
return (n%10) + sumofdigits(int(n//10))
print(sumofdigits(54))
print(5%10)
#threesum
# Function to find all unique triplets [x,y,z] such that x+y+z = 0
def f(nums):
nums.sort() # ✅ Sort the array so we can use two-pointer technique
res = [] # ✅ Store the valid triplets here
n = len(nums)
# ✅ Loop until the 3rd-last element (since we need triplets)
for i in range(n-2):
# ✅ Skip duplicate 'i' values to avoid repeated triplets
if i > 0 and nums[i] == nums[i-1]:
continue
target = -nums[i] # ✅ We want nums[left] + nums[right] = -nums[i]
left, right = i+1, n-1 # ✅ Two-pointer setup: one from left, one from right
# ✅ Keep checking while left pointer is before right pointer
while left < right:
s = nums[left] + nums[right] # ✅ Sum of two-pointer elements
if s == target:
# ✅ Found a valid triplet
res.append([nums[i], nums[left], nums[right]])
left += 1
right -= 1
# ✅ Skip duplicate 'left' values
while left < right and nums[left] == nums[left-1]:
left += 1
# ✅ Skip duplicate 'right' values
while left < right and nums[right] == nums[right+1]:
right -= 1
elif s < target:
# ✅ Sum too small, move left pointer right to increase it
left += 1
else:
# ✅ Sum too large, move right pointer left to decrease it
right -= 1
return res
# Test the function
print(f([-1, 0, 1, 2, -1, -4]))
# Expected Output: [[-1, -1, 2], [-1, 0, 1]]
simpler version for counting dups
def f(arr):
map={}
for num in arr:
if num in map.keys():
map[num] +=1
else:
map[num] = 1
return map
print(f([5, 6, 5, 6, 9, 6]))
Graph of deep clone
#
class GraphNode:
def __init__(self, val):
self.val=val
self.neighbours=[]
def graph_deep_copy(node: GraphNode) -> GraphNode:
if not node:
return none
return dfs(node)
def dfs(node: GraphNode, clone_map={}) -> GraphNode:
if node in clone_map:
return clone_map[node] #returns the cloned node, not original
cloned_node=GraphNode(node.val) #cloning first node and then more
clone_map[node]=cloned_node # original node and cloned node
for i in node.neighbours:
cloned_neighbours=dfs(i, clone_map)
cloned_node.neighbours.append(cloned_neighbours)
return cloned_node
n1 = GraphNode(1)
n2 = GraphNode(2)
n3 = GraphNode(3)
n4 = GraphNode(4)
n1.neighbours = [n2, n4]
n2.neighbours = [n1, n3]
n3.neighbours = [n2, n4]
n4.neighbours = [n1, n3]
# Clone it
cloned = graph_deep_copy(n1)
# Print to test
print("Original node value:", n1.val)
print("Cloned node value:", cloned.val)
print("Original neighbours of 1:", [n.val for n in n1.neighbours])
print("Cloned neighbours of 1:", [n.val for n in cloned.neighbours])
# Check they are different objects
print("Original == Cloned ?", n1 is cloned)
Graph Clone and Traversing through graphclone
# Online Python compiler (interpreter) to run Python online.
# Write Python 3 code in this online editor and run it.
# Online Python compiler (interpreter) to run Python online.
# Write Python 3 code in this online editor and run it.
class GraphNode:
def __init__(self,val):
self.val=val
self.neighbours=[]
def graph_clone(node:GraphNode) -> GraphNode:
if not node:
return none
return dfs(node)
def dfs(node:GraphNode, clone_map={})-> GraphNode:
if node in clone_map:
return clone_map[node]
cloned_node=GraphNode(node.val)
clone_map[node]=cloned_node #logging that it has been cloned
for nei in node.neighbours:
cloned_neighbours=dfs(nei, clone_map)
cloned_node.neighbours.append(cloned_neighbours)
return cloned_node
def dfs_traverse(node: GraphNode, visited=set()) -> GraphNode:
#if visited is None:
#visited = set()
if node in visited:
return
visited.add(node)
print(f"{node.val} --> {[n.val for n in node.neighbours]}")
for nei in node.neighbours:
dfs_traverse(nei, visited)
n1=GraphNode(1)
n2=GraphNode(2)
n3=GraphNode(3)
n4=GraphNode(4)
n1.neighbours=[n2,n4]
n2.neighbours=[n1,n3]
n3.neighbours=[n2,n4]
n4.neighbours=[n1,n3]
cloned_nodes=graph_clone(n1)
print("cloned node------------")
dfs_traverse(cloned_nodes)
Deep Clone with better explanation
# Online Python compiler (interpreter) to run Python online.
# Write Pytclasshon 3 code in this online editor and run it.
#create a graph class
#clone a graph
#traverse the graph
#create nodes and use clone graph algo
class GraphNode:
def __init__(self, val):
self.val=val
self.neighbours=[]
def graph_clone(node:GraphNode)->GraphNode:
if not node:
return none
return dfs(node)
def dfs(node:GraphNode, clone_Map={})->GraphNode:
if node in clone_Map:
return clone_Map[node] # this will return the clone
cloned_node=GraphNode(node.val)
clone_Map[node]=cloned_node
for nei in node.neighbours:
cloned_neighbours=dfs(nei, clone_Map)
cloned_node.neighbours.append(cloned_neighbours)
return cloned_node
# check hashmap, process node, add to hashmap, dfs node's neighbours, cloned node neighbours list appending
def dfs_traverse(node:GraphNode, visited=set()):
if node in visited:
return
visited.add(node)
print(f"{node.val} --> {[n.val for n in node.neighbours]}")
for nei in node.neighbours:
dfs_traverse(nei, visited)
#check hashmap/set, add to hashmap, print this node and neighbours, dfs node.neighbours
n1=GraphNode(1)
n2=GraphNode(2)
n3=GraphNode(3)
n4=GraphNode(4)
n1.neighbours=[n2,n4]
n2.neighbours=[n1,n3]
n3.neighbours=[n2,n4]
n4.neighbours=[n1,n3]
cloned=graph_clone(n1)
dfs_traverse(cloned)
Count Island
lets solve this problem from scratch
-- thse are the steps we will take
#start at 0,0
#if 0,0==1, call dfs, then -1, count+=1
#0<=row<len(matrix) and 0<=col<len(matrix[0])
#dfs dirs=[(-1,0), (1,0), (0,-1), (0, 1)]
matrix=[[1,1,0,0],[1,1,0,0],[0,0,1,1],[0,0,0,1]]
def count_island(matrix:list[list[int]]) -> int:
if not matrix:
return 0
print(matrix[0][0]) # this should print 1
print(count_island(matrix))
#now the issue is, we want to check all the values of that matrix dynamically
#if matrix[r][c]==1, then we want to get into another maze, but not so scary jungle
#to check up, down, left and right values of that 1. we use dfs(r,c,matrix) function
#then we will have to increate count=0 by 1 as we had found matrix[0][0] or matrix[r][c] ==1
#now further break down the code for
## we want to check all the values of that matrix dynamically
def count_island(matrix:list[list[int]]) -> int:
if not matrix:
return 0
count=0
for r in range(len(matrix)):
for c in range(len(matrix[0])):
if matrix[r][c]==1:
dfs(r,c,matrix)
count+=1
return count
# do you see in this code, we are going into a dfs, that is our cue to use dirs
#dirs=[(-1,0), (1,0), (0,-1), (0, 1)]
def dfs(r:int, c:int, matrix:list[list[int]]):
matrix[r][c]=-1 #mark current as visited
dirs=[(-1,0),(1,0),(0,-1),(0,1)] #recursively call dfs on each neighbouring cells
for d in dirs:
next_r, next_c=r+d[0], c+d[1]
if (is_within_matrix(nexy_r,next_c,matrix)
and matrix[next_r][next_c]==1):
dfs(next_r,next_c,matrix)
Lets look at the full solution
matrix=[[1,1,0,0],[1,1,0,0],[0,0,1,1],[0,0,0,1]]
#start at 0,0
#if 0,0==1, call dfs, then -1, count+=1
#0<=row<len(matrix) and 0<=col<len(matrix[0])
#dfs dirs=[(-1,0), (1,0), (0,-1), (0, 1)]
def count_island(matrix:list[list[int]]) -> int:
if not matrix:
return 0
count=0
for r in range(len(matrix)):
for c in range(len(matrix[0])):
if matrix[r][c]==1: # this is where I usually made mistake.. forgot if statement
# if a land cell is found, we will explore the surrounding
dfs(r,c,matrix)
count+=1
return count
def dfs(r:int, c:int, matrix:list[list[int]])-> None:
matrix[r][c]=-1 #mark current as visited
dirs=[(-1,0),(1,0),(0,-1),(0,1)] #recursively call dfs on each neighbouring cells
for d in dirs:
next_r, next_c=r+d[0], c+d[1] #d[0]=-1, d[1]=0 for the first iteration of d in dirs
if (is_within_matrix(next_r,next_c,matrix)
and matrix[next_r][next_c]==1):
dfs(next_r,next_c,matrix)
#for matrix[0][0], it does not go up because of is_within_matrix
#because for matrix[0][0], if we do matrix[0-1][0+0]= it becomes matrix[-1][0] so it is wrong.
#the is_within_matrix indicates clearly that this is not within matrix
#matrix[-1]<matrix[0] so false also matrix[0][-1] is also because no column before 0.
def is_within_matrix(r:int,c:int,matrix:list[list[int]]) -> bool:
return 0<=r<len(matrix) and 0<=c<len(matrix[0])
#the is_within_matrix indicates clearly that this is not within matrix
#matrix[-1]<matrix[0] false also matrix[0][-1] false because no column before 0.
print(count_island(matrix))
Few core things to remember for this count_island
# you need four 'must need' things:
# - count island func to start at matrix[0][0] aka first iter of matrix[r][c]
# - dfs function to mark -1 and dirs,dfs(next_r, next_c,matrix)
# - dirs=[(-1,0),(1,0),(0,-1),(0,1)]
# - check is within bounds 0<=r<len(matrix) and 0<=c<len(matrix[0])
Data Lineage Edge Traversal
# Our lineage edges — each record shows data flowing FROM -> TO
all_edges = [
{"from": "Blob_CSV1", "to": "Staging_tbl1"},
{"from": "raw.patients", "to": "ODS_tbl1"},
{"from": "Staging_tbl1", "to": "ODS_tbl1"},
{"from": "ODS_tbl1", "to": "Gold_tbl1"},
{"from": "Gold_tbl1", "to": "dbo.Patients"},
]
def find_related_edges(target):
# 'related' will store all edges that eventually lead to the target
related = []
# 'visited' keeps track of (src, tgt) pairs already processed
# so we don't process the same lineage path twice (avoids loops)
visited = set()
# 'current_targets' holds the list of tables we’re currently exploring upstream for
current_targets = {target}
# Level counter (for debugging / understanding BFS layers)
level = 1
# BFS-style traversal: keep going until no more upstream sources are found
while current_targets:
print(f"\n🔹 Level {level}: Looking for parents of {current_targets}")
# Collects all unique upstream nodes (sources) found at this level
new_targets = set()
# Loop through every edge to see if any 'to' field matches our current targets
for e in all_edges:
src, tgt = e["from"], e["to"]
# If this edge feeds into one of our current targets, and we haven’t visited it before
if tgt in current_targets and (src, tgt) not in visited:
# Debug print for visibility
print(f"✅ Found: {src} → {tgt}")
# Add this lineage relationship to results
related.append(e)
# Add this upstream source to our next-level exploration
new_targets.add(src)
# Mark this edge as visited so we don’t loop back to it again
visited.add((src, tgt))
# At this point, we’ve finished finding all parents for this level.
# We now move one step "upstream" by making those sources our new targets.
current_targets = new_targets
# Increase the level number for debugging clarity
level += 1
# Return all discovered lineage edges connected (directly or indirectly) to the target
return related
# Example run: start from dbo.Patients and traverse upstream
print(find_related_edges("dbo.Patients"))
The code above handle all edge cases, even the ones with double parents because both parents would be in new targets and therefore would become current targets. in the for loop, we are reading through every edge that exists in all_edges and comparing tgt of every edge to see if that matches any value of current targets set. This continue until for loop has finished reading through all edges in the all_edges. If both parents have further parents, the for loop will add them in new targets. These new_target values reset everytime it starts while loop because it was first declared inside the while loop.
“Ask the target: who’s your parent?”
Then ask those parents: “Who are your parents?”
Keep doing that until no one has parents left.
Start fresh each time — no memory of old answers (we reset
new_targets).
declared it within while loop...also I believe language could be even more simple for second language english speakers
ChatGPT said:
🧠 Super-Simple, Clear Version of Your Note
This code works for all cases —
even when one table has two parents.
That’s because both parents are added into new_targets.
Next time the loop runs, those parents become the new current_targets.
Inside the for loop, the code looks at every edge in all_edges.
For each edge, it asks:
“Does this edge’s
totable match anything in mycurrent_targets?”
If yes — that means we found a parent!
We then take the parent (from table) and put it into new_targets.
After checking all edges, the while loop runs again.
Now, the new_targets we found become the next current_targets.
👉 Every time the while loop starts,
we write a new line:
new_targets = set()
This line creates a new empty set for new_targets.
Because it’s written inside the loop,
it resets every time before the next search begins.
This way, the code only keeps the new results found in that round
and forgets the old ones.
That keeps the search clean and moves step by step upstream
until there are no more parents left.
# SAME SOLUTION IN DFS
all_edges = [
{"from": "Blob_CSV1", "to": "Staging_tbl1"},
{"from": "raw.patients", "to": "ODS_tbl1"},
{"from": "Staging_tbl1", "to": "ODS_tbl1"},
{"from": "ODS_tbl1", "to": "Gold_tbl1"},
{"from": "Gold_tbl1", "to": "dbo.Patients"},
]
def find_related(target):
related=[] # output list
stack=[target]
visited=set() #NOT MAKRING VISITED BECAUSE target IS INDIVIDUAL***
while stack:
ct=stack.pop()
for e in all_edges:
src,trg=e["from"],e["to"]
if trg == ct and (src,trg) not in visited:
visited.add((src,trg))
stack.append(src)
related.append((src,trg))
return related
print(find_related("dbo.Patients"))
***You’re exploring edges — each (src, trg) is an edge,
and you’re saying “find all sources that lead to my current target.”
So here, “visited” doesn’t mean “I’ve seen this node before,”
it means “I’ve already explored this connection (edge).”
Stack and Queue
DFS = Stack, Lifo, stack.pop()
BFS = queue, Fifo, leftpop()
Five important things needed:
stack/queue » stack = [start] / from collections import deque queue = deque([start])
visited={start} / marker
Ajd list or matrix
Mark visited before stack.append/queue.append
Mark Twice, Mark the moment you discover it
class GraphNode:
def __init__(self,val):
self.val=val
self.neighbors=[]
def travarse(node:GraphNode):
stack = [node]
visited = {node} # mark first time
while stack:
node = stack.pop()
print(f"{node.val}-->>{[n.val for n in node.neighbors]}") # pop first
for nei in node.neighbors:
if nei not in visited:
visited.add(nei) # mark second time
stack.append(nei) # neighbours will be pushed only once
Only real difference in bfs and dfs is popleft() vs pop(), I mean fifo vs lifo
graph = {
1: [2, 4],
2: [3],
3: [4],
4: []
}
# FIFO/BFS >> First in First Out
from collections import deque
q = deque([1])
visited = {1}
print("BFS order:")
while q:
u = q.popleft() #POP from the left -- fifo
print(u)
for v in graph[u]:
if v not in visited:
visited.add(v)
q.append(v)
# LIFO/DFS >> Last in First Out
stack = [1] # LIFO stack — DFS style
visited = {1} # keep track of what we’ve seen
print("DFS order:")
while stack:
node = stack.pop() # pop from RIGHT (last item added)
print(node)
for nei in graph[node]:
if nei not in visited:
visited.add(nei)
stack.append(nei) # push neighbour onto stack
Queue and BFS:
aadjacency_list = {
'A': ['B', 'F'],
'B': ['A', 'F', 'C'],
'C': ['B', 'E', 'D'],
'D': ['C', 'E'],
'E': ['D', 'C', 'F'],
'F': ['A', 'B', 'E']
}
from collections import deque
def travBFS(graph, start):
output=[]
q=deque([start])
visited={start}
while q:
item=q.popleft()
output.append(item)
for nei in graph[item]:
if nei not in visited:
visited.add(nei)
q.append(nei)
return output
def trv_dfs(matrix, start):
output=[]
stack=[start]
visited={start}
while stack:
node=stack.pop()
output.append(node)
for nei in matrix[node]:
if nei not in visited:
visited.add(nei)
stack.append(nei)
return output
print(travBFS(adjacency_list, 'A'))
About the author
A data analytics engineer with four years of experience working as a data engineer. Holds a MSc in Data.
Comments
Join the discussionNo comments yet. Be the first to comment.