CPSC170 Lab 9

More Linked Lists

Today, we will extend the capabilities of the our existing linked list class. Thus far, you have simply been able to remove elements from one of the two ends of the list. Today, you will implement code that can remove an arbitrary element from the list.

Removing Nodes

To remove an element from the front of the list, you simply moved the head reference. This way, when you printed the list, you would skip over that front element. Removing an arbitrary node is very similar. Consider the following list:

Assume we wanted to remove the node containing the data "4" from said list. We simply want to make any node that "points" to that node to point around that node:

In order to accomplish this, we first need to find the node that "points" to the node who contains the data that we are trying to remove. You can simply perform a linear search through the list, until the data in the next node is the element to be removed. Finally, you can set that found nodes next reference to the node following the node being removed.

Define a method in your linked list class called remove(self, element). remove should take as a parameter the data in the node to be removed. If element is not contained within the list, remove should raise a ValueError. Otherwise, you should remove the correct node from the list, and decrement the size counter.

Make sure you thoroughly test your remove method. Can you successfully remove the first or last elements using remove? What happens if you try to remove something from an empty list? Your code should gracefully handle all of these scenarios.

Iterators

Right now, your linked list is largely complete. However, traversing the list is a bit of a pain. You create a variable called current, and continually move the current pointer to until it reaches to the end of the list. Wouldn't it be nice if you could do something more akin to this:

  linked_list = LinkedList()
  ...
  for i in linked_list:
      print(i)

Luckily, you can. And it is not too difficult.

In order to use the above for loop syntax, your class needs to create an iterator. An iterator is a special object that allows for the traversal of some specified container, such as a linked list. In Python, anything that overrides the __next__ method are considered iterators.

The __iter__ returns some object which contains an implementation of the __next__ method. This __next__ continually returns the next item in the container, until there are no more items to return. It then raises a StopIteration exception, which is handled internally in python.

In your linked list class, define a private internal class called _Iterator. The constructor for this class should take a reference to the linked list to iterate over, and declare any attributes necessary to traverse the linked list. You should define a method called __next__ within this class. Each call to this method should return the next data from the linked list.

Define a method in your LinkedList class called __iter__, which simply constructs an instance of your private _Iterator class, and returns it. At this point, you should be able to iterate over the list using the above syntax.

For the purposes of this lab, you do not have to worry about some more complex issues related to iterators. One that you may run into as you use this iterator, is that if you remove items from the list, they may still exist as far as the iterator is concerned. Bonus points will be provided for any iterator implementations that solve this issue in particular.

Sieve of Eratosthenes

Eratosthenes was a Greek mathematician and astronomer from the 2nd century B.C.E. He is widely known as being the one of the first humans to estimate the circumference of the Earth, with an error of less that 2%. Another contribution of Eratosthenes is the Sieve of Eratosthenes, a mechanism for computing a list of primes up to a specified number.

The algorithm for generating such a list of primes is fairly straight forward. Consider a list of numbers numbers in the range [2,n]. The first item in the list is always the next prime p. p should be removed, and stored in the current list of known primes. Then, every multiple of p should be removed from the list numbers.

Using your LinkedList, you can implement this algorithm to get a list of primes up to n. First, populate the numbers list with all of the numbers from 2 until n. Also, create an empty list called primes. Remove the first element, and put it in the primes list. Then, remove all of the multiples of n in the list numbers. You can continue to remove a prime and its multiples, until there are no more elements in the numbers list to remove.

After this algorithm is complete, the list primes contains all of the prime numbers up to n. This list can be used to compute the prime factorization of numbers up to n. The faster that such a list can be generated, the faster numbers can be factored. Time the Eratosthenes algorithm. What does its Big-O class look like? Compare this algorithm to the old, brute-force algorithm we discussed way back in week 1. Is this one faster?

Submission

Submit a zip file of your code on the course Inquire site that uses your last names as the file name.