renaming labs

This commit is contained in:
Jidong Xiao
2025-01-07 17:19:02 -05:00
parent 77eb9240b2
commit b6b2f01b8a
18 changed files with 0 additions and 1547 deletions

View File

@@ -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 whats the problem?
Before we fix the problem, lets 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.-->

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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 dont 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 youre 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 isnt 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 lets 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-->
Heres 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: Heres 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 lets 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. Lets 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 its executed).
Use the next command to move down until we hit our next recursive call. Once on this line, lets 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, lets 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.

View File

@@ -1,7 +0,0 @@
1 0
2 1 2 3
3 3 3 4
5 5
0 0
4 4

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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
&nbsp;
&nbsp;
&nbsp;
&nbsp;
&nbsp;
&nbsp;
&nbsp;
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
&nbsp;
&nbsp;
&nbsp;
&nbsp;
&nbsp;
&nbsp;
&nbsp;
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 lets 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.

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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;
}