Skip to content

Commit

Permalink
-----------
Browse files Browse the repository at this point in the history
  • Loading branch information
Samir Paul committed May 25, 2022
1 parent 8471ee2 commit 6f50c22
Show file tree
Hide file tree
Showing 29 changed files with 1,135 additions and 0 deletions.
43 changes: 43 additions & 0 deletions 04_LinkedList/Check if a LinkedList is palindrome or not..py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# https://leetcode.com/problems/palindrome-linked-list/

'''
Go to the middle node and reverse the right-side linkedlist.
Then take 2 pointers one from start and another from middle and check
equality of value.
'''

class Solution:
def isPalindrome(self, head: Optional[ListNode]) -> bool:
if not head or not head.next: return True

# Find Middle
slow = head
fast = head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# slow is just the previous node of middle as we need to reverse the Linkedlist also

# Reverse LinkedList right-side of middle
pre = slow
cur = pre.next
nex = cur.next

while nex:
cur.next = nex.next
nex.next = pre.next
pre.next = nex
nex = cur.next

# start checking equality of values
left = head
right = slow.next
while left and right:
if left.val != right.val: return False
left = left.next
right = right.next

return True

# Time: O(N)
# Space: O(1)
21 changes: 21 additions & 0 deletions 04_LinkedList/Find intersection point of 2 LinkedList.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# https://leetcode.com/problems/intersection-of-two-linked-lists/

class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
a = headA
b = headB
loopCount = 0 # if no intersection

while True:
if a == b: return a

a = a.next
b = b.next

if not a: a = headB; loopCount += 1
if not b: b = headA

if loopCount > 1: return None

# Time: O(n + m)
# Space: O(1)
63 changes: 63 additions & 0 deletions 04_LinkedList/Flattening of a LinkedList.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# https://practice.geeksforgeeks.org/problems/flattening-a-linked-list/1

'''
Select 2 bottom directed linklist at a time and use the concept of "21. Merge Two Sorted Lists"
on them. Start traversing from the begining. Assign 'a' linkedList wirh smaller head and
'b' to the larger head. Change main head to a. At the end we will have the sorted Linkedlist with only bottom pointer.
Input:
a b c => take 'a' at smaller head
5 -> 10 -> 19 -> 28
| | | |
7 20 22 35
| | |
8 50 40
| |
30 45
Output: 5-> 7-> 8- > 10 -> 19-> 20-> 22-> 28-> 30-> 35-> 40-> 45-> 50 bottom pointer
'''


'''
class Node:
def __init__(self, d):
self.data=d
self.next=None
self.bottom=None
'''

def flatten(head):
while head:
if not head.next: return head

if head.data <= head.next.data:
a = head
b = head.next
c = b.next
else:
a = head.next
b = head
c = a.next
head = head.next

while a:
if b and a.bottom and a.bottom.data > b.data:
tmp = a.bottom
a.bottom = b
b = tmp
elif not a.bottom:
a.bottom = b
b = None
elif not a.bottom and not b:
a.bottom = c
break
a = a.bottom
head.next = c

return head


# Time Complexity: O(N*N*M)
# Auxiliary Space: O(1)
63 changes: 63 additions & 0 deletions 10_Trie/02. Implement Trie – 2 (Prefix Tree).py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# https://www.codingninjas.com/codestudio/problems/implement-trie_1387095

# https://youtu.be/ict1UawpXMM?t=359

class TrieNode:
def __init__(self):
self.children = {}
self.endOfWordCount = 0
self.prefixOfWordCount = 0

class Trie:
def __init__(self):
self.root = TrieNode()

def insert(self, word):
cur = self.root
for c in word:
if c not in cur.children:
cur.children[c] = TrieNode()
cur = cur.children[c]
cur.prefixOfWordCount += 1
cur.endOfWordCount += 1

def countWordsEqualTo(self, word):
cur = self.root
for c in word:
if c not in cur.children:
return 0
cur = cur.children[c]
return cur.endOfWordCount

def countWordsStartingWith(self, word):
cur = self.root
for c in word:
if c not in cur.children:
return 0
cur = cur.children[c]
return cur.prefixOfWordCount

def erase(self, word): # Dele te function
cur = self.root
toBeDeleted = None
for c in word: # as it a delete function so word is present in trie so we don't need to check if key 'c' present in children hashmap or not.
cur = cur.children[c]
cur.prefixOfWordCount -= 1
if toBeDeleted:
toBeDeleted = None
if cur.prefixOfWordCount == 0:
toBeDeleted = cur

if toBeDeleted:
toBeDeleted = None
cur.endOfWordCount -= 1


# It will also work if we don't delete the node only decrease the counts
def erase(self, word): # Dele te function
cur = self.root
for c in word: # as it a delete function so word is present in trie so we don't need to check if key 'c' present in children hashmap or not.
cur = cur.children[c]
cur.prefixOfWordCount -= 1

cur.endOfWordCount -= 1
62 changes: 62 additions & 0 deletions 10_Trie/03. Maximum XOR of Two Numbers in an Array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/

'''
Insert all the elements of nums in a trie with bit values in respective position.
where every node can have 2 children either with 0 key or 1 key.
As XOR is a inequality detector so we try to maximize the inequality between num and node.
So that the XOR of num and value of node will give the max value.
So we do the following steps While traversing the num from 31'th bit position to 0'th bit position:
If the current bit of num is 1 then we try to move the cur pointer towards the child with 0 key.
And if the current bit of num is 0 then we try to move the cur pointer towards the child with 1 key.
'''

class TrieNode:
def __init__(self):
self.children = {}
self.val = -1 # used to store the value of entire number at the end of trie node

class Trie:
def __init__(self):
self.root = TrieNode() # object of trienode class

def addNum(self, num):
cur = self.root # every time start from root
for i in range(31, -1, -1):
bit = 1 if num & (1 << i) else 0 # bit value i'th position of num
if bit not in cur.children:
cur.children[bit] = TrieNode()
cur = cur.children[bit]
cur.val = num # storing the value of entire num at the end of trie node


class Solution:
def findMaximumXOR(self, nums: List[int]) -> int:
trie = Trie() # creating object of Trie class
for num in nums: # adding all num to the trie structure
trie.addNum(num)

res = 0
for num in nums:
cur = trie.root # every time start cur pointer from root
for i in range(31, -1, -1):
bit = 1 if num & (1 << i) else 0 # bit value of i'th position of num
if bit == 1: # try to move towards opposite key ie. 0
if 0 in cur.children: # opposit key 0 exist then defenetly go towards the child 0
cur = cur.children[0]
else: # opposit key 0 not exist so we have only option to go towards what we have ie. 1
cur = cur.children[1]
else: # bit == 0 # try to move towards opposite key ie. 1
if 1 in cur.children: # opposit key 1 exist then defenetly go towards the child 1
cur = cur.children[1]
else: # opposit key 1 not exist so we have only option to go towards what we have ie. 0
cur = cur.children[0]
# as we tried to maximize the inequality between cur.val and num so XOR of them will give max value
res = max(res, cur.val ^ num)

return res



29 changes: 29 additions & 0 deletions 10_Trie/04. Number of Distinct Substrings in a String.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# https://www.codingninjas.com/codestudio/problems/count-distinct-substrings_985292
# https://youtu.be/RV0QeTyHZxo
class TrieNode:
def __init__(self):
self.children = {}

class Trie:
def __init__(self):
self.root = TrieNode()
def solve(self, s):
res = 0
for i in range(len(s)):
cur = self.root
for j in range(i, len(s)):
if s[j] not in cur.children:
cur.children[s[j]] = TrieNode()
res += 1
cur = cur.children[s[j]]
return res + 1 # +1 for empty substring ""

def countDistinctSubstrings(s):
trie = Trie()
return trie.solve(s)


# Time: O(N^2)
# Space: It is hard to predict spcace taken tries. It depends on the distinct elements of s. But as we are using only necessary keys in trie hashmap not all 26 keys so at max space can be N^2
# Space: in worst case O(N^2)

37 changes: 37 additions & 0 deletions 11_Binary-Search/Aggressive Cows - Advanced Binary Search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# https://www.codingninjas.com/codestudio/problems/aggressive-cows_1082559?leftPanelTab=1
# https://youtu.be/YTTdLgyqOLY?t=2626
'''
Same binary search approach like 'Minimum Page Allocation Problem'.
In the isValid function we need to check if with the current distance 'mid'
we can place all the cows in the stalls or not. If we can place all the cows then
try to increase the mid(ie. increase the low). If we can not place all cows then
that distsnce(ie. mid) we need to decrease the distance etween cows ie. dicrease the
mid.
'''
def aggressiveCows(stalls, k):
def isValid(mid):
count = 1 # count of cows
lastPosition = stalls[0] # position where we recently placed the cow
for i in range(1, len(stalls)):
if stalls[i] - lastPosition >= mid: # checking if we can place cow in this i'th position
count += 1
if count == k: return True
lastPosition = stalls[i]
return False

stalls.sort() # as in isValid() function we need the distance in increasing order
ans = 0
low = 0
high = max(stalls)
while low <= high:
mid = low + (high - low) // 2
if isValid(mid): # check if we can increase the distance or not
ans = max(ans, mid)
low = mid + 1
else:
high = mid - 1

return ans

# Time: n * log(max(stalls))
# Space: O(1)
Loading

0 comments on commit 6f50c22

Please sign in to comment.