DSA With Python
DSA With Python
4. Homogeneous Vs Non-Homogeneous
Data Structures:
Homogeneous Data Structures:
Store data elements of the same data type. Examples: an array
of integers.
2. Operating Systems
Operating systems utilize data structures like queues for process
scheduling, stacks for managing function calls and memory
allocation, and linked lists for managing file systems and free
memory blocks.
3. Compiler Design
Compilers employ data structures such as symbol tables to store
information about variables and functions, abstract syntax trees
(ASTs) to represent the structure of source code, and hash tables
for efficient symbol lookup.
4. Computer Graphics
Data structures like trees (e.g., k-d trees, octrees) and graphs are
used to represent and manipulate geometric objects, manage
spatial data, and optimize rendering processes in computer
graphics applications.
5. Networking
Graphs are extensively used to model network topologies,
represent routing paths, and implement routing algorithms. Queues
are used for managing data packets in network communication.
7. Navigation Systems
Graphs are used to represent road networks, and algorithms like
Dijkstra's or A* search, often implemented with priority queues, are
used to find optimal routes.
8. File Systems
Data structures like trees (e.g., directory trees) and linked lists
(e.g., for file allocation tables) are used to organize and manage
files and directories on storage devices.
What is Array?
Array is a linear data structure where all elements are
arranged sequentially. It is a collection of elements of
same data type stored at contiguous memory locations.
arr = array('i',[10,20,30,40])
print(arr)
## add element
arr.append(50)
print('After append: ',arr)
## insert at index
arr.insert(1,15)
print('After insert: ',arr)
## update at index
arr[2] = 25
print('after update: ',arr)
## Remove element
arr.remove(15)
print('After remove 15: ',arr)
In Python
All Python lists are implemented as dynamic referential
arrays:
The list grows dynamically (Dynamic Array behavior)
Stores references to Python objects (Referential Array concept)
for num in L:
print(num, 'at Address: ',id(num))
1 at Address: 140705501639592
hello at Address: 2880318671584
2 at Address: 140705501639624
python at Address: 2880223635776
3.14 at Address: 2880320665968
True at Address: 140705500754352
In [33]: L = ['hello','python',10,20,30,3.4,True]
## Searching
print(L.index(30))
## Sorting
L1 = [10,44,32,11,65,71,17]
L1.sort()
print(L1)
## Indexing
print(L1[0])
print(L1[-1])
## Slicing
## reverse elements
print(L1[::-1])
def __len__(self):
return self.n
def __str__(self):
res = ''
for i in range(self.n):
res = res + str(self.A[i]) + ','
return '[' + res[:-1] + ']'
def append(self,item):
# first check array is full or not
if self.size == self.n:
# full h - resize array
self.__resize_array(self.size*2)
# jagah h
self.A[self.n] = item
self.n += 1
def pop(self):
if self.n == 0:
return 'List Empty'
print(self.A[self.n-1])
self.n -= 1
def __getitem__(self,index):
## slicing
if isinstance(index,slice):
start,stop,step=index.indices(self.n)
result = [self.A[i] for i in range(start,stop,step)]
return result
def __delitem__(self,pos):
if 0 <= pos< self.n:
## delete
for i in range(pos,self.n-1):
# shifting
self.A[i] = self.A[i+1]
self.n -= 1
# searching
def index(self,value):
for i in range(self.n):
if self.A[i]==value:
return i
return 'Not Found'
# sorting
def sort(self):
for i in range(self.n):
for j in range(0,self.n-i-1):
if self.A[j]>self.A[j+1]:
self.A[j],self.A[j+1] = self.A[j+1],self.A[j]
# insert
def insert(self,pos,value):
if self.size==self.n:
self.__resize_array(self.size*2)
## agar jagah h
for i in range(self.n,pos,-1):
# shifting
self.A[i] = self.A[i-1]
self.A[pos]=value
self.n +=1
# remove
def remove(self,value):
# find index position
pos = self.index(value)
if type(pos)==int:
# if index found
self.__delitem__(pos)
else:
# index not found
return pos
# extend
def extend(self,iterable):
for item in iterable:
if self.n == self.size:
self.__resize_array(self.size*2)
self.A[self.n] = item
self.n += 1
# merge
def merge(self,other):
merged = List()
# all elements from list 1
for i in range(self.n):
merged.append(self.A[i])
# all elements from list2
for i in range(len(other)):
merged.append(other[i])
return merged
# reverse
def reverse(self):
start = 0
end = self.n - 1
while start<end:
self.A[start],self.A[end] = self.A[end],self.A[start]
start += 1
end -= 1
return self
# min elements
def min(self):
min_val = self.A[0]
for i in range(self.n):
if self.A[i]<min_val:
min_val = self.A[i]
return min_val
# max elements
def max(self):
max_val = self.A[0]
for i in range(self.n):
if self.A[i]>max_val:
max_val = self.A[i]
return max_val
# sum of elements
def sum(self):
sum = 0
for i in range(self.n):
sum += self.A[i]
return sum
## advanced function
## 1.count elements frequency
def count(self,item):
count = 0
for i in range(self.n):
if self.A[i]==item:
count+=1
return count
def __make_array(self,capacity):
# create ctypes array with capacity
return (capacity*ctypes.py_object)()
def __resize_array(self,new_capacity):
# create an new array with double size
B = self.__make_array(new_capacity)
# update capacity
self.size = new_capacity
# re-assign B to A
self.A = B
In [199… L = List()
## append elements
L.append(71)
L.append(12)
L.append(33)
L.append(46)
L.append(25)
print('List: ',L)
print('Length: ',len(L))
print('Index of 4: ',L.index(4))
print('First Element: ',L[0])
print('Last Element: ',L[-1])
L.sort()
print('Sorted Array: ',L)
## remove
L.remove(44)
print('after remove: ',L)
## extend list
L.extend([115,116])
print('after extend: ',L)
## merged list
merged_array = L.merge([44,55,66])
print('merged array: ',merged_array)
## reverse list
print('reverse array: ',L.reverse())
L.remove_duplicates()
print('After Remove Duplicated: ',L)
## binary search
print('Binary Search element-116 : ',L.binary_search(115))
List: [71,12,33,46,25]
Length: 5
Index of 4: Not Found
First Element: 71
Last Element: 25
Sorted Array: [12,25,33,46,71]
after insert: [12,91,25,44,33,46,71]
after delete: [91,25,44,33,46,71]
after remove: [91,25,33,46,71]
after extend: [91,25,33,46,71,115,116]
merged array: [91,25,33,46,71,115,116,44,55,66]
reverse array: [116,115,71,46,33,25,91]
Min Elements: 25
Max Elements: 116
Sum of Elements: 497
Count 71: 3
Count 115: 2
Count 1158: 0
Duplicate Array: [116,115,71,46,33,25,91,116,115,71,71]
After Remove Duplicated: [116,115,71,46,33,25,91]
after sorted: <__main__.py_object_Array_16 object at 0x0000029EA1A35550>
Binary Search element-116 : 5
linear_search([2,5,7,11,19,15],15)
Out[1]: 5
2.Binary Search
In [6]: def binary_search(arr,item):
low=0
high = len(arr)-1
while low<=high:
mid=(low+high)//2
if arr[mid]==item:
return mid
if arr[mid]<item:
low = mid+1
else:
high = mid-1
return -1
binary_search([2,3,5,7,11],7)
Out[6]: 3
Sorting
1. Bubble Sort
Bubble Sort is the simplest sorting algorithm that works
by repeatedly swapping the adjacent elements if they
are in the wrong order. This algorithm is not suitable for
large data sets as its average and worst-case time
complexity are quite high.
Compare the first two values and swap if necessary. Then compare
the next pair of values and swap if necessary. This process is
repeated n-1 times, where n is the number of values being sorted.
The example above sorts 4 numbers into ascending numerical
order. As you can see, this requires 3 (n-1) passes to achieve since
there are 4 items of data. The bigger numbers can be seen to
bubble (or ripple) to the top
bubble_sort([21,81,12,78,89,97])
2. Selection Sort
Selection Sort is a comparison-based sorting algorithm.
It sorts an array by repeatedly selecting the smallest (or
largest) element from the unsorted portion and
swapping it with the first unsorted element. This
process continues until the entire array is sorted.
selection_sort([34,12,21,65,11])
return
def merge_sort(arr):
if len(arr)==1:
return arr
mid = len(arr)//2
left = arr[:mid]
right = arr[mid:]
merge_sort(left)
merge_sort(right)
merge_sorted_arrays(left,right,arr)
return arr
merge_sort([89,14,55,61,34])
Time Complexity:
Best Case: O(n log n), When the array is already sorted or nearly sorted.
Average Case: O(n log n), When the array is randomly ordered.
Worst Case: O(n log n), When the array is sorted in reverse order.
Disadvantages
Space complexity: Merge sort requires additional memory to store the
merged sub-arrays during the sorting process.
Not in-place: Merge sort is not an in-place sorting algorithm, which means
it requires additional memory to store the sorted data. This can be a
disadvantage in applications where memory usage is a concern.
Merge Sort is Slower than QuickSort in general as QuickSort is more cache
friendly because it works in-place.
4. Quick Sort
QuickSort is a sorting algorithm based on the Divide and
Conquer that picks an element as a pivot and partitions
the given array around the picked pivot by placing the
pivot in its correct position in the sorted array.
In [20]: def quick_sort(arr):
if len(arr)<1:
return arr
pivot = arr[len(arr)//2]
Auxiliary Space:
Worst-case scenario: O(n) due to unbalanced partitioning leading to a
skewed recursion tree requiring a call stack of size O(n).
Best-case scenario: O(log n) as a result of balanced partitioning leading to a
balanced recursion tree with a call stack of size O(log n
LinkedList
A linked list is a type of linear data structure similar to arrays. It is a
collection of nodes that are linked with each other. A node contains
two things first is data and second is a link that connects it with
another node. Below is an example of a linked list with four nodes
and each node contains character data and a link to another node.
Our first node is where head points and we can access all the
elements of the linked list using the head.
# Link Nodes
node1.next = node2
node2.next = node3
node3.next = node4
while current:
print(current.data,end='->')
current = current.next
print('None')
10->20->30->40->None
Types Of LinkedList
1. Singly LinkedList
A singly linked list is a fundamental data structure, it consists of
nodes where each node contains a data field and a reference to the
next node in the linked list. The next of the last node is null,
indicating the end of the list. Linked Lists support efficient insertion
and deletion operations.
2. Dobly LinkedList
A doubly linked list is a more complex data structure than a singly
linked list, but it offers several advantages. The main advantage of
a doubly linked list is that it allows for efficient traversal of the list
in both directions. This is because each node in the list contains a
pointer to the previous node and a pointer to the next node. This
allows for quick and easy insertion and deletion of nodes from the
list, as well as efficient traversal of the list in both directions.
3. Circular LinkedList
A circular linked list is a data structure where the last node points
back to the first node, forming a closed loop.
Structure: All nodes are connected in a circle, enabling continuous traversal
without encountering NULL.
Difference from Regular Linked List: In a regular linked list, the last node
points to NULL, whereas in a circular linked list, it points to the first node.
Uses: Ideal for tasks like scheduling and managing playlists, where smooth
and repeated.
In Circular Singly Linked List, each node has just one pointer called
the "next" pointer. The next pointer of the last node points back to
the first node and this results in forming a circle. In this type of
Linked list, we can only move through the list in one direction.
In circular doubly linked list, each node has two pointers prev and
next, similar to doubly linked list. The prev pointer points to the
previous node and the next points to the next node. Here, in
addition to the last node storing the address of the first node, the
first node will also store the address of the last node.
Advantages
Efficient Traversal
No Null Pointers/References
Useful for Repetitive Tasks
Insertion at Beginning or End is O(1)
Uniform Traversal
Efficient Memory Utilization
Disadvantages
Complex Implementation
Infinite Loop Risk
Harder to debug
Deletion Complexity
Memory Overhead (for Doubly Circular LL)
Not cache friendly
# create Nodes
n1 = Node(10)
n2 = Node(20)
n3 = Node(30)
n4 = Node(40)
# link Nodes
n1.prev = None
n1.next = n2
n2.prev = n1
n2.next = n3
n3.prev = n2
n3.next = n4
n4.prev = n3
n4.next = None
head = n1
current = head
while current:
print(current.data, end= '->')
current = current.next
print('None')
10->20->30->40->None
# create Nodes
n1 = Node(10)
n2 = Node(20)
n3 = Node(30)
n4 = Node(40)
# LinkedNodes
n1.next = n2
n2.next = n3
n3.next = n4
n4.next = n1
# traverse
head = n1
current = head
while True:
print(current.data,end='->')
current = current.next
if current== head:
break
print('None')
10->20->30->40->None
# create Nodes
n1 = Node(10)
n2= Node(20)
n3= Node(30)
n4 = Node(40)
# LinkedNodes
n1.prev = n4
n1.next = n2
n2.prev = n1
n2.next = n3
n3.prev = n2
n3.next = n4
n4.prev = n3
n4.next = n1
# traverse
head = n1
current = head
while True:
print(current.data,end='->')
current = current.next
if current==head:
break
print('None')
10->20->30->40->None
class LinkedList:
def __init__(self):
self.head = None
self.n = 0
def __len__(self):
return self.n
def __str__(self):
curr = self.head
res = ''
while curr:
res += str(curr.data) + ' -> '
curr = curr.next
return res + 'None'
def insert_head(self,item):
# create an Node
new_node = Node(item)
new_node.next = self.head
self.head = new_node
self.n += 1
def append(self,item):
new_node = Node(item)
curr = self.head
def insert_after(self,after,item):
new_node = Node(item)
curr = self.head
while curr.next!=None:
if curr.data == after:
break
curr = curr.next
new_node.next = curr.next
curr.next = new_node
self.n += 1
def delete_head(self):
if self.head == None:
return 'Empty LL'
self.head = self.head.next
self.n -= 1
def pop(self):
if self.head==None:
return 'Empty LL'
curr = self.head
while curr.next.next!=None:
curr = curr.next
curr.next = None
self.n -= 1
def remove(self,value):
if self.head.data == value:
self.delete_head()
return
curr = self.head
while curr.next!=None:
if curr.next.data == value:
break
curr = curr.next
if curr.next!=None:
curr.next = curr.next.next
self.n -= 1
else:
return 'Not found'
def search(self,value):
curr = self.head
pos = 0
while curr!=None:
if curr.data == value:
return pos
curr = curr.next
pos += 1
def reverse(self):
prev = None
curr = self.head
while curr!=None:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
self.head=prev
def min(self):
curr = self.head
min_val = self.head.data
while curr!=None:
if curr.data<min_val:
min_val = curr.data
curr = curr.next
return min_val
def max(self):
curr = self.head
max_val = self.head.data
while curr!=None:
if curr.data>max_val:
max_val = curr.data
curr = curr.next
return max_val
def __getitem__(self,index):
curr = self.head
pos = 0
while curr!=None:
if pos==index:
return curr.data
curr = curr.next
pos += 1
l = LinkedList()
l.insert_head(1)
l.append(2)
l.append(3)
l.append(4)
l.append(5)
print(l)
l.pop()
print(l)
l.delete_head()
print(l)
l.remove(4)
print(l)
# search
print(l.search(3))
# get index
print(l[0])
l.append(12)
l.append(43)
l.append(40)
print(l)
l.reverse()
print(l)
print('Min: ',l.min())
print('Max: ',l.max())
print('Length: ',len(l))
Stack
A stack is a linear data structure that follows the Last-In/First-Out
(LIFO) principle, also known as First-In/Last-Out (FILO). This means
that the last element added is the first one to be removed. In a
stack, both insertion and deletion happen at the same end, which is
called the top of the stack.
Stack Operations
Stacks support a small set of basic operations, all of which run in O
(1) time:
empty (): checks if the stack is empty
size (): returns the number of elements in the stack
top () / peek (): shows the top element without removing it
push(a): adds an element a at the top
pop (): removes the top element
class LStack:
def __init__(self):
self.top = None
def is_empty(self):
return self.top==None
def peek(self):
if (self.is_empty()):
return 'Empty Stack'
return self.top.data
def push(self,value):
new_node = Node(value)
new_node.next = self.top
self.top = new_node
def pop(self):
if (self.is_empty()):
return 'Empty Stack'
else:
data = self.top.data
self.top = self.top.next
return data
def traverse(self):
temp = self.top
while (temp!=None):
print(temp.data)
temp = temp.next
def size(self):
temp = self.top
counter = 0
while temp!=None:
counter+=1
temp=temp.next
return counter
In [68]: s = LStack()
s.push(1)
s.push(2)
s.push(3)
s.push(4)
s.traverse()
s.pop()
s.pop()
print('*'*20)
s.traverse()
print('peek:',s.peek())
print('size: ',s.size())
print('check empty: ',s.is_empty())
4
3
2
1
********************
2
1
peek: 2
size: 2
check empty: False
def is_empty(self):
return self.top == -1
def push(self,value):
if self.top == self.size -1:
return 'overflow'
else:
self.top += 1
self.stack[self.top]=value
def pop(self):
if self.top == -1:
return 'empty stack'
else:
data = self.stack[self.top]
self.top -= 1
return data
def peek(self):
if self.top == -1:
return 'empty stack'
return self.stack[self.top]
def traverse(self):
for i in range(self.top+1):
print(self.stack[i])
def __len__(self):
counter = 0
for i in range(self.top+1):
counter+=1
return counter
In [66]: s = AStack(3)
s.push(1)
s.push(2)
s.push(3)
s.traverse()
s.pop()
print('*'*20)
s.traverse()
print('peek:',s.peek())
print('size: ',len(s))
print('check empty: ',s.is_empty())
1
2
3
********************
1
2
peek: 2
size: 2
check empty: False
Stack Programs
Function Signature
def reverse_string(text: str) -> str:
Examples
Example 1:
Example 2:
Constraints
0 <= len(text) <= 10^5
text consists of printable ASCII characters.
Hint
Use a stack to push each character of the string.
Pop each character from the stack and append it to a new string.
The popped sequence will form the reversed string.
In [69]: def rev_string(text):
s = LStack()
for i in text:
s.push(i)
res = ''
while (not s.is_empty()):
res = res + s.pop()
return res
rev_string('hello moto')
📝 Description
Initially, the editor is given a string text .
You are also given a sequence of operations pattern , where each character
in the pattern can be:
'u' → Undo the last action (move one character from the undo stack to
the redo stack)
'r' → Redo the last undone action (move one character from the redo
stack back to the undo stack)
After performing all operations, return the final text present in the undo stack.
🔹 Function Signature
def text_editor(text: str, pattern: str) -> str:
⚙️Example
Input:
text = "abcd"
pattern = "uur"
Constraints
🧩 Hint
for i in text:
u.push(i)
for i in pattern:
if i == 'u':
data = u.pop()
r.push(data)
elif i == 'r':
data = r.pop()
u.push(data)
res = ''
while (not u.is_empty()):
res = u.pop() + res
return res
text_editor('hello moto','uuurrru')
1. Is known by everyone.
2. Knows no one.
Your task: Identify the celebrity (return their index) or state that no celebrity
exists.
Function Signature
def find_celeb(L: list[list[int]]) -> None:
python
L = [
[0, 1, 1],
[0, 0, 1],
[0, 0, 0]
]
Hint:
Use a stack to efficiently find the celebrity:
1. Push all indices (0 to n-1) onto the stack.
2. Pop two indices i and j :
If i knows j , i cannot be celebrity → push j back
Else, j cannot be celebrity → push i back
3. The last remaining person is a potential celebrity.
4. Verify:
They know no one
Everyone else knows them
Time Complexity reduces from O(n²) to O(n).
for i in range(len(L)):
s.push(i)
while s.size()>=2:
i = s.pop()
j = s.pop()
if L[i][j]==0:
# means - i not knows j - j not celebirity
s.push(i)
else:
# means j not knows i - i not celibrity
s.push(j)
celeb = s.pop()
for i in range(len(L)):
if i!=celeb:
if L[i][celeb] == 0 or L[celeb][i]==1:
print('No one is celebrity')
return
L1 = [
[0, 1, 1],
[0, 0, 1],
[0, 0, 0]
]
find_celeb(L1)
L2 = [
[0, 1, 0],
[0, 0, 1],
[1, 0, 0]
]
find_celeb(L2)
the celibrity is 2
No one is celebrity
Function Signature
def valid_parenthesis(text: str) -> bool:
Examples
Example 1:
Explanation: All opening brackets are closed properly in the correct order.
Example 2:
Example 3:
Constraints
1 <= len(text) <= 10^4
text consists only of '(' , ')' , '{' , '}' , '[' , ']' .
Hint
Use a stack to track opening brackets.
For every closing bracket, check if it matches the top of the stack.
If it matches, pop the stack; otherwise, the string is invalid.
After processing all characters, the stack should be empty for a valid string.
print(valid_parenthesis("()[]{}"))
print(valid_parenthesis("([)])"))
print(valid_parenthesis("{[]}"))
True
False
True
Queue in Python
Queue is a linear data structure that stores items in a
First In First Out (FIFO) manner. The item that is added
first will be removed first. Queues are widely used in
real-life scenarios, like ticket booking, or CPU task
scheduling, where first-come, first-served rule is
followed.
Operations associated with queue are:
Enqueue: Adds an item to the queue. If queue is full, it is said to be an
Overflow condition – Time Complexity : O(1)
Dequeue: Removes an item from the queue. If the queue is empty, it is said
to be an Underflow condition – Time Complexity : O(1)
Front: Get front item from queue – Time Complexity : O(1)
Rear: Get last item from queue – Time Complexity : O(1)
class Queue:
def __init__(self):
self.front = None
self.rear = None
def enqueue(self,value):
new_node = Node(value)
if self.rear == None:
self.front = new_node
self.rear = self.front
else:
self.rear.next = new_node
self.rear = new_node
def dequeue(self):
if self.front == None:
return 'Empty Queue'
else:
self.front = self.front.next
def traverse(self):
temp = self.front
while temp!=None:
print(temp.data,end=' ')
temp = temp.next
print()
def front_item(self):
if self.front == None:
return 'Empty Queue'
return self.front.data
def rear_item(self):
if self.rear == None:
self.rear = self.front
return self.rear.data
def size(self):
counter = 0
temp = self.front
while temp!=None:
counter+=1
temp = temp.next
return counter
In [97]: q = Queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
q.enqueue(4)
q.traverse()
q.dequeue()
q.dequeue()
print('*'*20)
q.traverse()
print('Front: ',q.front_item())
print('rear: ',q.rear_item())
print('Size: ',q.size())
1 2 3 4
********************
3 4
Front: 3
rear: 4
Size: 2
H + 1 , H + 4 , H + 9 , H + 16
...................... H + k 2
This method is also known as the mid-square method because in this method we
look for i2-th probe (slot) in i-th iteration and the value of i = 0, 1, . . . n – 1.
def put(self,key,value):
## we find index using hash fn
hash_value = self.hash_function(key)
## for quadratic probing
i=0
if self.slots[hash_value]==None:
self.slots[hash_value]=key
self.data[hash_value]=value
else:
if self.slots[hash_value]==key:
self.data[hash_value]=value
else:
while self.slots[hash_value]!=None and self.slots[hash_value
new_hash_value = self.rehash(hash_value)
i+=1
if self.slots[new_hash_value]==None:
self.slots[new_hash_value]=key
self.data[new_hash_value]=value
else:
self.data[new_hash_value]=value
def get(self,key):
# calculate hash
start_position = self.hash_function(key)
current_position = start_position
while self.slots[current_position]!=None:
if self.slots[current_position]==key:
return self.data[current_position]
current_position = self.rehash(current_position)
if current_position == start_position:
return 'Item Not Found'
return 'Not Found'
def __setitem__(self,key,value):
return self.put(key,value)
def __getitem__(self,key):
return self.get(key)
def __str__(self):
for i in range(len(self.slots)):
print(self.slots[i],':',self.data[i])
return ''
def __len__(self):
return len(self.slots)
def hash_function(self,key):
return abs(hash(key)) % self.size
def rehash(self,old_hash):
return (old_hash + 1) % self.size
In [56]: d1 = Dictionary(3)
d1.put('python',100)
d1.put('java',200)
d1['php'] = 300
In [57]: print(d1.slots)
print(d1.data)
In [58]: d1.get('python')
Out[58]: 100
In [59]: d1.get('java')
Out[59]: 200
In [60]: d1.get('c++')
In [61]: print(d1)
php : 300
python : 100
java : 200
In [62]: print(len(d1))
class LL:
def __init__(self):
self.head = None
def add(self,key,value):
new_node = Node(key,value)
if self.head == None:
self.head = new_node
else:
temp = self.head
while temp.next!=None:
temp=temp.next
temp.next = new_node
def delete_head(self):
if self.head == None:
return 'Empty'
self.head = self.head.next
def remove(self,key):
if self.head.key == key:
self.delete_head()
return
if self.head == None:
return 'Empty'
temp = self.head
while temp!=None:
if temp.next.key == key:
break
temp = temp.next
temp.next = temp.next.next
def traverse(self):
temp = self.head
while temp!=None:
print(temp.key,':',temp.value)
temp=temp.next
def search(self,key):
temp = self.head
pos = 0
while temp!=None:
if temp.key==key:
return pos
temp=temp.next
pos += 1
return -1
def size(self):
temp = self.head
counter = 0
while temp!=None:
counter += 1
temp = temp.next
return counter
def get_node_at_index(self,index):
temp=self.head
counter=0
while temp!=None:
if counter==index:
return temp
temp= temp.next
counter += 1
class Dict:
self.capacity = capacity
self.size = 0
# create array of LL
self.buckets = self.make_array(self.capacity)
def make_array(self,capacity):
L = []
for i in range(capacity):
L.append(LL())
return L
def __setitem__(self,key,value):
self.put(key,value)
def __getitem__(self,key):
return self.get(key)
def __delitem__(self,key):
bucket_index = self.hash_function(key)
self.buckets[bucket_index].remove(key)
def __str__(self):
for i in self.buckets:
i.traverse()
return ""
def __len__(self):
return self.size
def get(self,key):
bucket_index = self.hash_function(key)
res = self.buckets[bucket_index].search(key)
if res == -1:
return "Not Present"
else:
node = self.buckets[bucket_index].get_node_at_index(res)
return node.value
bucket_index = self.hash_function(key)
if node_index == -1:
# insert
self.buckets[bucket_index].add(key,value)
self.size+=1
load_factor = self.size/self.capacity
def rehash(self):
self.capacity = self.capacity * 2
old_buckets = self.buckets
self.size = 0
self.buckets = self.make_array(self.capacity)
for i in old_buckets:
for j in range(i.size()):
node = i.get_node_at_index(j)
key_item = node.key
value_item = node.value
self.put(key_item,value_item)
node_index = self.buckets[bucket_index].search(key)
return node_index
def hash_function(self,key):
return abs(hash(key)) % self.capacity
In [148… d1 = Dict(3)
d1.put('python',1)
d1.put('java',2)
d1.put('php',3)
d1.put('go',4)
d1.put('html',5)
d1.put('css',6)
In [149… d1['python']=100
In [150… print(d1)
php : 3
python : 100
go : 4
java : 2
html : 5
css : 6
In [151… d1['matlab']=300
In [152… print(d1)
php : 3
python : 100
go : 4
matlab : 300
java : 2
html : 5
css : 6
In [155… print(d1)
php : 3
python : 100
go : 4
java : 2
html : 5
css : 6