0% found this document useful (0 votes)
91 views20 pages

BAGEXERCISE

The document discusses key concepts related to data structures like bags and classes in Python. It provides examples of bag implementations using arrays and linked lists and covers topics like time complexity of common bag operations like add, remove and equality check. It also defines concepts like interfaces, polymorphism and composition.

Uploaded by

jincy
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
91 views20 pages

BAGEXERCISE

The document discusses key concepts related to data structures like bags and classes in Python. It provides examples of bag implementations using arrays and linked lists and covers topics like time complexity of common bag operations like add, remove and equality check. It also defines concepts like interfaces, polymorphism and composition.

Uploaded by

jincy
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 20

1.

A bag is an example of:

1.A linear collection


2.An unordered collection
 Correct Answer: An unordered collection
 Explanation: A bag is a collection where items are stored without any particular
order. Unlike sequences, the order in which items are added to a bag is not
preserved.

2.The method that is responsible for setting the initial state of an object’s
instance variables is the:

1.__init__method
2.__str__ method
 Correct Answer: init method
 Explanation: The init method is a special method in Python classes that is called
when a new instance of the class is created. It initializes the object's instance
variables and sets their initial state.

3.The method that allows a programmer to visit each item in a collection is the:

1.__init__method
2.__iter__ method
 Correct Answer: iter method
 Explanation: The iter method is used to implement iteration in Python classes. It
allows objects to be used in a for loop and enables iteration over the items in a
collection.
4.Methods that change the internal state of an object are its:

1.Accessor methods
2.Mutator methods
 Correct Answer: Mutator methods
 Explanation: Mutator methods are responsible for modifying the internal state of
an object by changing the values of its attributes or instance variables.

5.The set of methods available to the user of a class is called its:

1.Implementation
2.Interface
 Correct Answer: Interface
 Explanation: An interface defines the set of methods that a class provides to its
users. It specifies how a class can be interacted with and what operations can be
performed on its objects.

6.Polymorphism is a term that refers to the use of:

1.The same method names in multiple classes


2.One class to represent the data contained in another class
 Correct Answer: The same method names in multiple classes
 Explanation: Polymorphism allows methods to behave differently depending on
the object they are called on. This can involve using the same method name in
different classes to achieve different behavior.

7.Composition is a term that refers to a:

1.Part-whole relationship among two classes


2.Many-to-one relationship among two classes
 Correct Answer: Part-whole relationship among two classes
 Explanation: Composition is a design principle where one class contains objects
of another class as part of its state. It represents a part-whole relationship
between the two classes.

8.The average running time of theadd method for bags is:

1.O(n)
2.O(k)
 Correct Answer: O(n)
 Explanation: The average running time for adding an item to a bag is linear, O(n),
where n is the number of items in the bag. This is because it may require iterating
over the entire collection to find the appropriate place to add the new item.

9.The average running time of theremovemethod for bags is:

1.O(n)
2.O(k)
 Correct Answer: O(n)
 Explanation: Similarly, the average running time for removing an item from a bag
is also linear, O(n), as it may involve searching for and then removing the item
from the collection.

10.The array bag implementation uses less memory than the linked bag
implementation when the array is:

1.Less than half full


2.More than half full

 Correct Answer: Less than half full


 Explanation: In the array bag implementation, memory usage is more efficient
when the array is less than half full because it avoids unnecessary memory
allocation. When the array is more than half full, resizing the array may be
required, resulting in additional memory overhead.

Answers:

1.b
2.a
3.b
4.b
5.b
6.a
7.a
8.b
9.a
10.b

Determine the running time of the == operation for the two bag implementations. Be forewarned that
there are several cases to analy

The running time of the == operation for the two bag implementations depends on their
underlying data structures and how equality is determined between two bags. Let's
analyze the time complexity for each implementation:

1. ArrayBag:
 In the ArrayBag implementation, to determine if two bags are equal, you
typically need to compare each element in one bag with each element in
the other bag. This comparison involves iterating over the elements of
both bags and checking for equality.
 Assuming n is the number of elements in each bag, the worst-case time
complexity of the == operation for ArrayBag is O(n^2). This is because in
the worst case, every element in one bag needs to be compared with every
element in the other bag.
2. LinkedBag:
 For the LinkedBag implementation, the time complexity of the ==
operation also depends on the number of elements in each bag.
 Similar to ArrayBag, in the worst case, you need to iterate over each
element in one bag and compare it with each element in the other bag.
 However, due to the nature of linked lists, accessing elements might be
slower compared to arrays, especially for large bags.
 Therefore, the worst-case time complexity of the == operation for
LinkedBag is also O(n^2), where n is the number of elements in each bag.

In summary, both ArrayBag and LinkedBag implementations have a worst-case time


complexity of O(n^2) for the == operation, where n is the number of elements in each
bag. This is because comparing every element in one bag with every element in the
other bag requires nested iterations over the bags, resulting in quadratic time
complexity.
Let's illustrate the running time of the == operation for the two bag implementations,
ArrayBag and LinkedBag, with an example.

Consider the following two bags:

1. ArrayBag:
 Bag A: [1, 2, 3, 4]
 Bag B: [1, 2, 3, 4]
2. LinkedBag:
 Bag C: [1, 2, 3, 4]
 Bag D: [1, 2, 3, 4]

Now, let's compare Bag A with Bag B using the == operation:

 ArrayBag:
 Since ArrayBag uses arrays to store elements, the comparison involves
iterating over each element of Bag A and checking if it exists in Bag B.
 For each element in Bag A, we need to perform a linear search in Bag B to
check for its existence.
 In this case, Bag A has 4 elements. For each element in Bag A, we perform
a linear search in Bag B with 4 elements.
 Thus, the total number of comparisons is 4 * 4 = 16.
 LinkedBag:
 Similarly, for LinkedBag, the comparison involves iterating over each
element of Bag C and checking if it exists in Bag D.
 Again, for each element in Bag C, we need to traverse Bag D's linked list
and check for its existence.
 Like ArrayBag, Bag C has 4 elements, and for each element, we perform a
traversal of Bag D with 4 elements.
 Hence, the total number of comparisons is also 4 * 4 = 16.

In this example, both ArrayBag and LinkedBag require 16 comparisons to determine the
equality of Bag A and Bag B. This example illustrates that both implementations have a
quadratic time complexity of O(n^2) for the == operation, where n is the number of
elements in each bag.

class ArrayBag:
def __init__(self, items=None):
if items is None:
items = []
self.items = items

def __eq__(self, other):


if not isinstance(other, ArrayBag):
return False
if len(self.items) != len(other.items):
return False
return all(item in other.items for item in self.items)

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

class LinkedBag:
def __init__(self, items=None):
self.head = None
if items:
for item in items:
self.add(item)
def add(self, item):
new_node = Node(item)
new_node.next = self.head
self.head = new_node

def __eq__(self, other):


if not isinstance(other, LinkedBag):
return False
current_self = self.head
current_other = other.head
while current_self and current_other:
if current_self.data != current_other.data:
return False
current_self = current_self.next
current_other = current_other.next
return current_self is None and current_other is None

# Example usage:
bag1 = ArrayBag([1, 2, 3, 4])
bag2 = ArrayBag([1, 2, 3, 4])
print(bag1 == bag2) # Output: True

bag3 = LinkedBag([1, 2, 3, 4])


bag4 = LinkedBag([1, 2, 3, 4])
print(bag3 == bag4) # Output: True

In this code:

 For ArrayBag, the __eq__ method checks if the length of both bags is
equal and if each element in one bag is present in the other bag.
 For LinkedBag, the __eq__ method traverses both bags simultaneously
and checks if each corresponding element is equal.
3. Determine the running time of the + operator for the two bag implementations.

The running time of the + operator for the two bag implementations depends on the
underlying data structure and how the addition operation is implemented.

For ArrayBag:

 When adding two ArrayBag objects together ( bag1 + bag2), you typically
concatenate the underlying lists ( items) of both bags.
 If the size of bag1 is n1 and the size of bag2 is n2, the concatenation operation
would take O(n1 + n2) time, where n1 and n2 are the sizes of the lists in bag1 and
bag2, respectively.
 The time complexity is linear because you need to iterate over each element in
both lists to perform the concatenation.

For LinkedBag:

 When adding two LinkedBag objects together ( bag1 + bag2), you typically traverse
to the end of the first bag and then append the elements of the second bag.
 If the size of bag1 is n1 and the size of bag2 is n2, the addition operation would
take O(n1) time to traverse to the end of bag1 and O(n2) time to append the
elements of bag2.
 Therefore, the total time complexity is O(n1 + n2), where n1 and n2 are the sizes
of the bags.

In summary, the running time of the + operator for both ArrayBag and LinkedBag
implementations is linear in the total number of elements being added.
EXAMPLE

Let's consider an example to demonstrate the running time of the + operator for both
ArrayBag and LinkedBag implementations.

Suppose we have two bags:

ArrayBag:

 bag1 contains the elements [1, 2, 3]


 bag2 contains the elements [4, 5, 6]

LinkedBag:

 bag1 contains the elements [7, 8, 9]


 bag2 contains the elements [10, 11, 12]

Now, let's perform the addition operation for both implementations:

For ArrayBag:

 bag1 + bag2 would concatenate the lists [1, 2, 3] and [4, 5, 6] to produce [1, 2, 3, 4, 5,
6].
 Since the sizes of both bags are 3, the total time complexity would be linear in
the total number of elements, which is 6.

For LinkedBag:

 bag1 + bag2 would traverse to the end of bag1, then append the elements of bag2
to it.
 Traversing bag1 takes O(n1) time, where n1 is the size of bag1 (3 in this case).
 Appending the elements of bag2 takes O(n2) time, where n2 is the size of bag2
(also 3).
 Therefore, the total time complexity would be linear in the total number of
elements, which is 6.

In both cases, the running time of the + operator is linear in the total number of
elements being added.
omplete the code for the ArrayBag method add, so that the array is resized when necessary.

To complete the code for the ArrayBag method add, we need to ensure that the array is resized
when necessary. Resizing typically involves creating a new array with a larger capacity and
copying the existing elements into the new array. Here's how we can implement it

class ArrayBag:
def __init__(self):
self.items = [] # Assuming we have an array implementation
self.size = 0
self.capacity = 10 # Initial capacity of the array

def add(self, item):


"""Adds item to self."""
if self.size == self.capacity:
self.resize() # Resize the array if it's full
self.items.append(item)
self.size += 1

def resize(self):
"""Resizes the array to accommodate more elements."""
self.capacity *= 2 # Double the capacity
new_items = [None] * self.capacity # Create a new array with double
capacity
for i in range(self.size):
new_items[i] = self.items[i] # Copy elements from old array to new array
self.items = new_items # Update items to point to the new array

class ArrayBag:
def __init__(self):
self.items = [] # Assuming we have an array implementation
self.size = 0
self.capacity = 10 # Initial capacity of the array

def add(self, item):


"""Adds item to self."""
if self.size == self.capacity:
self.resize() # Resize the array if it's full
self.items.append(item)
self.size += 1

def resize(self):
"""Resizes the array to accommodate more elements."""
self.capacity *= 2 # Double the capacity
new_items = [None] * self.capacity # Create a new array with double
capacity
for i in range(self.size):
new_items[i] = self.items[i] # Copy elements from old array to new array
self.items = new_items # Update items to point to the new array
In this code:

 We initialize self.capacity to some initial value (e.g., 10).


 In the add method, before adding a new item, we check if the array is full ( self.size
== self.capacity).
 If the array is full, we call the resize method, which doubles the capacity of the
array and copies the existing elements into the new array.
 After resizing, we can safely add the new item to the array.

This ensures that the array dynamically resizes itself to accommodate more elements
when needed, preventing potential overflow errors.

omplete the code for the ArrayBag method remove, so that the array is resized when necessary

To complete the code for the ArrayBag method remove, we don't need to resize the array
because removing an element doesn't affect the size of the array. However, we do need to
ensure that after removing an item, we shift the remaining elements to fill the gap created by
the removal. Here's the complete code:

class ArrayBag:
def __init__(self):
self.items = [] # Assuming we have an array implementation
self.size = 0
self.capacity = 10 # Initial capacity of the array

def add(self, item):


"""Adds item to self."""
if self.size == self.capacity:
self.resize() # Resize the array if it's full
self.items.append(item)
self.size += 1

def remove(self, item):


"""Removes item from self."""
if item not in self.items:
raise ValueError(f"{item} not in bag")

# Find the index of the target item


target_index = None
for i in range(self.size):
if self.items[i] == item:
target_index = i
break

# Shift elements to the left starting from the target index


for i in range(target_index, self.size - 1):
self.items[i] = self.items[i + 1]

# Decrement logical size


self.size -= 1

# Optional: Set the last element to None or remove it


self.items[self.size] = None

return item
def resize(self):
"""Resizes the array to accommodate more elements."""
self.capacity *= 2 # Double the capacity
new_items = [None] * self.capacity # Create a new array with double
capacity
for i in range(self.size):
new_items[i] = self.items[i] # Copy elements from old array to new array
self.items = new_items # Update items to point to the new array

In the remove method:

 We first check if the item exists in the bag. If not, we raise a ValueError.
 Then, we find the index of the target item.
 We shift the elements to the left starting from the target index to fill the gap
created by removing the item.
 Finally, we decrement the logical size of the bag.

The resize method remains the same as in the previous explanation.

EXPLANATION
ure, let's go through the remove method of the ArrayBag class line by line:

pythonCopy code
def remove ( self, item ): """Removes item from self."""
 This line defines the remove method of the ArrayBag class. It takes item as a
parameter and removes that item from the bag.
 The docstring provides a brief description of what the method does.
pythonCopy code
if item not in self.items: raise ValueError( f"{item} not in bag" )
 This line checks if the item to be removed exists in the bag.
 If the item is not found ( item not in self.items), it raises a ValueError with a
message indicating that the item is not in the bag.
pythonCopy code
target_index = None for i in range (self.size): if self.items[i] == item: target_index = i break
 This block of code iterates through the items in the bag to find the
index of the item to be removed.
 It initializes target_index to None and then checks each item in the bag.
 If the current item matches the item to be removed, it assigns the index
of that item to target_index and breaks out of the loop.
pythonCopy code
for i in range (target_index, self.size - 1 ): self.items[i] = self.items[i + 1 ]
 This loop shifts the elements in the array to the left starting from the
index of the item to be removed ( target_index).
 It iterates over the elements from target_index to self.size - 1 (excluding the
last element).
 Each element is replaced with the next element in the array, effectively
shifting all elements one position to the left.
pythonCopy code
self.size -= 1 self.items[self.size] = None
 After shifting the elements, we decrement the logical size of the bag
(self.size) by 1.
 We then set the last element of the array to None (optional step).
 This step ensures that the last element of the array is cleared after
removal. It's optional and depends on how you want to handle the
removed elements.
pythonCopy code
return item
 Finally, we return the removed item from the method.
****
Add the method clone to the ArrayBag and LinkedBag classes. This method expects no arguments when
called, and returns an exact copy of the type of bag on which it is called. For example, the variable bag2
would contain the numbers 2, 3, and 4 at the end of the following code segment: bag1 =
ArrayBag([2,3,4]) bag2 = bag1.clone() bag1 == bag2 # Returns True bag1 is bag2 # Returns False

To add the clone method to the ArrayBag and LinkedBag classes, you need to implement
a method that creates a copy of the bag object. Here's how you can do it:

For ArrayBag:
class ArrayBag:
def __init__(self):
self.items = []
self.size = 0

def clone(self):
"""Returns a shallow copy of the ArrayBag."""
new_bag = ArrayBag()
new_bag.items = self.items.copy() # Create a shallow copy of the items list
new_bag.size = self.size # Copy the size
return new_bag

For LinkedBag

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

class LinkedBag:
def __init__(self):
self.head = None
self.size = 0

def clone(self):
"""Returns a shallow copy of the LinkedBag."""
new_bag = LinkedBag()
if self.head is not None:
current = self.head
new_head = Node(current.data)
new_bag.head = new_head
new_size = 1
current = current.next
while current is not None:
new_node = Node(current.data)
new_head.next = new_node
new_head = new_head.next
current = current.next
new_size += 1
new_bag.size = new_size
return new_bag

These implementations create a shallow copy of the bag objects. It copies the items of
the bag and the size of the bag. Keep in mind that this creates a shallow copy, so if the
items themselves are mutable objects (like lists or dictionaries), they will be shared
between the original bag and the cloned bag.

Sure, let's break down the code line by line:

For ArrayBag:
pythonCopy code
class ArrayBag : def __init__ ( self ): self.items = [] self.size = 0
 class ArrayBag:: Defines a class named ArrayBag.
 def __init__(self):: Defines the constructor method for ArrayBag.
 self.items = [] : Initializes an empty list items to store the elements of the bag.
 self.size = 0: Initializes the size of the bag to zero.
pythonCopy code
def clone ( self ): """Returns a shallow copy of the ArrayBag.""" new_bag = ArrayBag() new_bag.items =
self.items.copy() # Create a shallow copy of the items list new_bag.size = self.size # Copy the size return
new_bag
 def clone(self):: Defines a method named clone that takes no arguments.
 """Returns a shallow copy of the ArrayBag.""" : Docstring explaining the purpose of
the method.
 new_bag = ArrayBag(): Creates a new instance of ArrayBag to hold the cloned items.
 new_bag.items = self.items.copy() : Creates a shallow copy of the items list using the
copy() method, ensuring that modifying the copy won't affect the original list.
 new_bag.size = self.size : Copies the size of the original bag to the cloned bag.
 return new_bag: Returns the cloned ArrayBag object.

For LinkedBag:

pythonCopy code
class Node : def __init__ ( self, data ): self.data = data self. next = None
 class Node:: Defines a class named Node.
 def __init__(self, data): : Defines the constructor method for Node which initializes a
node with some data.
 self.data = data: Initializes the data attribute of the node with the provided data.
 self.next = None: Initializes the next attribute to None, indicating that this node is
currently the last node in the linked list.
pythonCopy code
class LinkedBag : def __init__ ( self ): self.head = None self.size = 0
 class LinkedBag:: Defines a class named LinkedBag.
 def __init__(self):: Defines the constructor method for LinkedBag.
 self.head = None: Initializes the head attribute to None, indicating that the linked list
is initially empty.
 self.size = 0: Initializes the size of the bag to zero.
pythonCopy code
def clone ( self ): """Returns a shallow copy of the LinkedBag.""" new_bag = LinkedBag() if self.head is not
None : current = self.head new_head = Node(current.data) new_bag.head = new_head new_size = 1 current =
current. next while current is not None : new_node = Node(current.data) new_head. next = new_node new_head
= new_head. next current = current. next new_size += 1 new_bag.size = new_size return new_bag
 def clone(self):: Defines a method named clone that takes no arguments.
 """Returns a shallow copy of the LinkedBag.""" : Docstring explaining the purpose of
the method.
 new_bag = LinkedBag(): Creates a new instance of LinkedBag to hold the cloned
items.
 if self.head is not None:: Checks if the bag is not empty.
 current = self.head : Sets current to the head of the original bag.
 new_head = Node(current.data) : Creates a new node for the head of the cloned bag
with the same data as the original head.
 new_bag.head = new_head : Sets the head of the cloned bag to the newly created
head node.
 new_size = 1: Initializes the size of the cloned bag to 1, since we've added one
element (the head).
 current = current.next : Moves current to the next node in the original bag.
 The while loop iterates over the remaining nodes in the original bag.
 Inside the loop, it creates a new node for each node in the original bag and links
them together to form the cloned bag.
 Finally, it updates the size of the cloned bag and returns it.

You might also like