Lecture 3 (Medium Problems on LL)¶
Helpful Classes and Methods¶
In [3]:
class Node:
def __init__(self,val=0,next=None):
self.val = val
self.next = next
def to_ll(nums):
if nums is None or len(nums) ==0:
return None
head = Node(nums[0])
node = head
for num in nums[1:]:
node.next = Node(num)
node = node.next
return head
def print_ll(head):
node = head
while node is not None:
print(node.val ,end="->")
node = node.next
print()
Find middle element in a Linked List¶
Given the head of a linked list of integers, determine the middle node of the linked list. However, if the linked list has an even number of nodes, return the second middle node.
Input: LL: 1 2 3 4 5
Output: 3
Input: LL: 1 2 3 4 5 6
Output: 4
Approach 1¶
- calculate no of nodes : n
- when n is even : steps= n/2
- when n is odd : steps = n//2
In [ ]:
def middle_element(head):
if head is None:
return None
n =0
node = head
while node is not None:
n +=1
node = node.next
count = n //2
node = head
while count !=0:
node = node.next
count -=1
return node.val
print(middle_element(to_ll([1,2,3,4,5])))
print(middle_element(to_ll([1,2,3,4,5,6])))
3 4
Complexity¶
O(N) : N = no. of element in ll
O(1)
Approach 2 (Tortoise and Hare Algorithm)¶
In [5]:
def middle_element(head):
if head is None:
return None
slow = head
fast = head
## move slow ptr 1 and fast ptr 2
while fast is not None and fast.next is not None:
slow = slow.next
fast = fast.next.next
return slow.val
print(middle_element(to_ll([1,2,3,4,5])))
print(middle_element(to_ll([1,2,3,4,5,6])))
3 4
Complexity¶
O(N) : N = no of elements in the ll
O(1)
Reverse a Linked List¶
Given the head of a singly linked list, write a program to reverse the linked list, and return the head pointer to the reversed list.
LL: 1 3 2 4
Output: 4 2 3 1
LL : 4
Output : 4
Approach 1 (Iterative)¶
- use 3 ptrs
In [4]:
def reverse_ll(head):
if head is None or head.next is None:
return head
# no of nodes >1
left = head
right = head.next
while right is not None:
temp = right.next
right.next = left
left = right
right = temp
head.next = None
return left
print_ll(reverse_ll(to_ll([1,2,3,4])))
print_ll(reverse_ll(to_ll([4])))
4->3->2->1-> 4->
Complexity¶
O(N) : N = no of nodes in the ll
O(1)
Approach 2 (Recursive)¶
- We know for 0 or 1 nodes , root is the ans
- Imagine we have a linked list 1 → 2 → 3 → 4 → null
- The above condition will be true when we reach node 4
- Now for recursive stack , we will be in node 3 when above condition is not true
- we want 3→ next → next = 3
- 3→ next = null
- repeat this process for 2 and then 1 , we will have the reversed list
In [ ]:
def reverse_ll(head):
if head is None or head.next is None:
return head
new_head = reverse_ll(head.next)
head.next.next = head
return new_head
print_ll(reverse_ll(to_ll([1,2,3,4])))
print_ll(reverse_ll(to_ll([4])))
Complexity¶
O(N) : N = no of nodes in ll
O(N) : N = no of nodes in ll
Length of Loop in Linked List¶
Given the head of a linked list, determine the length of a loop present in the linked list; if not present, return 0.
1 -> 2 - > 3 -> 4 -> 5 -> 3
3
1 -> 2-> 3 -> 4 -> 9
0
Approach 1 (two iterations)¶
- First iteration found the common point
- Check how manu times it repeat
In [ ]:
def loop_length(head):
if head is None or head.next is None:
return 0
## Floyd' cycle detection
slow = fast = head
while fast is not None and fast.next is not None:
slow = slow.next
fast = fast.next.next
if slow == fast:
break
## No loop found
if slow != fast:
return 0
count =1
fast= fast.next
while slow != fast:
fast = fast.next
count +=1
return count
Complexity¶
O(N) : N = no of elements in LL
O(1)