Lecture 3 (Medium Problems on LL)¶
Helpful Classes and Methods¶
In [1]:
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])
temp = head
for num in nums[1:]:
temp.next = Node(num)
temp = temp.next
return head
def from_ll(head):
result = []
temp = head
while temp is not None:
result.append(temp.val)
temp = temp.next
return result
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 [2]:
def middle_element(head):
if head is None:
return None
count =0
temp = head
while temp is not None:
temp = temp.next
count +=1
i=0
temp = head
while i != count // 2:
i +=1
temp = temp.next
return temp.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 [3]:
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])))
print(middle_element(to_ll([1])))
3 4 1
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 [8]:
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(from_ll(reverse_ll(to_ll([1,3,2,4]))) )
print(from_ll(reverse_ll(to_ll([4]))) )
[4, 2, 3, 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
head.next = None
return new_head
print(from_ll(reverse_ll(to_ll([1,3,2,4]))) )
print(from_ll(reverse_ll(to_ll([4]))) )
[4, 2, 3, 1] [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
slow = head
fast = head.next
while fast is not None and fast.next is not None and fast != slow:
slow = slow.next
fast = fast.next.next
if slow != fast:
return 0
count = 1
fast = slow.next
while slow != fast:
fast = fast.next
count +=1
return count
Complexity¶
O(N) : N = no of elements in LL
O(1)