renaming labs
This commit is contained in:
@@ -1,77 +0,0 @@
|
|||||||
# 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.-->
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
/* 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;
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
@@ -1,268 +0,0 @@
|
|||||||
#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
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
# Lab 8 — Recursion
|
|
||||||
|
|
||||||
This lab gives you practice in the use of recursion to solve problems. All three checkpoints addressed in
|
|
||||||
this lab deal with finding and counting the number of paths between points on a rectilinear grid. A starting
|
|
||||||
point (x, y) with non-negative integer coordinates is provided. You are only allowed to move horizontally
|
|
||||||
and vertically along the grid. Hence, from (x, y) you may move to (x + 1, y), (x − 1, y), (x, y − 1), (x, y + 1).
|
|
||||||
However, **keep in mind that your goal is to return to the origin (0, 0) in such a way that you never increase the distance to the origin.** The distance is counted as the minimum number of total vertical and horizontal steps to reach the origin. In
|
|
||||||
the first checkpoint the grid will be “free”. In the second and third checkpoints, some of the grid locations
|
|
||||||
will be “blocked” in the sense that you can not move to that point.
|
|
||||||
|
|
||||||
Stop now and play with small examples. Draw pictures of a grid. Think about the implications of the rules
|
|
||||||
before you proceed with the checkpoints.
|
|
||||||
|
|
||||||
## Checkpoint 1
|
|
||||||
*estimate: 10-30 minutes*
|
|
||||||
|
|
||||||
Did you notice that the rules prevent certain moves from occurring? What are they? If you don’t get them
|
|
||||||
right you will not be able to do the lab correctly. Confirm your understanding with one of the lab TAs.
|
|
||||||
|
|
||||||
Now, write a simple recursive program (from scratch) that reads a starting location, (x, y) and returns the
|
|
||||||
total number of different paths back to the origin when the entire grid is “free”. Two paths are different if
|
|
||||||
there is at least one step on the path that is different even if most of the steps are the same. As a hint, when
|
|
||||||
x == 0 and y == 0 there is 1 path, when x == 2 and y == 1 there are 3 paths, and when x == 2 and
|
|
||||||
y == 2 there are 6 paths. When you’re confident your program is debugged try even bigger values. For what
|
|
||||||
values does the program take a few seconds to complete on your computer? If you increase these values by
|
|
||||||
1, how does it impact the running time? Is this what you expected from Big O Notation?
|
|
||||||
|
|
||||||
**To complete this checkpoint** show a TA your program to count all paths back to the origin. Be prepared
|
|
||||||
to discuss the running time of your program for different input values.
|
|
||||||
|
|
||||||
## Checkpoint 2
|
|
||||||
*estimate: 20-40 minutes*
|
|
||||||
|
|
||||||
Please download the files:
|
|
||||||
[start.cpp](start.cpp)
|
|
||||||
[grid1.txt](grid1.txt)
|
|
||||||
[grid2.txt](grid2.txt)
|
|
||||||
[grid3.txt](grid3.txt)
|
|
||||||
[grid4.txt](grid4.txt)
|
|
||||||
|
|
||||||
Starting from the supplied program, start.cpp, write a program to count the number of paths when some
|
|
||||||
of the grid locations are blocked. When a location is blocked, no path can go through it. Before writing
|
|
||||||
your own code, compile and run start.cpp. Use the files grid1.txt, etc. as input. These have a sequence
|
|
||||||
of blocked locations, ending with the point location (0, 0) (which isn’t blocked, but signals the end of the
|
|
||||||
input). The next location is the location for the initial (x, y). By changing this location you will be able to
|
|
||||||
test your program in different ways.
|
|
||||||
|
|
||||||
**To complete this checkpoint** show a TA your code to find and count legal (unblocked) paths.
|
|
||||||
|
|
||||||
## Checkpoint 3
|
|
||||||
*estimate: 30-40 minutes*
|
|
||||||
|
|
||||||
Modify your program from Checkpoint 2 so that it finds and outputs a legal path from the given (x, y)
|
|
||||||
location to the origin. You should both print the sequence of coordinates of a solution path AND modify the
|
|
||||||
print_grid function to draw the grid a second time with the path marked on the grid with ’$’ symbols. If
|
|
||||||
there is more than one path, it does not matter which it outputs. Did you notice that all legal paths to the
|
|
||||||
origin are the same length under the rules provided?
|
|
||||||
Now let’s explore recursion with gdb/lldb. <!--Review the handout from Lab 3, Checkpoint 3:
|
|
||||||
http://www.cs.rpi.edu/academics/courses/spring23/csci1200/labs/03_debugging/lab_post.pdf-->
|
|
||||||
Here’s a table of equivalent commands in gdb (Linux, WSL) vs. lldb (Mac, Linux, WSL):
|
|
||||||
https://lldb.llvm.org/lldb-gdb.html
|
|
||||||
1. Compile your program with the -g flag, so it includes debug info, including line numbers:
|
|
||||||
|
|
||||||
```console
|
|
||||||
clang++ -Wall -g start.cpp -o start.out
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Start gdb (or lldb):
|
|
||||||
|
|
||||||
```console
|
|
||||||
gdb ./start.out
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Set a breakpoint at the first line of the main function:
|
|
||||||
|
|
||||||
```console
|
|
||||||
b main
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Now run/launch the program. Note: Here’s where we specify the necessary command line arguments.
|
|
||||||
|
|
||||||
```console
|
|
||||||
r grid2.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Place a breakpoint on the line in main where you first call your recursive function. Replace <NUM> with
|
|
||||||
the line number in your source code.
|
|
||||||
|
|
||||||
```console
|
|
||||||
b <NUM>
|
|
||||||
```
|
|
||||||
|
|
||||||
gdb will confirm with a message like this:
|
|
||||||
Breakpoint 2 at 0xwhatever: file start.cpp, line <NUM>
|
|
||||||
You can review your currently set breakpoints:
|
|
||||||
|
|
||||||
```console
|
|
||||||
info b
|
|
||||||
```
|
|
||||||
|
|
||||||
If you happen to mistype the line number, you can delete the breakpoint and try again. <BNUM> is the
|
|
||||||
breakpoint number with the incorrect line number.
|
|
||||||
|
|
||||||
```console
|
|
||||||
delete <BNUM>
|
|
||||||
```
|
|
||||||
|
|
||||||
6. Place another breakpoint at the first line of your recursive function. And then let the program run
|
|
||||||
until it reaches the next breakpoint by typing:
|
|
||||||
continue
|
|
||||||
When gdb pauses, it should inform you of the current program line number.
|
|
||||||
7. Now let’s step into your recursive function to get a closer look at recursion. Let the program run until
|
|
||||||
it enters the first recursive function call:
|
|
||||||
|
|
||||||
```console
|
|
||||||
continue
|
|
||||||
```
|
|
||||||
|
|
||||||
Inspect the data in the function arguments and the grid of booleans. Print the coordinates of the
|
|
||||||
current location, which are passed in as function arguments named x and y (for example).
|
|
||||||
|
|
||||||
```console
|
|
||||||
print x
|
|
||||||
print y
|
|
||||||
```
|
|
||||||
|
|
||||||
If you are using grid2.txt as your grid, these values should read x = 9 and y = 7.
|
|
||||||
8. Let’s navigate within our recursive algorithm using step and next. NOTE: Though similar sounding,
|
|
||||||
these are two different commands in gdb/lldb. step will enter into the details of a called function and
|
|
||||||
allow you to walk through its code, while next will fully execute one line from the current function
|
|
||||||
(skipping over all of the details of the function as it’s executed).
|
|
||||||
Use the next command to move down until we hit our next recursive call. Once on this line, let’s step
|
|
||||||
into the function call. After you step into the function, you may want to type continue so we can skip
|
|
||||||
to the next breakpoint. Print out the coordinates of the current location, which should be different.
|
|
||||||
Also, let’s print the boolean values from locations in the grid adjacent to the current position. For
|
|
||||||
example:
|
|
||||||
|
|
||||||
```console
|
|
||||||
print blocked_grid
|
|
||||||
print blocked_grid[x][y]
|
|
||||||
print blocked_grid[x-1][y+1]
|
|
||||||
```
|
|
||||||
Because gdb supports using basic math on these variables, we can also print the grid values above,
|
|
||||||
below, to the left, and to the right of our current position.
|
|
||||||
9. Use step and continue to go further and further into the recursion. Use backtrace to show the
|
|
||||||
function calls currently on the call stack, including the specific line numbers. As you walk step by step
|
|
||||||
through your algorithm and print data, do the variable values and sequence of locations and function
|
|
||||||
calls match your expectations? Ask a TA or mentor if you have questions about this information.
|
|
||||||
10. Finally, delete all of the breakpoints and then let the program finish without interruption.
|
|
||||||
|
|
||||||
```console
|
|
||||||
continue
|
|
||||||
```
|
|
||||||
|
|
||||||
**To complete this checkpoint and the entire lab**, present your modified program to a TA or mentor.
|
|
||||||
Demonstrate your skills with gdb/lldb to print out values of adjacent positions in the blocked grids vector,
|
|
||||||
to use backtrace, and navigate within recursive function calls using next, step, continue. Be prepared to
|
|
||||||
discuss how you will use gdb/lldb to help debug your future Data Structures homeworks.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
1 0
|
|
||||||
2 1 2 3
|
|
||||||
3 3 3 4
|
|
||||||
5 5
|
|
||||||
|
|
||||||
0 0
|
|
||||||
4 4
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
2 0 3 1
|
|
||||||
0 1 3 3
|
|
||||||
1 2 4 1 5 2
|
|
||||||
1 3 5 3
|
|
||||||
0 5 2 5
|
|
||||||
5 4 6 4 7 4
|
|
||||||
3 5 4 6 5 6 7 5
|
|
||||||
7 7 8 8
|
|
||||||
|
|
||||||
0 0
|
|
||||||
9 7
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
0 1
|
|
||||||
2 2
|
|
||||||
2 3
|
|
||||||
3 3
|
|
||||||
4 3
|
|
||||||
3 1
|
|
||||||
1 4
|
|
||||||
5 5
|
|
||||||
6 6
|
|
||||||
1 9
|
|
||||||
1 8
|
|
||||||
1 7
|
|
||||||
3 7
|
|
||||||
2 7
|
|
||||||
3 10
|
|
||||||
7 7
|
|
||||||
7 8
|
|
||||||
7 6
|
|
||||||
7 5
|
|
||||||
7 4
|
|
||||||
7 3
|
|
||||||
8 3
|
|
||||||
9 3
|
|
||||||
10 3
|
|
||||||
|
|
||||||
0 0
|
|
||||||
10 10
|
|
||||||
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
0 1
|
|
||||||
2 2
|
|
||||||
2 3
|
|
||||||
3 3
|
|
||||||
4 3
|
|
||||||
3 1
|
|
||||||
1 4
|
|
||||||
5 5
|
|
||||||
6 6
|
|
||||||
1 9
|
|
||||||
1 8
|
|
||||||
1 7
|
|
||||||
3 7
|
|
||||||
2 7
|
|
||||||
3 10
|
|
||||||
7 7
|
|
||||||
7 8
|
|
||||||
7 6
|
|
||||||
7 5
|
|
||||||
7 4
|
|
||||||
7 3
|
|
||||||
8 3
|
|
||||||
9 3
|
|
||||||
10 3
|
|
||||||
15 15
|
|
||||||
17 15
|
|
||||||
16 15
|
|
||||||
18 15
|
|
||||||
14 15
|
|
||||||
13 15
|
|
||||||
12 14
|
|
||||||
17 18
|
|
||||||
7 18
|
|
||||||
7 17
|
|
||||||
7 16
|
|
||||||
7 15
|
|
||||||
7 14
|
|
||||||
|
|
||||||
0 0
|
|
||||||
18 18
|
|
||||||
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
// Starting code for Checkpoints 2 and 3. This includes
|
|
||||||
// functions to read the grid and to output it.
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <list>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
|
|
||||||
// A simple class to represent a point location. It only has a
|
|
||||||
// constructor and a two public member variables. This is one of the
|
|
||||||
// few times that you are allowed to use non-private member variables.
|
|
||||||
|
|
||||||
class Point {
|
|
||||||
public:
|
|
||||||
Point(int x0, int y0) : x(x0), y(y0) {}
|
|
||||||
int x,y;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// NOTE: We could use a boolean (true/false) to represent whether each
|
|
||||||
// cell of the grid was blocked or open. This would be the minimal
|
|
||||||
// representation for memory.
|
|
||||||
|
|
||||||
// However, debuggers (both traditional and memory debuggers) might
|
|
||||||
// not be able to help debug errors with vectors of booleans, if they
|
|
||||||
// are efficiently packed by a clever STL implementation. So instead
|
|
||||||
// we use an enum, to improve readability, and to ensure that the
|
|
||||||
// status of each grid cell is stored as an integer avoiding debugger
|
|
||||||
// confusion during development.
|
|
||||||
|
|
||||||
|
|
||||||
enum GRID_STATUS { GRID_CLEAR, GRID_BLOCKED };
|
|
||||||
|
|
||||||
|
|
||||||
// Input the grid and the start location. The input is a sequence of
|
|
||||||
// x y locations, terminated by x==0 and y==0. The last input, which
|
|
||||||
// follows 0 0 input, is the start location.
|
|
||||||
//
|
|
||||||
// The grid is represented as a 2d vector of GRID_STATUS, with each location
|
|
||||||
// that is blocked --- meaning that no path can go through --- being
|
|
||||||
// represented by the value GRID_BLOCKED. The grid is large enough to
|
|
||||||
// include all blocked points and include the starting location. The
|
|
||||||
// first coordinate of the vector of vectors is the x coordinate, and
|
|
||||||
// the second is the y coordinate. The format of the input is
|
|
||||||
// specified in the lab handout.
|
|
||||||
|
|
||||||
void read_grid(std::istream& istr,
|
|
||||||
std::vector<std::vector<GRID_STATUS> >& blocked_grid,
|
|
||||||
int& start_x, int& start_y) {
|
|
||||||
|
|
||||||
// Read the x y locations into a list of Points. Keep track of the
|
|
||||||
// max x and max y values so that the size of the grid can be
|
|
||||||
// determined.
|
|
||||||
int x, y;
|
|
||||||
int max_x = 0, max_y = 0; // keep track of the max coordinate values
|
|
||||||
std::list<Point> blocked_points;
|
|
||||||
while ((istr >> x >> y) && ! (x==0 && y==0)) {
|
|
||||||
blocked_points.push_back(Point(x,y));
|
|
||||||
if (x > max_x) max_x = x;
|
|
||||||
if (y > max_y) max_y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that a 0 0 location has been read, read the start location.
|
|
||||||
// If this is beyond the max x or y value then update these values.
|
|
||||||
istr >> start_x >> start_y;
|
|
||||||
if (start_x > max_x) max_x = start_x;
|
|
||||||
if (start_y > max_y) max_y = start_y;
|
|
||||||
|
|
||||||
// Make a vector of vectors with all entries marked clear.
|
|
||||||
std::vector<GRID_STATUS> one_row_of_ys(max_y+1, GRID_CLEAR);
|
|
||||||
std::vector<std::vector<GRID_STATUS> > empty_grid(max_x+1, one_row_of_ys);
|
|
||||||
blocked_grid = empty_grid;
|
|
||||||
|
|
||||||
// For Point in the list, mark the location in the list as blocked.
|
|
||||||
std::list<Point>::iterator p;
|
|
||||||
for (p = blocked_points.begin(); p != blocked_points.end(); ++p) {
|
|
||||||
blocked_grid[p->x][p->y] = GRID_BLOCKED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Output the grid to cout. The form of the output is explained in
|
|
||||||
// the cout statement below.
|
|
||||||
|
|
||||||
void print_grid(const std::vector<std::vector<GRID_STATUS> > & blocked_grid,
|
|
||||||
unsigned int start_x, unsigned int start_y) {
|
|
||||||
|
|
||||||
std::cout << "Here is the grid with the origin in the upper left corner, x increasing \n"
|
|
||||||
<< "horizontally and y increasing down the screen. An 'X' represents a blocked\n"
|
|
||||||
<< "location and the 'S' represents the starting location.\n\n";
|
|
||||||
|
|
||||||
for (unsigned int y=0; y<blocked_grid[0].size(); ++y) {
|
|
||||||
for (unsigned int x=0; x<blocked_grid.size(); ++x) {
|
|
||||||
if (x == start_x && y == start_y)
|
|
||||||
std::cout << " S";
|
|
||||||
else if (blocked_grid[x][y] == GRID_BLOCKED)
|
|
||||||
std::cout << " X";
|
|
||||||
else
|
|
||||||
std::cout << " .";
|
|
||||||
}
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
if (argc != 2) {
|
|
||||||
std::cerr << "Usage: " << argv[0] << " grid-file" << std::endl;;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
std::ifstream istr(argv[1]);
|
|
||||||
if (!istr) {
|
|
||||||
std::cerr << "Could not open " << argv[1] << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::vector<GRID_STATUS> > blocked_grid;
|
|
||||||
int start_x, start_y;
|
|
||||||
read_grid(istr, blocked_grid, start_x, start_y);
|
|
||||||
print_grid(blocked_grid, start_x, start_y);
|
|
||||||
|
|
||||||
// Start here with your code...
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
# Lab 9 — Maps
|
|
||||||
|
|
||||||
This lab gives you practice initial practice in working with the STL associative container, maps. No downloads
|
|
||||||
are needed until Checkpoint 3.
|
|
||||||
|
|
||||||
## Checkpoint 1
|
|
||||||
*estimate: 10-20 minutes*
|
|
||||||
|
|
||||||
Write a program from scratch that uses a map to find all the modes in an input sequence of integers.
|
|
||||||
Remember, a mode is an integer that occurs at least as many times in the sequence as any other integer.
|
|
||||||
|
|
||||||
Thus, in the sequence
|
|
||||||
|
|
||||||
```console
|
|
||||||
19 83 -12 83 65 19 45 -12 45 19 45
|
|
||||||
```
|
|
||||||
|
|
||||||
the two modes are 19 and 45. Include one command-line argument to provide an input file. Use operator[] for maps when inserting values.
|
|
||||||
|
|
||||||
**To complete this checkpoint**: show a TA your debugged implementation and how it runs correctly on several interesting test cases.
|
|
||||||
|
|
||||||
## Checkpoint 2
|
|
||||||
*estimate: 10-20 minutes*
|
|
||||||
|
|
||||||
Rewrite your program from checkpoint 1 to use find or insert or both instead of operator[].
|
|
||||||
|
|
||||||
**To complete this checkpoint**: show a TA your revised and tested program.
|
|
||||||
|
|
||||||
## Checkpoint 3
|
|
||||||
*estimate: 20-40 minutes*
|
|
||||||
|
|
||||||
Please download the [phonebook.cpp](phonebook.cpp) needed for the final checkpoint:
|
|
||||||
|
|
||||||
This code implements a simple caller ID program. This program could be used by a university or company
|
|
||||||
to perform a “reverse lookup” — given the 4 digit phone number extension, return the name of the caller. A
|
|
||||||
vector of strings stores the complete database of names assigned to phone number extensions. Compile and
|
|
||||||
run this program. Add your own test cases to the main function.
|
|
||||||
|
|
||||||
Part 1: Analyze the computational cost of this program using the big ‘O’ notation in terms of n the number
|
|
||||||
of assigned phone numbers in the phonebook, and N the largest possible phone number (number of different
|
|
||||||
possible phone numbers). What is the running time of constructing the phonebook and of the add and
|
|
||||||
identify functions? How much memory is used by this program? Express this using order notation. What
|
|
||||||
would happen if you extended this to a 7- or a 10-digit number? Would it work on a cell phone?
|
|
||||||
|
|
||||||
Part 2: Rewrite this program to use maps, storing only the numbers that are assigned. Analyze the cost of
|
|
||||||
creating the map, and of the add and the identify functions. Test it on 7 digit numbers. In what ways is the
|
|
||||||
vector version better and in what ways is the map version better?
|
|
||||||
|
|
||||||
**To complete this checkpoint**: Present your analysis from Part 1 to a TA, demonstrate the new version
|
|
||||||
of your program, and be prepared to discuss your analysis of Part 2.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
// A simple "caller ID" program
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// add a number, name pair to the phonebook
|
|
||||||
void add(vector<string> &phonebook, int number, string const& name) {
|
|
||||||
phonebook[number] = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// given a phone number, determine who is calling
|
|
||||||
void identify(const vector<string> & phonebook, int number) {
|
|
||||||
if (phonebook[number] == "UNASSIGNED")
|
|
||||||
cout << "unknown caller!" << endl;
|
|
||||||
else
|
|
||||||
cout << phonebook[number] << " is calling!" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
// create the phonebook; initially all numbers are unassigned
|
|
||||||
vector<string> phonebook(10000, "UNASSIGNED");
|
|
||||||
|
|
||||||
// add several names to the phonebook
|
|
||||||
add(phonebook, 1111, "fred");
|
|
||||||
add(phonebook, 2222, "sally");
|
|
||||||
add(phonebook, 3333, "george");
|
|
||||||
|
|
||||||
// test the phonebook
|
|
||||||
identify(phonebook, 2222);
|
|
||||||
identify(phonebook, 4444);
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
# Lab 10 — Trees, Binary Trees, & Binary Search Trees
|
|
||||||
|
|
||||||
## Checkpoint 1
|
|
||||||
|
|
||||||
*estimate: 20-30 minutes*
|
|
||||||
|
|
||||||
problem 1: Draw a binary tree with 4 levels with the integers 1-7 such that the sum of elements on every level of the tree is the same.
|
|
||||||
|
|
||||||
problem 2: Create a exactly balanced binary search tree with 7 color words (order the colors alphabetically).
|
|
||||||
|
|
||||||
problem 3: Draw a exactly-balanced binary search tree containing the letters of the word: uncopyrightable
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
What is the pre-order traversal of the tree above?
|
|
||||||
|
|
||||||
problem 4: Now draw a exactly-balanced binary tree of characters such that a post-order traversal spells the word: uncopyrightable
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
What is the breadth-first traversal of the tree above?
|
|
||||||
|
|
||||||
**To complete this checkpoint**: When you have finished all of the problems, discuss your answers with your lab TA or mentor.
|
|
||||||
|
|
||||||
## Checkpoint 2
|
|
||||||
|
|
||||||
*estimate: 20-35 minutes*
|
|
||||||
|
|
||||||
Now let’s explore the implementation of the ds_set class, along with the use of recursive functions to manipulate binary search trees. Download and examine the files: [ds_set.h](ds_set.h) and [test_ds_set.cpp](test_ds_set.cpp).
|
|
||||||
|
|
||||||
The implementation of *find* provided in ds_set.h is recursive. Re-implement and test a non-recursive replacement for this function.
|
|
||||||
|
|
||||||
**To complete this checkpoint**: Show one of the TAs your new code. Be prepared to discuss the running time for the two different versions of *find* for various inputs.
|
|
||||||
|
|
||||||
## Checkpoint 3
|
|
||||||
|
|
||||||
*estimate: 20-35 minutes*
|
|
||||||
|
|
||||||
The implementation of the copy constructor and the assignment operator is not yet complete
|
|
||||||
because each depends on a private member function called *copy_tree*, the body of which has not yet been
|
|
||||||
written. Write *copy_tree* and then test to see if it works by “uncommenting” the appropriate code from the
|
|
||||||
main function.
|
|
||||||
|
|
||||||
**To complete this checkpoint**: Test your code and show one of the TAs your new code.
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
// Partial implementation of binary-tree based set class similar to std::set.
|
|
||||||
// The iterator increment & decrement operations have been omitted.
|
|
||||||
#ifndef ds_set_h_
|
|
||||||
#define ds_set_h_
|
|
||||||
#include <iostream>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// TREE NODE CLASS
|
|
||||||
template <class T>
|
|
||||||
class TreeNode {
|
|
||||||
public:
|
|
||||||
TreeNode() : left(NULL), right(NULL) {}
|
|
||||||
TreeNode(const T& init) : value(init), left(NULL), right(NULL) {}
|
|
||||||
T value;
|
|
||||||
TreeNode* left;
|
|
||||||
TreeNode* right;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T> class ds_set;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// TREE NODE ITERATOR CLASS
|
|
||||||
template <class T>
|
|
||||||
class tree_iterator {
|
|
||||||
public:
|
|
||||||
tree_iterator() : ptr_(NULL) {}
|
|
||||||
tree_iterator(TreeNode<T>* p) : ptr_(p) {}
|
|
||||||
tree_iterator(const tree_iterator& old) : ptr_(old.ptr_) {}
|
|
||||||
~tree_iterator() {}
|
|
||||||
tree_iterator& operator=(const tree_iterator& old) { ptr_ = old.ptr_; return *this; }
|
|
||||||
// operator* gives constant access to the value at the pointer
|
|
||||||
const T& operator*() const { return ptr_->value; }
|
|
||||||
// comparions operators are straightforward
|
|
||||||
bool operator== (const tree_iterator& rgt) { return ptr_ == rgt.ptr_; }
|
|
||||||
bool operator!= (const tree_iterator& rgt) { return ptr_ != rgt.ptr_; }
|
|
||||||
// increment & decrement will be discussed in Lecture 19 and Lab 11
|
|
||||||
|
|
||||||
private:
|
|
||||||
// representation
|
|
||||||
TreeNode<T>* ptr_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// DS SET CLASS
|
|
||||||
template <class T>
|
|
||||||
class ds_set {
|
|
||||||
public:
|
|
||||||
ds_set() : root_(NULL), size_(0) {}
|
|
||||||
ds_set(const ds_set<T>& old) : size_(old.size_) {
|
|
||||||
root_ = this->copy_tree(old.root_); }
|
|
||||||
~ds_set() { this->destroy_tree(root_); root_ = NULL; }
|
|
||||||
ds_set& operator=(const ds_set<T>& old) {
|
|
||||||
if (&old != this) {
|
|
||||||
this->destroy_tree(root_);
|
|
||||||
root_ = this->copy_tree(old.root_);
|
|
||||||
size_ = old.size_;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef tree_iterator<T> iterator;
|
|
||||||
|
|
||||||
int size() const { return size_; }
|
|
||||||
bool operator==(const ds_set<T>& old) const { return (old.root_ == this->root_); }
|
|
||||||
|
|
||||||
// FIND, INSERT & ERASE
|
|
||||||
iterator find(const T& key_value) { return find(key_value, root_); }
|
|
||||||
std::pair< iterator, bool > insert(T const& key_value) { return insert(key_value, root_); }
|
|
||||||
int erase(T const& key_value) { return erase(key_value, root_); }
|
|
||||||
|
|
||||||
// OUTPUT & PRINTING
|
|
||||||
friend std::ostream& operator<< (std::ostream& ostr, const ds_set<T>& s) {
|
|
||||||
s.print_in_order(ostr, s.root_);
|
|
||||||
return ostr;
|
|
||||||
}
|
|
||||||
void print_as_sideways_tree(std::ostream& ostr) const {
|
|
||||||
print_as_sideways_tree(ostr, root_, 0); }
|
|
||||||
|
|
||||||
// ITERATORS
|
|
||||||
iterator begin() const {
|
|
||||||
if (!root_) return iterator(NULL);
|
|
||||||
TreeNode<T>* p = root_;
|
|
||||||
while (p->left) p = p->left;
|
|
||||||
return iterator(p);
|
|
||||||
}
|
|
||||||
iterator end() const { return iterator(NULL); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// REPRESENTATION
|
|
||||||
TreeNode<T>* root_;
|
|
||||||
int size_;
|
|
||||||
|
|
||||||
// PRIVATE HELPER FUNCTIONS
|
|
||||||
TreeNode<T>* copy_tree(TreeNode<T>* old_root) {
|
|
||||||
// Implemented in Lab 10
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_tree(TreeNode<T>* p) { /* Implemented in Lecture 18 */ }
|
|
||||||
|
|
||||||
iterator find(const T& key_value, TreeNode<T>* p) {
|
|
||||||
if (!p) return iterator(NULL);
|
|
||||||
if (p->value > key_value)
|
|
||||||
return find(key_value, p->left);
|
|
||||||
else if (p->value < key_value)
|
|
||||||
return find(key_value, p->right);
|
|
||||||
else
|
|
||||||
return iterator(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<iterator,bool> insert(const T& key_value, TreeNode<T>*& p) {
|
|
||||||
if (!p) {
|
|
||||||
p = new TreeNode<T>(key_value);
|
|
||||||
this->size_++;
|
|
||||||
return std::pair<iterator,bool>(iterator(p), true);
|
|
||||||
}
|
|
||||||
else if (key_value < p->value)
|
|
||||||
return insert(key_value, p->left);
|
|
||||||
else if (key_value > p->value)
|
|
||||||
return insert(key_value, p->right);
|
|
||||||
else
|
|
||||||
return std::pair<iterator,bool>(iterator(p), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
int erase(T const& key_value, TreeNode<T>* &p) { /* Implemented in Lecture 19 or 20 */ }
|
|
||||||
|
|
||||||
void print_in_order(std::ostream& ostr, const TreeNode<T>* p) const {
|
|
||||||
if (p) {
|
|
||||||
print_in_order(ostr, p->left);
|
|
||||||
ostr << p->value << "\n";
|
|
||||||
print_in_order(ostr, p->right);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_as_sideways_tree(std::ostream& ostr, const TreeNode<T>* p, int depth) const {
|
|
||||||
if (p) {
|
|
||||||
print_as_sideways_tree(ostr, p->right, depth+1);
|
|
||||||
for (int i=0; i<depth; ++i) ostr << " ";
|
|
||||||
ostr << p->value << "\n";
|
|
||||||
print_as_sideways_tree(ostr, p->left, depth+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include "ds_set.h"
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
|
|
||||||
// build a set
|
|
||||||
ds_set<std::string> set1;
|
|
||||||
|
|
||||||
std::pair< ds_set<std::string>::iterator, bool > insert_result;
|
|
||||||
std::string to_insert = std::string("hello");
|
|
||||||
insert_result = set1.insert(to_insert);
|
|
||||||
assert(insert_result.second);
|
|
||||||
|
|
||||||
insert_result = set1.insert(std::string("good-bye"));
|
|
||||||
assert(insert_result.second);
|
|
||||||
|
|
||||||
insert_result = set1.insert(std::string("friend"));
|
|
||||||
assert(insert_result.second);
|
|
||||||
|
|
||||||
insert_result = set1.insert(std::string("abc"));
|
|
||||||
assert(insert_result.second);
|
|
||||||
|
|
||||||
insert_result = set1.insert(std::string("puppy"));
|
|
||||||
assert(insert_result.second);
|
|
||||||
|
|
||||||
insert_result = set1.insert(std::string("zebra"));
|
|
||||||
assert(insert_result.second);
|
|
||||||
|
|
||||||
insert_result = set1.insert(std::string("daddy"));
|
|
||||||
assert(insert_result.second);
|
|
||||||
|
|
||||||
insert_result = set1.insert(std::string("puppy"));
|
|
||||||
assert(!insert_result.second && * insert_result.first == std::string("puppy"));
|
|
||||||
|
|
||||||
ds_set<std::string>::iterator p = set1.begin();
|
|
||||||
assert(p != set1.end() && *p == std::string("abc"));
|
|
||||||
|
|
||||||
|
|
||||||
// visualize the set
|
|
||||||
std::cout << "The set size is " << set1.size()
|
|
||||||
<< "\nHere are the elements: \n" << set1 << std::endl;
|
|
||||||
|
|
||||||
p = set1.find("foo");
|
|
||||||
if (p == set1.end())
|
|
||||||
std::cout << "\"foo\" is not in the set\n";
|
|
||||||
else
|
|
||||||
std::cout << "\"foo\" is in the set\n"
|
|
||||||
<< "The iterator points to " << *p << std::endl;
|
|
||||||
|
|
||||||
p = set1.find("puppy");
|
|
||||||
if (p == set1.end())
|
|
||||||
std::cout << "\"puppy\" is not in the set\n";
|
|
||||||
else
|
|
||||||
std::cout << "\"puppy\" is in the set\n"
|
|
||||||
<< "The iterator points to " << *p << std::endl;
|
|
||||||
|
|
||||||
p = set1.find("daddy");
|
|
||||||
if (p == set1.end())
|
|
||||||
std::cout << "\"daddy\" is not in the set\n";
|
|
||||||
else
|
|
||||||
std::cout << "\"daddy\" is in the set\n"
|
|
||||||
<< "The iterator points to " << *p << std::endl;
|
|
||||||
|
|
||||||
std::cout << "\nHere is the tree, printed sideways.\n"
|
|
||||||
<< "The indentation is proportional to the depth of the node\n"
|
|
||||||
<< "so that the value stored at the root is the only value printed\n"
|
|
||||||
<< "without indentation. Also, for each node, the right subtree\n"
|
|
||||||
<< "can be found above where the node is printed and indented\n"
|
|
||||||
<< "relative to it\n\n";
|
|
||||||
set1.print_as_sideways_tree(std::cout);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Needed for checkpoint 3
|
|
||||||
ds_set<std::string> set2(set1);
|
|
||||||
std::cout << "set1.size() = " << set1.size() << ", set2.size() = " << set2.size() << std::endl;
|
|
||||||
std::cout << "\nHere is set2 printed sideways:\n";
|
|
||||||
set2.print_as_sideways_tree(std::cout);
|
|
||||||
|
|
||||||
// Now add more stuff to set2.
|
|
||||||
insert_result = set2.insert(std::string("a"));
|
|
||||||
assert(insert_result.second);
|
|
||||||
insert_result = set2.insert(std::string("b"));
|
|
||||||
assert(insert_result.second);
|
|
||||||
std::cout << "\nAfter two inserts:\n"
|
|
||||||
<< "set1.size() = " << set1.size() << ", set2.size() = " << set2.size() << "\n"
|
|
||||||
<< "\nThe contents of set1:\n" << set1 << std::endl
|
|
||||||
<< "\nThe contents of set2:\n" << set2 << std::endl;
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
# Lab 11 — Stacks and Queues
|
|
||||||
|
|
||||||
In this lab, you will find out the answer to this question: When the STL queue library/class is not available to you, can you make your own queue? More specifically, in this lab, you will implement queues in different ways, and then fix memory leaks in the provided program. Start by downloading the provided program [levelOrder.cpp](levelOrder.cpp). The provided program [levelOrder.cpp](levelOrder.cpp) traverses a binary tree by level order. It prints the following message to STDOUT:
|
|
||||||
|
|
||||||
```console
|
|
||||||
Level Order Traversal: 1 2 3 4 5 6 7
|
|
||||||
Level Order Traversal: 1 2 3 4 5 6 7 8 9
|
|
||||||
Level Order Traversal: 1 2 3 4 5 6 7 8
|
|
||||||
```
|
|
||||||
|
|
||||||
## Checkpoint 1:
|
|
||||||
|
|
||||||
*estimate: 30-40 minutes*
|
|
||||||
|
|
||||||
First, read the code of the provided program, and run the program to see its output.
|
|
||||||
|
|
||||||
- Play this [animation](https://jidongxiao.github.io/CSCI1200-DataStructures/animations/trees/level_order/index.html) to see how level order traverse works.
|
|
||||||
|
|
||||||
The provided program includes the STL queue library with this line:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include <queue>
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, let us assume that the STL queue library is not available, but the STL stack library is avaiable, meaning that you are now not allowed to have this above line in the program, but you can have the following line in the program:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include <stack>
|
|
||||||
```
|
|
||||||
|
|
||||||
Do not change the *main* function. Do not change the *levelOrderTraversal* function, except this line:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
std::queue<TreeNode*> myQueue;
|
|
||||||
```
|
|
||||||
|
|
||||||
Can you still make the program work? i.e., still traversing a binary tree by level order, when the STL queue library is not available, but the STL stack library is available to you. You can implement your own classes or functions.
|
|
||||||
|
|
||||||
**To complete this checkpoint**: Show a TA your program, and your test results. Your program should still produce the same results as the original program. And you must be able to explain your program.
|
|
||||||
|
|
||||||
## Checkpoint 2:
|
|
||||||
|
|
||||||
*estimate: 30-40 minutes*
|
|
||||||
|
|
||||||
Now, let us assume that the STL queue library is not available, and the STL stack library is not avaiable, meaning that you are now not allowed to have either of following two lines in the program:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include <queue>
|
|
||||||
#include <stack>
|
|
||||||
```
|
|
||||||
|
|
||||||
However, you can include the STL list library like this:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include <list>
|
|
||||||
```
|
|
||||||
|
|
||||||
Still, do not change the *main* function, and do not change the *levelOrderTraversal* function, except this line:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
std::queue<TreeNode*> myQueue;
|
|
||||||
```
|
|
||||||
|
|
||||||
Can you still make the program work? i.e., still traversing a binary tree by level order, when neither the STL queue library nor the STL stack library is available, but the STL list library is available to you. You can implement your own classes or functions.
|
|
||||||
|
|
||||||
**To complete this checkpoint**: Show a TA your program, and your test results. Your program should still produce the same results as the original program. And you must
|
|
||||||
be able to explain your program.
|
|
||||||
|
|
||||||
## Checkpoint 3:
|
|
||||||
|
|
||||||
*estimate: 15-20 minutes*
|
|
||||||
|
|
||||||
The provided program clearly has memory leaks. Fix the memory leaks.
|
|
||||||
|
|
||||||
**To complete this checkpoint**: Show a TA your program, and your test results with either Valgrind or DrMemory.
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
// Definition for a binary tree node.
|
|
||||||
class TreeNode {
|
|
||||||
public:
|
|
||||||
int val;
|
|
||||||
TreeNode* left;
|
|
||||||
TreeNode* right;
|
|
||||||
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::vector<int>> levelOrderTraversal(TreeNode* root) {
|
|
||||||
std::vector<std::vector<int>> result;
|
|
||||||
|
|
||||||
if (root == nullptr) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::queue<TreeNode*> myQueue;
|
|
||||||
myQueue.push(root);
|
|
||||||
|
|
||||||
while (!myQueue.empty()) {
|
|
||||||
int size = myQueue.size();
|
|
||||||
std::vector<int> current_level;
|
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
TreeNode* current = myQueue.front();
|
|
||||||
myQueue.pop();
|
|
||||||
std::cout << current->val << " ";
|
|
||||||
|
|
||||||
if (current != nullptr) {
|
|
||||||
current_level.push_back(current->val);
|
|
||||||
|
|
||||||
if (current->left != nullptr) {
|
|
||||||
myQueue.push(current->left);
|
|
||||||
}
|
|
||||||
if (current->right != nullptr) {
|
|
||||||
myQueue.push(current->right);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.push_back(current_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
/* test case 1
|
|
||||||
* 1
|
|
||||||
* 2 3
|
|
||||||
* 4 5 6 7
|
|
||||||
*/
|
|
||||||
TreeNode* root1 = new TreeNode(1);
|
|
||||||
root1->left = new TreeNode(2);
|
|
||||||
root1->right = new TreeNode(3);
|
|
||||||
root1->left->left = new TreeNode(4);
|
|
||||||
root1->left->right = new TreeNode(5);
|
|
||||||
root1->right->left = new TreeNode(6);
|
|
||||||
root1->right->right = new TreeNode(7);
|
|
||||||
|
|
||||||
std::cout << "Level Order Traversal: ";
|
|
||||||
levelOrderTraversal(root1);
|
|
||||||
std::cout << std::endl;
|
|
||||||
|
|
||||||
/* test case 2
|
|
||||||
* 1
|
|
||||||
* 2 3
|
|
||||||
* 4 5 6 7
|
|
||||||
* 8 9
|
|
||||||
*/
|
|
||||||
TreeNode* root2 = new TreeNode(1);
|
|
||||||
root2->left = new TreeNode(2);
|
|
||||||
root2->right = new TreeNode(3);
|
|
||||||
root2->left->left = new TreeNode(4);
|
|
||||||
root2->left->right = new TreeNode(5);
|
|
||||||
root2->right->left = new TreeNode(6);
|
|
||||||
root2->right->right = new TreeNode(7);
|
|
||||||
root2->right->right->left = new TreeNode(8);
|
|
||||||
root2->right->right->right = new TreeNode(9);
|
|
||||||
|
|
||||||
std::cout << "Level Order Traversal: ";
|
|
||||||
levelOrderTraversal(root2);
|
|
||||||
std::cout << std::endl;
|
|
||||||
|
|
||||||
/* test case 3
|
|
||||||
* 1
|
|
||||||
* 2 3
|
|
||||||
* 4 5 6 7
|
|
||||||
* 8
|
|
||||||
*/
|
|
||||||
TreeNode* root3 = new TreeNode(1);
|
|
||||||
root3->left = new TreeNode(2);
|
|
||||||
root3->right = new TreeNode(3);
|
|
||||||
root3->left->left = new TreeNode(4);
|
|
||||||
root3->left->right = new TreeNode(5);
|
|
||||||
root3->right->left = new TreeNode(6);
|
|
||||||
root3->right->right = new TreeNode(7);
|
|
||||||
root3->left->left->left = new TreeNode(8);
|
|
||||||
|
|
||||||
std::cout << "Level Order Traversal: ";
|
|
||||||
levelOrderTraversal(root3);
|
|
||||||
std::cout << std::endl;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user