current = head;
//stop if we reach the end or find it
while (current != null
&& !cureent.data.euals(it)) {
current = current.next;
counter++; //we just passed a node
}
//if the list is empty,
//or we went to the end
if (current == null)
return -1;
return counter;
}
```
---
template: find-final
.left-column2[
.red[
- __What happens when `it == null`?__
- __What happens if we allow elements stored in the nodes to be `null`?__
]
]
---
template: section
## Removing From a Linked List
---
## Removing From a Linked List
In order to remove an element from a list, we first need to determine if it is
actually stored in the list.
--
As with adding to the list, we will need to consider a few possible scenarios:
- removing an element that is stored in the first node
- removing an element that is stored in the last node
- removing an element that is store in a node somewhere in between
--
The final algorithm has to work if we call it on an empty list, one-elements list
or a list with many nodes.
---
name: remove-front
## Removing from the Front of a List
In these steps we will assume that there is at least one node, so that we have something
to remove.
---
template: remove-front
.center[
]
---
template: remove-front
.center[
]
.center80[
`head = ???`
]
---
template: remove-front
.center[
]
.center80[
`head = head.next`
]
---
template: remove-front
.center[
]
.center80[
`head = head.next`
Do we need to do anything to get rid of the actual node that is no longer part of the list?
]
---
template: remove-front
.center[
]
.center80[
`head = head.next`
Do we need to do anything to get rid of the actual node that is no longer part of the list?
- this depends on the programming language
- in Java, as long as there are no more references to a given object, the Java garbage collection will take care of
releasing the memory (i.e., marking it available for reuse)
]
---
template: remove-front
.center[
]
.center80[
`head = head.next`
Would this work, if we had a list with one element?
]
--
.center80[
- almost,
- if we removed the very last node, `head` is now set to `null`, but `tail` might still
be pointing to the removed node
]
--
.center80[
```
if (head == null )
tail = null;
```
]
---
name: remove-back
## Removing the Last Node
In these steps we will assume that there is at least one node, so that we have something
to remove.
---
template: remove-back
.center[
]
---
template: remove-back
.center[
]
.center80[
In order to remove the last node, we need to have a reference to the node right before.
]
---
template: remove-back
.center[
]
.center80[
In order to remove the last node, we need to have a reference to the node right before.
That's because we need to change where the `next` reference in that _one before_ node points to:
`current.next = ???`
]
---
template: remove-back
.center[
]
.center80[
In order to remove the last node, we need to have a reference to the node right before.
That's because we need to change where the `next` reference in that _one before_ node points to:
`current.next = ???`
]
---
template: remove-back
.center[
]
.center80[
In order to remove the last node, we need to have a reference to the node right before.
That's because we need to change where the `next` reference in that _one before_ node points to:
`current.next = null`
]
--
.center80[
Is this all we need to do? Or are there more steps needed?
]
---
template: remove-back
.center[
]
.center80[
In order to remove the last node, we need to have a reference to the node right before.
That's because we need to change where the `next` reference in that _one before_ node points to:
`current.next = null`
]
.center80[
Is this all we need to do? Or are there more steps needed?
- the `tail` reference is no longer pointing to the last node, we need to fix this
]
---
template: remove-back
.center[
]
.center80[
In order to remove the last node, we need to have a reference to the node right before.
That's because we need to change where the `next` reference in that _one before_ node points to:
`current.next = null`
]
.center80[
Is this all we need to do? Or are there more steps needed?
- the `tail` reference is no longer pointing to the last node, we need to fix this
`tail = current; `
]
---
template: remove-back
.center[
]
.center80[
In order to remove the last node, we need to have a reference to the node right before.
That's because we need to change where the `next` reference in that _one before_ node points to:
`current.next = null`
]
.center80[
Is this all we need to do? Or are there more steps needed?
- the `tail` reference is no longer pointing to the last node, we need to fix this
`tail = current; `
]
---
template: remove-back
.center[
]
.center80[
Would this work, if we had a list with one element?
]
--
.center80[
- no, because with one node, there is no node before the one that we are removing
]
--
.center80[
- if the list has only one element, we need to set both `head` and `tail` to `null`
```
if (head.next == null ) {
head = null;
tail = null;
}
```
]
---
name: remove-middle
## Removing a Node from Within a List
In these steps we will assume that there is at least one node, so that we have something
to remove.
---
template: remove-middle
.center[
]
---
template: remove-middle
.center[
]
.center80[
Once again, in order to remove a node, we need to have a reference to
the node right before.
]
---
template: remove-middle
.center[
]
.center80[
Once again, in order to remove a node, we need to have a reference to
the node right before.
That's because we need to change where the `next` reference in that
_one before_ node points to:
`current.next = ???`
]
--
.center80[
We want to have `current.next` store a memory address of the node after
the one that we are trying to delete. Where is that address stored?
]
---
template: remove-middle
.center[
]
.center80[
Once again, in order to remove a node, we need to have a reference to
the node right before.
That's because we need to change where the `next` reference in that
_one before_ node points to:
`current.next = ???`
]
.center80[
We want to have `current.next` store a memory address of the node after
the one that we are trying to delete. Where is that address stored?
`current.next = current.next.next;`
]
---
template: remove-middle
.center[
]
.center80[
Is this all we need to do?
- pretty much
- we won't worry about the case of a list with one element, since then it is
really removing the first/last node
]
---
template: remove-middle
.center[
]
.center80[
Java garbage collector will eventually reclaim the memory allocated for the node
that was deleted.
]
---
template:section
# Performance Analysis
---
## What is the performance of the operations that we discussed?
Assume we have __N__ elements in the list.
_Note that asymptotic analysis only makes sense when N is very large
and as it is growing to infinity._
--
- add at a specified position
--
__O(N)__
--
- adding to the front/back __O(1)__
--
- remove from a specified position
--
__O(N)__
--
- remove from the front __O(1)__
--
- remove from the back
--
__O(N)__
--
- find the position/index of an element
--
__O(N)__
--
- get the number of elements in the list
--
__O(1)__
--
- empty the list
--
__O(1)__
---
template:section
# Examples and Things to Think About
---
## Implementing `size()` and `clear()`
Provide the implementation for these two methods from our list interface:
```java
// returns the current number of elements in the list
public int size();
// removes all elements from the list, so it is once again empty
public void clear();
```
-----
Now, let's assume that for some unknown reason the `LinkedList` class implementation
does not have a variable that keeps track of the size of the list (this is not
a wise decision, but we can do that for the sake of an exercise).
Rewrite your `size()` method so that it can determine the number of elements
in the list without using the `size` data field.
---
## Iterating through the list
We talked about iterating through the list till the very end (or past the end)
and about stopping at the last node.
Write the code that would iterate through the list and stop at the second to last node.
Make sure that your code works without crashing if the list is empty or if it has only
one element. In such cases the iteration should never happen and `current` should
be set to `null`.
What application might this kind of iteration have?
---
## Implementing `get()`
The only remaining unimplemented method from our list interface
is the `get()` method:
```java
// retrieves and returns an item from position pos
// throws NoSuchElementException if pos < 0 or pos >= size
public E get( int pos) throws NoSuchElementException;
```
Implement it. In what way is it similar to the `find()` method?
---
## Alternative `find()`
The `find()` in our interface returns the index of the element. There is
often similar methods called `contains` that determines if the element
is in the list or not and returns `true` or `false` accordingly.
Implement such a method.
---
## Putting it all together for `remove()`
We talked about different scenarios for removing an element from a list.
Use these code fragments and ideas to actually implement the `remove()`
method that was defined in our interface:
```Java
// removes and returns an element at position pos, shifts elements starting
// at pos+1 by one to the left (lower position values)
// throws NoSuchElementException if pos < 0 or pos >= size
public E remove(int pos) throws NoSuchElementException;
```
Assume that there is a `size` data field that keeps track of the exact number of
elements in the list. And, of course, the `LinkedList` class has data
fields that store references to the first node and to the last node:
`head` and `tail`.
Make sure that your function works when the list is empty, when it has only one element,
and when it has many elements.
---
## Adding Numbers
Imagine that we are working on a program that uses short linked lists to represent positive integers.
In this representation, the 1's digit is stored in the first node, the 10's
digit is stored in the second node, and so on. So a numbers 1,673 would be represented by
`3 -> 7 -> 6 -> 1`
Write a function that given two such numbers, calculates and returns their sum (as a linked list).
You do not need to implement a list itself, but you won't be really be able
to test your add function without at least a minimal class to represent these numbers.