renaming labs
This commit is contained in:
77
labs/list_implementation/README.md
Normal file
77
labs/list_implementation/README.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Lab 7 — List Implementation
|
||||
|
||||
## Checkpoint 1
|
||||
*estimate: 20-30 minutes*
|
||||
|
||||
The implementation of the dslist class in [dslist.h](dslist.h) is incomplete. In particular, the class is missing the destroy_list
|
||||
private member function that is used by the destructor and the clear member function. The provided test
|
||||
case in [checkpoint1.cpp](checkpoint1.cpp) works “fine”, so what’s the problem?
|
||||
|
||||
Before we fix the problem, let’s use Dr. Memory and/or Valgrind to look at the details more carefully.
|
||||
You should use the memory debugging tools both on your local machine and by submitting the files to
|
||||
the homework server. Study the memory debugger output carefully. The output should match your
|
||||
understanding of the problems caused by the missing destroy_list implementation. Ask a TA if you
|
||||
have any questions.
|
||||
|
||||
Now write and debug the destroy_list function and then re-run the memory debugger (both locally and on
|
||||
the submission server) to show that the memory problems have been fixed. Also finish the implementation
|
||||
of the push_front, pop_front, and pop_back functions.
|
||||
|
||||
**To complete this checkpoint**, show a TA the implementation and memory debugger output before and
|
||||
after writing destroy_list.
|
||||
|
||||
## Checkpoint 2:
|
||||
*estimate: 20-30 minutes*
|
||||
|
||||
The PushBack() and the PrintList() function are used in [checkpoint2.cpp](checkpoint2.cpp), but their definitions are missing, please complete these two functions and make sure the program runs and produces the following output.
|
||||
|
||||
```console
|
||||
$ g++ checkpoint2.cpp
|
||||
$ ./a.out
|
||||
Linked List of NodeA nodes: 1 -> 2 -> 3 -> 4 -> 5 -> nullptr
|
||||
Linked List of NodeB nodes: 1 -> 1.41421 -> 1.73205 -> 2 -> 2.23607 -> nullptr
|
||||
```
|
||||
|
||||
**Note**: Hardcoding the PrintList() function to just print the above two messages is strictly prohibited. Also, your functions must be templated functions.
|
||||
|
||||
**To complete this checkpoint**, show a TA the implementation and the output of your program.
|
||||
|
||||
## Checkpoint 3: Merge Two Lists.
|
||||
*estimate: 30-40 minutes*
|
||||
|
||||
Given two doubly-linked lists: linked list A and linked list B, and both linked lists are sorted. Data in linked list A is sorted in an ascending order. Data in linked list B is also sorted in an ascending order. Merge these two lists such that data in the merged list is still sorted in an ascending order.
|
||||
|
||||
More specifically, complete the mergeLists() function in [checkpoint3.cpp](checkpoint3.cpp), such that the program prints the following output.
|
||||
|
||||
```console
|
||||
$ g++ checkpoint3.cpp
|
||||
$ ./a.out
|
||||
1 3 5 7 9
|
||||
2 4 6 8 10
|
||||
1 2 3 4 5 6 7 8 9 10
|
||||
10 9 8 7 6 5 4 3 2 1
|
||||
```
|
||||
|
||||
**To complete this checkpoint**, explain to a TA your implementation and show the output of your program.
|
||||
|
||||
<!--TODO: how about memory leaks?-->
|
||||
|
||||
<!--## Checkpoint 3: Debugging a Merge Sort program.
|
||||
*estimate: 30-40 minutes*
|
||||
|
||||
We expect our program [checkpoint3.cpp](checkpoint3.cpp) to produce the following results when it is compiled and run.
|
||||
|
||||
```console
|
||||
$ g++ checkpoint3.cpp
|
||||
$ ./a.out
|
||||
Test Case 1: Original Vector: 5 2 9 1 5 6
|
||||
Sorted Vector: 1 2 5 5 6 9
|
||||
|
||||
Test Case 2: Original Vector: 3 8 2 7 4
|
||||
Sorted Vector: 2 3 4 7 8
|
||||
|
||||
```
|
||||
|
||||
But this program currently does not behave as expected. Troubleshoot this program, find the problems and fix them. You can use a debugger.
|
||||
|
||||
**To complete this checkpoint**, explain to a TA the bugs you found, show a TA your fixes and run the program to show that your fixes are correct and the program now produces the expected results.-->
|
||||
73
labs/list_implementation/checkpoint1.cpp
Normal file
73
labs/list_implementation/checkpoint1.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <list>
|
||||
|
||||
#include "dslist.h"
|
||||
|
||||
int main() {
|
||||
|
||||
// =======================================
|
||||
// CHECKPOINT 1
|
||||
|
||||
// create a list of the sqrt of the first 10 integers
|
||||
dslist<double> a;
|
||||
for (int i = 0; i < 10; ++i)
|
||||
a.push_back(sqrt(i));
|
||||
|
||||
|
||||
// print out details of the list
|
||||
assert (a.size() == 10);
|
||||
assert (a.front() == 0);
|
||||
assert (a.back() == 3);
|
||||
dslist<double>::iterator itr;
|
||||
std::cout << "Elements = ";
|
||||
for (itr = a.begin(); itr != a.end(); ++itr)
|
||||
std::cout << " " << *itr;
|
||||
std::cout << std::endl;
|
||||
|
||||
// clear out the list
|
||||
a.clear();
|
||||
|
||||
/*
|
||||
assert (a.size() == 0);
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
// simple tests of push_front, pop_front, and pop_back
|
||||
a.push_front(5);
|
||||
a.push_back(7);
|
||||
a.push_front(3);
|
||||
a.push_back(9);
|
||||
assert (a.size() == 4);
|
||||
|
||||
assert (*(a.begin()) == 3);
|
||||
assert (*(++a.begin()) == 5);
|
||||
assert (*(++(++a.begin())) == 7);
|
||||
assert (*(++(++(++a.begin()))) == 9);
|
||||
|
||||
std::cout << "Elements = ";
|
||||
for (itr = a.begin(); itr != a.end(); ++itr)
|
||||
std::cout << " " << *itr;
|
||||
std::cout << std::endl;
|
||||
|
||||
a.pop_back();
|
||||
a.pop_front();
|
||||
assert (a.size() == 2);
|
||||
assert (*(a.begin()) == 5);
|
||||
assert (*(++a.begin()) == 7);
|
||||
|
||||
std::cout << "Elements = ";
|
||||
for (itr = a.begin(); itr != a.end(); ++itr)
|
||||
std::cout << " " << *itr;
|
||||
std::cout << std::endl;
|
||||
|
||||
a.pop_back();
|
||||
a.pop_front();
|
||||
assert (a.size() == 0);
|
||||
assert (a.begin() == a.end());
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
82
labs/list_implementation/checkpoint2.cpp
Normal file
82
labs/list_implementation/checkpoint2.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/* This program attempts to create two linked list, one linked list consists of nodes of class NodeA type,
|
||||
* the other linked list consists of nodes of class NodeB type, but we would like to use the same
|
||||
* PushBack() function to append nodes to the end of these two linked lists, and use the same PrintList() function
|
||||
* to print the elements in both lists, and thus we need to make the PushBack() and the PrintList() templated functions.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
class NodeA {
|
||||
public:
|
||||
int data;
|
||||
NodeA* next;
|
||||
NodeA* prev;
|
||||
};
|
||||
|
||||
class NodeB{
|
||||
public:
|
||||
double data;
|
||||
NodeB* next;
|
||||
NodeB* prev;
|
||||
};
|
||||
|
||||
int main() {
|
||||
// Part 1: test NodeA class.
|
||||
// Initialize an empty linked list, consisting of NodeA nodes.
|
||||
NodeA* headA = nullptr;
|
||||
|
||||
// Create nodes and add them to the end of the list using PushBack
|
||||
for (int i = 1; i <= 5; ++i) {
|
||||
NodeA* newNode = new NodeA;
|
||||
// data of NodeA is an int type.
|
||||
newNode->data = i;
|
||||
newNode->next = nullptr;
|
||||
newNode->prev = nullptr;
|
||||
|
||||
// Add the node to the end of the list
|
||||
PushBack(headA, newNode);
|
||||
}
|
||||
|
||||
// Print the linked list to verify the nodes
|
||||
std::cout << "Linked List of NodeA nodes: ";
|
||||
PrintList(headA);
|
||||
|
||||
// Clean up memory (free nodes)
|
||||
NodeA* currentA = headA;
|
||||
while (currentA != nullptr) {
|
||||
NodeA* next = currentA->next;
|
||||
delete currentA;
|
||||
currentA = next;
|
||||
}
|
||||
|
||||
// Part 2: test NodeB class.
|
||||
// Initialize an empty linked list, consisting of NodeB nodes.
|
||||
NodeB* headB = nullptr;
|
||||
|
||||
// Create nodes and add them to the end of the list using PushBack
|
||||
for (int i = 1; i <= 5; ++i) {
|
||||
NodeB* newNode = new NodeB;
|
||||
// data of NodeA is a double type.
|
||||
newNode->data = (double)sqrt(i);
|
||||
newNode->next = nullptr;
|
||||
newNode->prev = nullptr;
|
||||
|
||||
// Add the node to the end of the list
|
||||
PushBack(headB, newNode);
|
||||
}
|
||||
|
||||
// Print the linked list to verify the nodes
|
||||
std::cout << "Linked List of NodeB nodes: ";
|
||||
PrintList(headB);
|
||||
|
||||
// Clean up memory (free nodes)
|
||||
NodeB* currentB = headB;
|
||||
while (currentB != nullptr) {
|
||||
NodeB* next = currentB->next;
|
||||
delete currentB;
|
||||
currentB = next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
94
labs/list_implementation/checkpoint3.cpp
Normal file
94
labs/list_implementation/checkpoint3.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include <iostream>
|
||||
|
||||
template <class T>
|
||||
class Node {
|
||||
public:
|
||||
T value;
|
||||
Node<T>* next;
|
||||
Node<T>* prev;
|
||||
|
||||
// constructor
|
||||
Node(T val) : value(val), next(nullptr), prev(nullptr) {}
|
||||
};
|
||||
|
||||
// function to merge two sorted doubly linked lists
|
||||
// this function returns a pointer pointing to the head node of the merged list.
|
||||
template <class T>
|
||||
Node<T>* mergeLists(Node<T>* head_A, Node<T>* head_B) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
// create 5 nodes and link them to form a linked list, this is linked list A.
|
||||
Node<int>* head_A = new Node<int>(1);
|
||||
Node<int>* second_A = new Node<int>(3);
|
||||
Node<int>* third_A = new Node<int>(5);
|
||||
Node<int>* fourth_A = new Node<int>(7);
|
||||
Node<int>* fifth_A = new Node<int>(9);
|
||||
|
||||
// link the nodes
|
||||
head_A->next = second_A;
|
||||
second_A->prev = head_A;
|
||||
second_A->next = third_A;
|
||||
third_A->prev = second_A;
|
||||
third_A->next = fourth_A;
|
||||
fourth_A->prev = third_A;
|
||||
fourth_A->next = fifth_A;
|
||||
fifth_A->prev = fourth_A;
|
||||
|
||||
// traverse linked list A and print the values
|
||||
Node<int>* current = head_A;
|
||||
while (current != nullptr) {
|
||||
std::cout << current->value << " ";
|
||||
current = current->next;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
// create 5 nodes and link them to form a linked list, this is linked list B.
|
||||
Node<int>* head_B = new Node<int>(2);
|
||||
Node<int>* second_B = new Node<int>(4);
|
||||
Node<int>* third_B = new Node<int>(6);
|
||||
Node<int>* fourth_B = new Node<int>(8);
|
||||
Node<int>* fifth_B = new Node<int>(10);
|
||||
|
||||
// link the nodes
|
||||
head_B->next = second_B;
|
||||
second_B->prev = head_B;
|
||||
second_B->next = third_B;
|
||||
third_B->prev = second_B;
|
||||
third_B->next = fourth_B;
|
||||
fourth_B->prev = third_B;
|
||||
fourth_B->next = fifth_B;
|
||||
fifth_B->prev = fourth_B;
|
||||
|
||||
// traverse linked list B and print the values
|
||||
current = head_B;
|
||||
while (current != nullptr) {
|
||||
std::cout << current->value << " ";
|
||||
current = current->next;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
Node<int>* head_C;
|
||||
Node<int>* tail_C;
|
||||
head_C = mergeLists(head_A, head_B);
|
||||
|
||||
// traverse linked list C and print the values
|
||||
current = head_C;
|
||||
while (current != nullptr) {
|
||||
std::cout << current->value << " ";
|
||||
// keep tracking current and when current reaches nullptr, tail_C will be the tail node.
|
||||
tail_C = current;
|
||||
current = current->next;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
// traverse linked list C backwards and print the values
|
||||
current = tail_C;
|
||||
while (current != nullptr) {
|
||||
std::cout << current->value << " ";
|
||||
current = current->prev;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
268
labs/list_implementation/dslist.h
Normal file
268
labs/list_implementation/dslist.h
Normal file
@@ -0,0 +1,268 @@
|
||||
#ifndef dslist_h_
|
||||
#define dslist_h_
|
||||
// A simplified implementation of the STL list container class,
|
||||
// including the iterator, but not the const_iterators. Three
|
||||
// separate classes are defined: a Node class, an iterator class, and
|
||||
// the actual list class. The underlying list is doubly-linked, but
|
||||
// there is no dummy head node and the list is not circular.
|
||||
#include <cassert>
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// NODE CLASS
|
||||
template <class T>
|
||||
class Node {
|
||||
public:
|
||||
Node() : next_(NULL), prev_(NULL) {}
|
||||
Node(const T& v) : value_(v), next_(NULL), prev_(NULL) {}
|
||||
|
||||
// REPRESENTATION
|
||||
T value_;
|
||||
Node<T>* next_;
|
||||
Node<T>* prev_;
|
||||
};
|
||||
|
||||
// A "forward declaration" of this class is needed
|
||||
template <class T> class dslist;
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// LIST ITERATOR
|
||||
template <class T>
|
||||
class list_iterator {
|
||||
public:
|
||||
// default constructor, copy constructor, assignment operator, & destructor
|
||||
list_iterator(Node<T>* p=NULL) : ptr_(p) {}
|
||||
// NOTE: the implicit compiler definitions of the copy constructor,
|
||||
// assignment operator, and destructor are correct for this class
|
||||
|
||||
// dereferencing operator gives access to the value at the pointer
|
||||
T& operator*() { return ptr_->value_; }
|
||||
|
||||
// increment & decrement operators
|
||||
list_iterator<T>& operator++() { // pre-increment, e.g., ++iter
|
||||
ptr_ = ptr_->next_;
|
||||
return *this;
|
||||
}
|
||||
list_iterator<T> operator++(int) { // post-increment, e.g., iter++
|
||||
list_iterator<T> temp(*this);
|
||||
ptr_ = ptr_->next_;
|
||||
return temp;
|
||||
}
|
||||
list_iterator<T>& operator--() { // pre-decrement, e.g., --iter
|
||||
ptr_ = ptr_->prev_;
|
||||
return *this;
|
||||
}
|
||||
list_iterator<T> operator--(int) { // post-decrement, e.g., iter--
|
||||
list_iterator<T> temp(*this);
|
||||
ptr_ = ptr_->prev_;
|
||||
return temp;
|
||||
}
|
||||
// the dslist class needs access to the private ptr_ member variable
|
||||
friend class dslist<T>;
|
||||
|
||||
// Comparions operators are straightforward
|
||||
bool operator==(const list_iterator<T>& r) const {
|
||||
return ptr_ == r.ptr_; }
|
||||
bool operator!=(const list_iterator<T>& r) const {
|
||||
return ptr_ != r.ptr_; }
|
||||
|
||||
private:
|
||||
// REPRESENTATION
|
||||
Node<T>* ptr_; // ptr to node in the list
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// LIST CLASS DECLARATION
|
||||
// Note that it explicitly maintains the size of the list.
|
||||
template <class T>
|
||||
class dslist {
|
||||
public:
|
||||
// default constructor, copy constructor, assignment operator, & destructor
|
||||
dslist() : head_(NULL), tail_(NULL), size_(0) {}
|
||||
dslist(const dslist<T>& old) { copy_list(old); }
|
||||
dslist& operator= (const dslist<T>& old);
|
||||
~dslist() { destroy_list(); }
|
||||
|
||||
typedef list_iterator<T> iterator;
|
||||
|
||||
// simple accessors & modifiers
|
||||
unsigned int size() const { return size_; }
|
||||
bool empty() const { return head_ == NULL; }
|
||||
void clear() { destroy_list(); }
|
||||
|
||||
// read/write access to contents
|
||||
const T& front() const { return head_->value_; }
|
||||
T& front() { return head_->value_; }
|
||||
const T& back() const { return tail_->value_; }
|
||||
T& back() { return tail_->value_; }
|
||||
|
||||
// modify the linked list structure
|
||||
void push_front(const T& v);
|
||||
void pop_front();
|
||||
void push_back(const T& v);
|
||||
void pop_back();
|
||||
|
||||
iterator erase(iterator itr);
|
||||
iterator insert(iterator itr, const T& v);
|
||||
iterator begin() { return iterator(head_); }
|
||||
iterator end() { return iterator(NULL); }
|
||||
|
||||
private:
|
||||
// private helper functions
|
||||
void copy_list(const dslist<T>& old);
|
||||
void destroy_list();
|
||||
|
||||
//REPRESENTATION
|
||||
Node<T>* head_;
|
||||
Node<T>* tail_;
|
||||
unsigned int size_;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// LIST CLASS IMPLEMENTATION
|
||||
template <class T>
|
||||
dslist<T>& dslist<T>::operator= (const dslist<T>& old) {
|
||||
// check for self-assignment
|
||||
if (&old != this) {
|
||||
destroy_list();
|
||||
copy_list(old);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void dslist<T>::push_front(const T& v) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void dslist<T>::pop_front() {
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void dslist<T>::push_back(const T& v) {
|
||||
Node<T>* newp = new Node<T>(v);
|
||||
// special case: initially empty list
|
||||
if (!tail_) {
|
||||
head_ = tail_ = newp;
|
||||
} else {
|
||||
// normal case: at least one node already
|
||||
newp->prev_ = tail_;
|
||||
tail_->next_ = newp;
|
||||
tail_ = newp;
|
||||
}
|
||||
++size_;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void dslist<T>::pop_back() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// do these lists look the same (length & contents)?
|
||||
template <class T>
|
||||
bool operator== (dslist<T>& left, dslist<T>& right) {
|
||||
if (left.size() != right.size()) return false;
|
||||
typename dslist<T>::iterator left_itr = left.begin();
|
||||
typename dslist<T>::iterator right_itr = right.begin();
|
||||
// walk over both lists, looking for a mismatched value
|
||||
while (left_itr != left.end()) {
|
||||
if (*left_itr != *right_itr) return false;
|
||||
left_itr++; right_itr++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool operator!= (dslist<T>& left, dslist<T>& right){ return !(left==right); }
|
||||
template <class T>
|
||||
typename dslist<T>::iterator dslist<T>::erase(iterator itr) {
|
||||
assert (size_ > 0);
|
||||
--size_;
|
||||
iterator result(itr.ptr_->next_);
|
||||
// One node left in the list.
|
||||
if (itr.ptr_ == head_ && head_ == tail_) {
|
||||
head_ = tail_ = 0;
|
||||
}
|
||||
// Removing the head in a list with at least two nodes
|
||||
else if (itr.ptr_ == head_) {
|
||||
head_ = head_->next_;
|
||||
head_->prev_ = 0;
|
||||
}
|
||||
// Removing the tail in a list with at least two nodes
|
||||
else if (itr.ptr_ == tail_) {
|
||||
tail_ = tail_->prev_;
|
||||
tail_->next_ = 0;
|
||||
}
|
||||
// Normal remove
|
||||
else {
|
||||
itr.ptr_->prev_->next_ = itr.ptr_->next_;
|
||||
itr.ptr_->next_->prev_ = itr.ptr_->prev_;
|
||||
}
|
||||
delete itr.ptr_;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
typename dslist<T>::iterator dslist<T>::insert(iterator itr, const T& v) {
|
||||
++size_ ;
|
||||
Node<T>* p = new Node<T>(v);
|
||||
p->prev_ = itr.ptr_->prev_;
|
||||
p->next_ = itr.ptr_;
|
||||
itr.ptr_->prev_ = p;
|
||||
if (itr.ptr_ == head_)
|
||||
head_ = p;
|
||||
else
|
||||
p->prev_->next_ = p;
|
||||
return iterator(p);
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
void dslist<T>::copy_list(const dslist<T>& old) {
|
||||
size_ = old.size_;
|
||||
// Handle the special case of an empty list.
|
||||
if (size_ == 0) {
|
||||
head_ = tail_ = 0;
|
||||
return;
|
||||
}
|
||||
// Create a new head node.
|
||||
head_ = new Node<T>(old.head_->value_);
|
||||
// tail_ will point to the last node created and therefore will move
|
||||
// down the new list as it is built
|
||||
tail_ = head_;
|
||||
// old_p will point to the next node to be copied in the old list
|
||||
Node<T>* old_p = old.head_->next_;
|
||||
// copy the remainder of the old list, one node at a time
|
||||
while (old_p) {
|
||||
tail_->next_ = new Node<T>(old_p->value_);
|
||||
tail_->next_->prev_ = tail_;
|
||||
tail_ = tail_->next_;
|
||||
old_p = old_p->next_;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void dslist<T>::destroy_list() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user