0% found this document useful (0 votes)
2 views

Dsa Assignement2

The document outlines the operations and implementation of a sorted linked list, including insertion, removal, and presence checking of nodes. It details the expected outcomes of various operations, provides a class structure for the linked list, and analyzes the time complexity of each method. The analysis concludes that both singly and doubly linked lists exhibit similar time complexities for key operations, with the exception of the length function which operates in constant time.

Uploaded by

zille.huma
Copyright
© © All Rights Reserved
0% found this document useful (0 votes)
2 views

Dsa Assignement2

The document outlines the operations and implementation of a sorted linked list, including insertion, removal, and presence checking of nodes. It details the expected outcomes of various operations, provides a class structure for the linked list, and analyzes the time complexity of each method. The analysis concludes that both singly and doubly linked lists exhibit similar time complexities for key operations, with the exception of the length function which operates in constant time.

Uploaded by

zille.huma
Copyright
© © All Rights Reserved
You are on page 1/ 12

Assignment 2

Part 2 – Linked Lists

Step 1: Drawings (20%)


 insert(30);
1. insert(30) Operation
Before insertion:
Current list: 5 → 10 → 23
After inserting 30:
 Since 30 is greater than all existing values, it will be inserted at the end.
 Updated list: 5 → 10 → 23 → 30
 The new node 30 will have its prev pointing to 23 and next set to None.
 The back pointer will now reference the new node 30.

2. insert(2) Operation
Before insertion:
Current list: 5 → 10 → 23
After inserting 2:
 Since 2 is smaller than all existing values, it will be inserted at the front.
 Updated list: 2 → 5 → 10 → 23
 The new node 2 will have its next pointing to 5 and prev set to None.
 The front pointer will now reference the new node 2.

3. remove(8) Operation
Before removal:
Current list: 8 → 10 → 15 → 23
After attempting to remove 8:
 Node 8 exists at the front.
 It will be removed, and front will now point to 10.Updated list: 10 → 15 → 23

 The next pointer of the new front node 10 remains unchanged.


 Return value: True (indicating successful removal).

4. remove(23) Operation
Before removal:
Current list: 5 → 10 → 15 → 23
After removing 23:
 Node 23 exists at the back.
 It will be removed, and back will now point to 15.
 Updated list: 5 → 10 → 15
 The next pointer of node 15 will be set to None.
 Return value: True.

5. is_present(21) Operation
Current list:
7 → 18 → 19 → 21 → 26
Checking for 21:
 Traverse nodes in the order: 7, 18, 19, 21.
 Node 21 is found.
 Return value: True.

6. is_present(7) Operation
Current list:
7 → 18 → 19 → 21 → 26
Checking for 7:
 The first node is 7.
 Return value: True.

7. is_present(11) Operation
Current list:
7 → 18 → 19 → 21 → 26
Checking for 11:
 Traverse nodes in the order: 7, 18, 19.
 11 is not found in the list.
 Return value: False.

8. is_present(30) Operation
Current list:
7 → 18 → 19 → 21 → 26
Checking for 30:
 Traverse nodes in the order: 7, 18, 19, 21, 26.
 30 is not found.
 Return value: False.

Step 2: Implementing a Sorted Linked list (60%)


class SortedList:
class Node:
# Node class constructor with initialization of data members
def __init__(self, data=None, next_node=None, prev_node=None):
self.data = data
self.next = next_node
self.prev = prev_node

def __init__(self):
# Initialize the front and back pointers of the list
self.front = None
self.back = None
self._length = 0

def __len__(self):
# Return the length of the list
return self._length

def insert(self, data):


# Create a new node with the given data
new_node = self.Node(data)

# If the list is empty, set both front and back to the new node
if self.front is None:
self.front = self.back = new_node
else:
current = self.front
previous = None

# Traverse to find the correct sorted position


while current and current.data < data:
previous = current
current = current.next

# Insert at the beginning if no previous node


if previous is None:
new_node.next = self.front
self.front.prev = new_node
self.front = new_node
# Insert at the end if current is None
elif current is None:
previous.next = new_node
new_node.prev = previous
self.back = new_node
else:
# Insert in the middle
previous.next = new_node
new_node.prev = previous
new_node.next = current
current.prev = new_node

self._length += 1

def remove(self, data):


current = self.front
# Traverse to find the node to remove
while current and current.data != data:
current = current.next

if current is None:
return False # Node not found

# Adjust pointers to remove the node


if current.prev:
current.prev.next = current.next
else:
self.front = current.next

if current.next:
current.next.prev = current.prev
else:
self.back = current.prev

self._length -= 1
return True

def is_present(self, data):


current = self.front
# Traverse while the current node's data is less than or equal to the target
while current and current.data <= data:
if current.data == data:
return True
current = current.next
return False

def __iter__(self):
curr = self.front
while curr:
yield curr.data
curr = curr.next

def __reversed__(self):
curr = self.back
while curr:
yield curr.data
curr = curr.prev

# Main program to test the implementation


def main():
# Create a new SortedList instance
sorted_list = SortedList()

# Test insertions
print("Inserting values into the sorted list...")
sorted_list.insert(10)
sorted_list.insert(5)
sorted_list.insert(15)
sorted_list.insert(7)
sorted_list.insert(12)

# Print the list content using forward iteration


print("\nList after insertions (expected sorted order: 5, 7, 10, 12, 15):")
for item in sorted_list:
print(item, end=" ")
print("\nLength of list:", len(sorted_list))

# Test the is_present method


print("\nChecking if values are present in the list:")
print(f"Is 10 present? {sorted_list.is_present(10)}")
print(f"Is 3 present? {sorted_list.is_present(3)}")
print(f"Is 15 present? {sorted_list.is_present(15)}")

# Test removals
print("\nRemoving values from the list...")
print(f"Removing 10: {sorted_list.remove(10)}")
print(f"Removing 3 (not in list): {sorted_list.remove(3)}")
print(f"Removing 5: {sorted_list.remove(5)}")

# Print the list content after removals


print("\nList after removals (expected sorted order: 7, 12, 15):")
for item in sorted_list:
print(item, end=" ")
print("\nLength of list:", len(sorted_list))

# Test reversed iteration


print("\nList in reverse order:")
for item in reversed(sorted_list):
print(item, end=" ")

# Run the main function to test the SortedList implementation


main()
Output:

Step 3: Analysis (20%)


1. Insert Function
Singly Linked List Analysis:
 In the worst situation, we must search the complete list for the new node's correct
sorted position.
 Beginning at the beginning, the method iterates through each node until it reaches the
proper location. This occurs when the new value is greater than all existing values.
Once the position is found, the insertion itself (adjusting pointers)
Time Complexity:
 O(n) because, in the worst case, we may need to traverse all n nodes to find the
correct position.

Doubly Linked List Analysis


 The prev pointer does not considerably cut down on traversal time because we are just
inserting nodes in sorted order by traversing from the front, but it is similar to the
singly linked list with the added advantage of having access to both next and prev
pointers.
 Time Complexity:
 O(n), same as in the singly linked list.
2. remove(data) Method
Singly Linked List Analysis
 The worst-case situation is that the node that needs to be eliminated either doesn't
exist or is at the end of the list.
 To locate the node containing the designated data, we must traverse from the front.
 We must additionally change the previous node's next pointer if it is located.
 Time Complexity:
 O(n) for finding the node.
 Removing the node itself is O(1) after it's found.

Doubly Linked List Analysis


 Even so, traversal still takes O(n) time to locate the node; but, since we have access to
both the next and previous pointers, deleting the node is quicker.
 Time Complexity:
 O(n) for traversal, but removal itself is more efficient due to the prev pointer
being readily available.

3. is_present(data) Method
Singly Linked List Analysis
 If we come across a node with a value higher than the data we're looking for, we can
end the search early because the list has been sorted.
 The worst-case situation is that the value either doesn't exist or is greater than any
value in the list.
 Time Complexity:
 O(n) in the worst case (i.e., when we have to traverse all nodes).

Doubly Linked List Analysis


 The addition of prior pointers does not aid in data search; similarly to a singly linked
list, we still need to navigate the list from the front.
 Time Complexity:
 O(n) in the worst case.
4. __len__() Method
Singly Linked List Analysis
 Since a counter (_length) is used to track the length, this method just returns that
value.
 Time Complexity:
 O(1) since we are just returning a stored value.

Doubly Linked List Analysis


 The time complexity is the same because the same method is applied.
 Time Complexity:
 O(1).

Comparison of T(n) and f(n)


 The insert, remove, and is_present functions have comparable time complexity for
both implementations: (O(n)).
 If the length is kept as a data member, the length function is likewise (O(1)) in both
implementations.
 Thus, for all functions except the length function, which stays constant, we may state
that (T(n) \sim O(f(n))).

You might also like