Skip to main content

All DSA Problems

Updated 12 min readMarkdown

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 to table match anything in my current_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.

No comments yet. Be the first to comment.