In this assignment, you will implement a doubly-linked list class, together
with some list operations. To make things easier, you’ll implement a list
of int
, rather than a template class.
#pragma once
/*
dlist.hpp
Doubly-linked lists of ints
*/
#include <vector>
/* dlist
Doubly-linked list class.
*/
class dlist {
public:
struct node
{
int value;
node* next;
node* prev;
};
/* head(), tail()
Returns the head/tail nodes of the list. These functions are provided
for you.
*/
node* head() const { return hd; }
node* tail() const { return tl; }
/* constructor
You do not need to add any code to the constructor, since lists start
as empty.
*/
dlist() { }
// **************** Implement ALL the following functions ****************
/* destructor
Delete all nodes in the list.
Must run in O(n) time.
*/
~dlist();
/* copy constructor
Construct a copy of the `original` list. The copy must not share any
node pointers with the original list.
Must run in O(n) time.
*/
dlist(const dlist& original);
/* a = b (copy assignment)
Copy all values in `b` into `a`. After the copy, `a` and `b` must not
share any node pointers.
Must run in O(n) time.
*/
dlist& operator= (const dlist& original);
/* at(i)
Returns a pointer to the i-th node (head = index 0, tail = size() - 1).
If i < 0, return the head. If i >= size, return the tail.
Must run in O(i) time.
*/
node* at(int i) const;
/* insert(a, value)
Insert a new node after `a`, containing the `value`. If
`a == nullptr` then insert the new node *before* the head.
Must run in O(1) time.
*/
void insert(node *a, int value);
/* erase(which)
Delete the given node. If which == nullptr, then this function does
nothing.
Must run in O(1) time.
*/
void erase(node* which);
/* erase(i)
Erase the node at index i (indexes work as for at()). If i < 0,
erase the head, if i >= size(), erase the tail.
Must run in O(i) time.
*/
void erase(int i);
/* remove(x)
Remove the *first* node whose value is `x`. If `x` does not occur in the
list, then nothing should be removed. If multiple nodes have `x` as
their values, only the first (closest to the head) should be removed.
Must run in O(n) time.
*/
void remove(int x);
/* push_back(value)
Add a new node containing value at the *end* of the list.
Must run in O(1) time.
*/
void push_back(int value);
/* push_front(value)
Add a new node containing value before the head of the list.
Must run in O(1) time.
*/
void push_front(int value);
/* pop_front()
Remove the first (head) element. If the list is empty, do nothing.
Must run in O(1) time.
*/
void pop_front();
/* pop_back()
Remove the last (tail) element. If the list is empty, do nothing.
Must run in O(1) time.
*/
void pop_back();
/* size()
Returns the size of the list.
Must run in O(n) in the worst case.
*/
int size() const;
/* empty()
Returns true if the list is empty.
Must run in O(1) time.
*/
bool empty() const;
/* to_vector()
Converts the list to a vector containing the same values and returns it.
Must run in O(n) time.
*/
std::vector<int> to_vector() const;
private:
node* hd = nullptr; // Pointer to the first node
node* tl = nullptr; // Pointer to the last node
// Add any other private members you need
};
You can download this header file. On the server, you can
find a copy of the above file in /usr/local/class/src/cs133/dlist.hpp
.
As with other assignments, you can put the implementations for all the class
methods into the .hpp
file if you want, or you can put them in a separate
.cpp
file and compile that with the test file, assign2_test.cpp
.
You can download assign2_test.cpp, the test-runner
for this assignment, and use it to test your code. As before, it provides a main
which will run all the different functions and make sure they are implemented
correctly. However, it’s possible in this assignment that some mistakes might
show up not as failing tests, but as segment violations (or null-pointer
dereferences); i.e., your program will crash instead of the test printing
a FAILED message. Failed tests will include a link back to this webpage, pointing
to a description of the test that was failed (see below).
Note: In this and all future assignments, memory leaks or errors count as
failure, but the test-runner by itself cannot detect these. You must run your
submission inside valgrind
, or use the test-assignment
script to check for
memory leaks/errors.
On the server, you can diagnose segfaults by running your program like this:
catchsegv ./assign2_test
If your program crashes, this will print out a backtrace which shows, among other things, the line number on which the error occurred.
Of course, it’s also possible that your code might mess up the list structure to the point where the test runner gets into an infinite loop. If that happens, look at the most recent message printed for a clue as to where the problem is. (Ctrl-C can be used to kill a program in an infinite loop.)
You can find both these files on the server in the path /usr/local/class/src/cs133
.
E.g. you could create the directory and copy the files with
mkdir ~/cs133/assign2/
cp /usr/local/class/src/cs133/assign2_test.cpp ~/cs133/assign2/
cp /usr/local/class/src/cs133/dlist.hpp ~/cs133/assign2/
Testing for big-O runtimes
The test-runner for assignment 2 contains some experimental code to check whether some of your functions have the required big-O runtime complexities. Due to the imprecise nature of timing software, failures in this area will be reported as warnings rather than hard failures. However, you will want to look into any warnings that are printed in this section; if I see any warnings about incorrect big-O complexities, I will look at your code and it won’t pass if you are using an algorithm with the incorrect runtime complexity.
Things to watch out for
Implementations that do not meet the time complexity requirements. E.g., an \(O(n^2)\) implementation where \(O(n)\) is required. There are tests for \(O(1)\) complexity, but not for \(O(n)\).
Failing to update the
prev
pointers when modifying the list structure. Almost every list-modifying method will have to update theprev
pointer in an existing node.
Submission
Save your work in a directory on the server named cs133/assign2/
.
Invariants
Because this is a doubly-linked list, the class invariants (statements which should always be true) are more extensive than for a singly-linked list:
The empty list: if
head == nullptr
andtail == nullptr
then the list is empty.empty()
should return true, andsize()
should return 0.Single-element list: if
head != nullptr
andtail != nullptr
andhead == tail
then the list has a single node, pointed to by bothhead
andtail
.size()
should return 1.Note that the previous two imply that there is no valid list where
head == nullptr
buttail
is non-null, or vice versa.head
is the first node in the list,tail
is the last; if a list has any nodes at all then it has a first and a last node, even if they are the same node (as in the single-element list).The above are the only two cases where
head == tail
. Ifhead != tail
then both must be non-nullptr
, andsize() ≥ 2
.If
head != nullptr
thenhead->prev == nullptr
always (i.e., there is nothing before the head).If there is a node
a
wherea->next == nullptr
, thena == tail
. (This implies that a list cannot “stop” in the middle; it must be possible to follow thenext
pointers fromhead
totail
.)If there is a node
a
wherea->prev == nullptr
, thena == head
. (This implies that a list cannot “stop” in the middle; it must be possible to follow theprev
points fromtail
tohead
.)If
tail != null
thentail->next == nullptr
always (i.e., there is nothing after the tail).If
a
andb
are two nodes such thata->next == b
, thenb->prev == a
. Note that this implies thata->next->prev == a
as long as botha
anda->next
are non-null.If
a
andb
are two nodes such thatb->prev == a
, thena->next == b
. This implies thatb->prev->next == b
as long asb->prev
is non-null.If
a->next == nullptr
then there must not be a node in the list whose->prev == a
. (If nothing comes aftera
, then no other node should think that it comes aftera
.)If
a->prev == nullptr
then there must not be a node in the list whose->next == a
. (If nothing comes beforea
, then no other node should think that it comes before a.)It should never be the case that
a->next == a
ora->prev == a
.As an extension of this, it should never be the case that there is a sequence of nodes
a₀, a₁, … aₙ
such thata₀->next == a₁
,a₁->next == a₂
, …aₙ₋₁->next == aₙ
, butaₙ->next == a₀
, and similarly for->prev
. That is, no node should have an->next
which points backwards to an earlier node in the list, and no node should have an->prev
which points forward to a later node in the list.
Tests
The verify()
function performs some basic checks on the structure of the list,
to make sure that it is a correctly-formed doubly-linked list. verify()
is
called in many places, so you may see these tests pop up while testing other
parts of the system.
Tests for “empty” lists
These tests apply when the verify()
function thinks that your list is
empty, typically because one or both of head, tail
is nullptr
.
Empty list (head == tail == nullptr
) but list.size() != 0
.
If a list has both its head
and tail
null, then it should be the empty
list and should have a size of 0, but the size()
method returned a non-zero
value (the actual value returned will be printed with the test message).
This most likely indicates a problem with your .size()
method.
head
is nullptr
but tail
is not
If one of head
or tail
is nullptr
, then they both should be; there is no
valid list where the head does not exist but the tail does. This is
probably a problem with either insert
or remove
, as those are the
only functions that could mutate the head and/or the tail.
tail
is nullptr
but head
is not
If one of head
or tail
is nullptr
, then they both should be; there is no
valid list where the tail does not exist but the head does. Again, this
is probably a problem in insert
or remove
.
Tests for single-element lists
These tests apply when verify
thinks that your list has one element,
typically because head == tail
, with neither == nullptr
.
head->next == nullptr
, but head != tail
If head->next == nullptr
, then head
should be the last node in the list;
i.e., the list has only one element, which should also be the tail of the list.
But in this list, head != tail
. This is probably a problem in
insert
or remove
.
tail->prev == nullptr
, but head != tail
<
Similar to the previous, if tail->prev == nullptr
then tail
is the first
node of the list, and hence there should only be one node, and it would be both
the head and the tail. But somehow, head != tail
. This is probably a problem
in insert
or remove
.
head == tail
(!= nullptr
), but size() != 1
If head == tail
and neither is nullptr
, then the list should have a single
element, but the size()
method is not returning 1. This is probably
a problem in size()
.
head == tail
(!= nullptr
), but node->next != nullptr
If head == tail
and neither is nullptr
, then the list should have a single
element, and both its next
and prev
should be nullptr
(because if it is
the only element, then there is nothing before it, and nothing after it). But
in this case, the single node’s ->next
is non-null. This is probably a
problem in insert
or remove
.
head == tail
(!= nullptr
), but node->prev != nullptr
If head == tail
and neither is nullptr
, then the list should have a single
element, and both its next
and prev
should be nullptr
(because if it is
the only element, then there is nothing before it, and nothing after it). But
in this case, the single node’s ->prev
is non-null. This is probably a
problem in insert
or remove
.
Tests on lists with ≥ 2 elements
These tests apply when verify
thinks that your list has at least two
elements, usually because head != tail
and neither is nullptr
.
head != tail
, but list.size() ≤ 1
If head != tail
(neither null), then the list should have at least two
elements, but the value returned by size()
is 1 or 0. This could be
a problem with insert
or remove
, or with size
.
head->prev != nullptr
If head
is non-null, its ->prev
should always be nullptr
(there is
nothing before the head). This is probably a problem with insert
or
remove
not correctly updating head->prev
when inserting before or
removing the head.
tail->next != nullptr
If tail
is non-null, its ->next
should always be nullptr
(there is
nothing after the tail). This is probably a problem with insert
or
remove
not updating tail->next
when adding or removing the tail.
head != tail
, but head->next == nullptr
If head != tail
then the list should have at least two elements, but
head->next == nullptr
, as if there was nothing after the head. This is
probably a problem with insert
or remove
.
head != tail
, but tail->prev == nullptr
If head != tail
then the list should have at least two elements, but
tail->prev == nullptr
, as if there was nothing before the tail. This is
probably a problem with insert
or remove
.
Found a non-tail node with n->next == nullptr
Only the tail node should have its ->next == nullptr
; this indicates that
it is not possible to traverse the list in a forward direction (starting at the
head and following the ->next
pointers) and reach the tail. This is
probably a problem with insert
or remove
.
Found a non-head node with n->prev == nullptr
Only the head node should have its ->prev == nullptr
; this indicates that it
is not possible to traverse the list in a reverse direction (starting at the
tail and following the ->prev
pointers) and reach the head. This is
probably a problem with insert
or remove
.
n->prev->next
does not point back to n
For any internal node (node which is not the head or the tail) n
,
n->prev->next
should always point back to n
itself. This is
probably a problem with insert
or remove
.
n->next->prev
does not point back to n
For any internal node (node which is not the head or the tail) n
,
n->next->prev
should always point back to n
itself. This is
probably a problem with insert
or remove
.
Found a node x where next == prev
It should never be the case at any node that ->next
points to the same node
as ->prev
(no node can be both before and after something). This is
probably a problem with insert
or remove
.
Found a node x where n->prev == n
This indicates that there is a node whose ->prev
pointer points back to the
node itself; this should never happen in a well-formed list. This is
probably a problem with insert
or remove
.
Found a node x where n->next == n
This indicates that there is a node whose ->next
pointer points back to the
node itself; this should never happen in a well-formed list. This is
probably a problem with insert
or remove
.
Could not traverse the list from head to tail
The test code attempted to use a loop of the form
for(dlist::node* n = l.head(); n != l.tail(); n = n->next)
to traverse the list from head to tail, but it reached a point where
n == nullptr
before reaching the tail. This is
probably a problem with insert
or remove
.
Could not traverse the list from tail to head
The test code attempted to use a loop of the form
for(dlist::node* n = l.tail(); n != l.head(); n = n->prev)
to traverse the list from tail to head, but it reached a point where
n == nullptr
before reaching the head. This is
probably a problem with insert
or remove
.
List has a cycle
A cycle is a list where some node’s ->next
points back to an earlier node
in the list, or the node’s ->prev
points forward to a later node in the list.
If we try to loop over the nodes in this list, we will get stuck in an infinite
loop, visiting the same nodes over and over. This is
probably a problem with insert
or remove
.
The remaining tests apply to either specific kinds of lists, or specific operations.
Tests on the empty list
There is really only one possible test on the empty list.
empty list is not .empty()
When we first construct a list, it should contain no nodes, so .empty()
should
return true, but it is returning false.
Insertion tests
To test the insert()
method, we test inserting a series of values at the
head
(since the head
will be nullptr
initially, this also tests
insert(nullptr)
), inserting a series of values at the tail
, and finally
inserting a series of values in the middle of a list. After each insert
,
the verify
function is run, so any of the above verification tests may trigger
at this stage.
size() != 1
after inserting one element into an empty list
If we insert a single element into an empty list, the size()
of the resulting list should be 1, but it is not.
After inserting one element into an empty list, head() is nullptr
If we insert a single element into an empty list, then head()
should return
a non-nullptr
value (i.e., a pointer to the newly-inserted node). Since the
head()
function is provided for you, this probably means that your insert()
is failing to update the hd
member.
After inserting one element into an empty list, tail() is nullptr
If we insert a single element into an empty list, then tail()
should return
a non-nullptr
value (i.e., a pointer to the newly-inserted node). Since the
head()
function is provided for you, this probably means that your insert()
is failing to update the tl
member.
After inserting one element into an empty list, head() != at(0)
After inserting a single element into the empty list, at(0)
should return
a pointer to this new element, which should also be the head()
of the list.
This may indicate a problem with your insert()
, or with your at()
functions.
After inserting one element into an empty list, tail() != at(0)
After inserting a single element into the empty list, at(0)
should return
a pointer to this new element, which should also be the tail()
of the list.
This may indicate a problem with your insert()
, or with your at()
functions.
After inserting one element into an empty list, .at(0) is nullptr
After inserting a single element into the empty list, at(0)
should return
a pointer to this new element. This most likely indicates a problem with your
at()
function.
After inserting 100 into an empty list, l.at(0) ≠ 100
If we insert 100 into an empty list, then the value of the head (and tail)
should be equal to 100. This could be a problem with insert
, or with your
at()
method, since the test accesses the head as l.at(0)
. It might also
indicate that you are not correctly setting the ->value
of the node when
you create it.
After inserting n elements, list.size() ≠
n
(After insert(nullptr,x)
) Starting from the empty list, we insert n elements.
The resulting list should
have size n, but it does not. This may indicate a problem with your size()
function; if you added a sz
data member, this may indicate that you forgot to
increment it in insert()
.
After inserting x, the contents of the list are incorrect
If we insert the values 0, 1, …, 8, 9 into an empty list at nullptr
(i.e.,
insert(nullptr,x)
, we should end up with a list containing
9, 8, 7, 6, 5, 4, 3, 2, 1, 0
(insert(nullptr,x)
is equivalent to push_front(x)
, hence the reverse order.)
After inserting n elements, list.size() ≠
n
(After insert(head(), x)
) Starting from the empty list, we insert n elements.
The resulting list should
have size n, but it does not. This may indicate a problem with your size()
function; if you added a sz
data member, this may indicate that you forgot to
increment it in insert()
.
After inserting x, the contents of the list are incorrect
If we insert the values 0, 1, …, 8, 9 into an empty list at the head()
, we
should end up with a list containing
0, 9, 8, 7, 6, 5, 4, 3, 2, 1
(The odd order is because the first insert
will insert the 0, but subsequent
inserts will add new values after the head.) However, the resulting list does
not contain these element (the actual test message will include the contents of
the list, for comparison).
After inserting n elements, list.size() ≠
n
(After insert(tail(), x)
) Starting from the empty list, we insert n elements.
The resulting list should
have size n, but it does not. This may indicate a problem with your size()
function; if you added a sz
data member, this may indicate that you forgot to
increment it in insert()
.
After inserting x, the contents of the list are incorrect
If we start from an empty list and insert 0, 1, … 8, 9 at the tail, the resulting list should contain
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
but it does not. (The actual test message will include the contents of the list, for comparison.)
After inserting x, the contents of the list are incorrect
If we start from the list
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
and we insert 100, 101, …, 118, 119 between elements 5 and 6, then the resulting list should contain
0, 1, 2, 3, 4, 5, 100, 101, … 118, 119, 6, 7, 8, 9, 10
but it does not.
After inserting 31 elements, list.size() ≠ 31
The list
0, 1, 2, 3, 4, 5, 100, 101, … 118, 119, 6, 7, 8, 9, 10
should have size 31, but size()
returned some other value.
Tests of erase(node*)
These tests begin by constructing the list 0, 1, 2, ..., 28, 29
.
After erasing x
at head, list contents are incorrect
(In this step, we try to erase
elements 0 through 9 of the list.)
We called erase(head())
and afterwards the contents of the list are not
what they should be. This usually indicates a problem with your erase(node*)
function, possibly the case where the head is erased.
After erasing x
at tail, list contents are incorrect
(In this step, we try to erase
elements 20 through 29 of the list.)
We called erase(tail())
and afterwards the contents of the list are not
what they should be. This usually indicates a problem with your erase(node*)
function, possibly the case where the tail is erased.
After erasing x
in the middle, list contents are incorrect
(If the previous two tests were successful, we are left with the list 10, 11, …, 19.)
We called erase()
on a node in the middle of the list (value = 15
) and
afterwards the contents of the list are not
what they should be. This usually indicates a problem with your erase(node*)
function.
After erasing all elements, list is not empty()
We constructed a list containing 10 elements, and then called erase(head())
10 times. The resulting list should be empty, but it is not.
Tests of erase(index)
As above, we begin by constructing a list containing values 0, 1, 2, …, 29.
After erasing x
at head, list contents are incorrect
We call erase(0)
10 times; after one of these times (given by x
), the
list contents were not correct.
After erasing x
at tail, list contents are incorrect
We call erase(size()-1)
10 times; after one of these times, the list contents
were not correct.
After erasing 15 in the middle, list contents are incorrect
If the previous two tests were successful, we should be left with a list
containing 10, 11, 12, …, 19. We call erase(5)
, which should erase the
node containing 15, but the resulting list is incorrect.
After erasing all elements, list is not empty()
We call erase(0)
until the list should be empty, but when we’re done, the
list is not empty.
After erasing at index i
list contents are incorrect
We construct a new list containing 0, 1, 2, …, 29. Then we erase
various
negative indexes; the specification for erase(i)
states that if i < 0
then
the head should be erased. However, the list contents are not correct.
After erasing at index i
list contents are incorrect
Again, we construct a new list and then erase(i)
for various i ≥ size()
.
The specification for erase
states that if i ≥ size()
then the tail should
be erased. However, the list contents are not correct.
Tests of remove(x)
After removing x at head, list contents are incorrect
If we start from the list 0, 1, 2, …, 28, 29 and remove 10 elements from the head, the resulting list should contain
10, 11, 12, … 28, 29
but it does not. (The test will print the actual contents of the list, for comparison.)
After removing x at tail, list contents are incorrect
If we start from the list 10, 11, …, 28, 29 and remove 10 elements from the tail, the resulting list should contain
10, 11, ..., 18, 19
but it does not.
After removing x in the middle, list contents are incorrect
If we start from the list 10, 11, …, 18, 19 and remove 15, the resulting list should contain
10, 11, 12, 13, 14, 16, 17, 18, 19
but it does not.
After removing all elements, list is not empty()
If we start with the list 10, 11, 12, 13, 14, 16, 17, 18, 19 and we remove the 9 remaining elements, the resulting list should be empty, but somehow it is not.
After removing x (which does not exist), list contents are incorrect
remove(x)
for an x
which does not exist in the list should not change the
list contents, but it did.
After removing 2, list contents are incorrect
We construct a list containing
1, 2, 3, 2, 4
and then call remove(2)
. This should remove only the first instance of 2,
leaving the list as 1, 3, 2, 4
but the list contents are incorrect.
push_back
, push_front
tests
Last push_back’d value = x, but tail ≠
x
The tail’s value should always equal the most-recently push_back
‘d value,
but it does not.
After push_back
'ing n values, size ≠
n
If we start from an empty list and push_back
n values, the size of the
resulting list should be n, but it is not.
After push_back
'ing 0, 1, 2, …, 18, 19, list does not contain correct elements
If we start with the empty list and push_back
the values 0, 1, …, 18, 19,
the resulting list should contain
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19
but it does not. The actual test message will include a print-out of the list, for comparison.
After push_front
'ing x, head->value ≠
x
After every push_front(x)
, the value of the head should be equal to x, but
it is not.
After push_front
'ing n values, size() ≠
n
If we start from an empty list and push_front
n values, the resulting list
should have size n, but it does not.
After push_front
'ing 0, 1, 2, …, 18, 19, list does not contain correct elements
If we start from the empty list and push_front
the values 0, 1, 2, …, 18, 19,
the resulting list should contain
19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0
but it does not. The actual test message will include a print-out of the list contents for comparison.
pop_front
, pop_back
tests
List should have n elements, but size() ≠
n
If we start from the list
0,1,2,3,4,5,6,7,8,9
and we pop_front
n elements, then the resulting list should have size
10 - n, but it does not.
After pop_front
'ing all elements, the list is not empty()
.
If we pop_front()
all 10 elements from a list of size 10, the resulting
list should be empty()
but it is not.
List should have n elements, but size() ≠
n
If we start with a list of 10 elements and perform n pop_back()
s, the
resulting list should have 10 - n elements, but it does not.
Tests for the to_vector
function
After running to_vector()
on the empty list, the resulting vector is not empty
We create an empty list and then call to_vector()
; the resulting vector should
be empty, but it is not.
to_vector()
` did not return 0, 1, 2, … 9
We construct a list containing 0, 1, 2, …, 9 and then call to_vector()
on
it; the resulting vector contains the wrong values.
Tests for copy-construction
Note: “Testing” for memory leaks/corruption is done by the test-assignment
script, which will run the test-runner inside valgrind
.
After copy-constructing a copy of 0, 1, 2, …, 9, copy has the wrong values
We construct a list a
containing 0, 1, 2, …, 9, and then we copy-construct
b
:
dlist b = a;
b
should contain the same values as a
, but it does not.
After copy-constructing a copy of 0, 1, 2, …, 9, copy shares node*
with the original
As above, we construct b
as a copy of 0, 1, 2, …, 9. The copy should have
its own nodes, not sharing any with the original, but at least one node*
occurs in both the copy and the original. (This will probably also result in
a crash with a double-free error.)
After copy-construction, size of copy is m
but size of the original is n
(m != n
)
As above, we copy-construct a copy of 0, 1, 2, …, 9. size()
on the copy
should return 10 (same as the size of the original) but it did not.
Tests for copy-assignment
We begin by constructing a list a
containing 0, 1, 2, …, 9, and a list
b
containing 10, 11, 12, …, 19.
After a = b, a is different from b
After performing the copy-assignment statement a = b;
, a
should contain
the same elements as b
, but it does not.
After a = b, a and b share some node pointers.
No node*
should be shared between a
and b
after the copy-assignment, but
at least one node*
occurs in both lists. (This will probably also result in
a crash with a double-free error.)
After a = b, a and b have different sizes
After copy-assignment a.size()
should be equal to b.size()
but they are
different. (Note that in this case, a
and b
had the same size before the
copy.)
After a = b, a should contain 10, 11, 12, …, 19 but does not
After a = b;
, a
should contain 10, 11, 12, .., 19, but its elements are
incorrect (the test will print the contents of a
for comparison).
The next four tests are performed with a = 0, 1, 2, .., 9
, b = 0, 1, ..., 19
,
i.e., a.size() == 10
but b.size() == 20
.
After a = b, a is different from b
After performing the copy-assignment statement a = b;
, a
should contain
the same elements as b
, but it does not.
After a = b, a and b share some node pointers.
No node*
should be shared between a
and b
after the copy-assignment, but
at least one node*
occurs in both lists. (This will probably also result in
a crash with a double-free error.)
After a = b, a and b have different sizes
After copy-assignment a.size()
should be equal to b.size()
but they are
different. (Note that in this case, a
and b
had the same size before the
copy.)
After a = b, a should contain 0, 1, 2, …, 19 but does not
After a = b;
, a
should contain 0, 1, 2, .., 19, but its elements are
incorrect (the test will print the contents of a
for comparison).
After self-assignment (a = a), size is incorrect; expected size 10
We construct a list a = 0, 1, 2, ..., 9
and then perform
a = a;
This should leave a
unchanged, but the size has changed from the original.
After self-assignment (a = a), a is empty
Again, we construct a = 0, 1, 2, ..., 9
and then perform a = a;
. This
should leave a
unchanged, but afterwards a
is empty.
Tests for big-O complexity (O(1))
These tests attempt to check insert
, push_back
and push_front
to see
if they run in O(1) time. This is somewhat imprecise, so failures in this area
are reported as warnings. If you get a warning that an operation does not
run in O(1) time, try re-running the tests; only consistent failures should
warrant more investigation.
Only three operations are tested for O(1)-ness: insert
, push_front
and
push_back
. We only test these three because they all increase the size of the
list, so we can simply run them in a loop and see if they get slower as the size
of the list increases.
insert()
does not appear to run in O(1) time
insert()
was called repeatedly, measuring the time taken for each call. As
the size of the list increases, the runtime of insert
should not change, but
it appears to getting slower (increasing linearly with the size of the list).
push_back()
does not appear to run in O(1) time
push_back()
was called repeatedly, measuring the time taken for each call. As
the size of the list increases, the runtime of insert
should not change, but
it appears to getting slower (increasing linearly with the size of the list).
push_front()
does not appear to run in O(1) time
push_front()
was called repeatedly, measuring the time taken for each call. As
the size of the list increases, the runtime of insert
should not change, but
it appears to getting slower (increasing linearly with the size of the list).
Total tests: 83 (some tests are performed more than once)