From 77eb9240b2f7ce086e9bf1e0ebdd66640cc89e77 Mon Sep 17 00:00:00 2001 From: Jidong Xiao Date: Tue, 7 Jan 2025 17:17:59 -0500 Subject: [PATCH] renaming labs --- labs/04_memory_debugging/README.md | 128 --------- labs/04_memory_debugging/buggy_lab4.cpp | 116 -------- labs/04_memory_debugging/desserts.cpp | 10 - labs/04_memory_debugging/first.txt | 4 - labs/04_memory_debugging/fruits.cpp | 10 - labs/04_memory_debugging/grains.cpp | 10 - labs/04_memory_debugging/last.txt | 49 ---- labs/04_memory_debugging/middle.txt | 10 - labs/04_memory_debugging/protein.cpp | 11 - labs/04_memory_debugging/veggies.cpp | 10 - labs/05_vectors/README.md | 218 -------------- labs/05_vectors/test_vec.cpp | 97 ------- labs/05_vectors/vec.h | 141 --------- labs/06_lists_iterators/README.md | 101 ------- labs/06_lists_iterators/checkpoint1.cpp | 47 --- labs/06_lists_iterators/checkpoint3.cpp | 63 ---- .../reference_to_a_pointer.cpp | 28 -- labs/06_lists_iterators/reverse_iterator.cpp | 18 -- labs/list_implementation/README.md | 77 +++++ labs/list_implementation/checkpoint1.cpp | 73 +++++ labs/list_implementation/checkpoint2.cpp | 82 ++++++ labs/list_implementation/checkpoint3.cpp | 94 ++++++ labs/list_implementation/dslist.h | 268 ++++++++++++++++++ labs/maps/README.md | 50 ++++ labs/maps/phonebook.cpp | 34 +++ labs/recursion/README.md | 158 +++++++++++ labs/recursion/grid1.txt | 7 + labs/recursion/grid2.txt | 11 + labs/recursion/grid3.txt | 28 ++ labs/recursion/grid4.txt | 41 +++ labs/recursion/start.cpp | 130 +++++++++ labs/stacks_and_queues/README.md | 75 +++++ labs/stacks_and_queues/levelOrder.cpp | 110 +++++++ labs/trees_I/README.md | 57 ++++ labs/trees_I/ds_set.h | 155 ++++++++++ labs/trees_I/test_ds_set.cpp | 97 +++++++ 36 files changed, 1547 insertions(+), 1071 deletions(-) delete mode 100644 labs/04_memory_debugging/README.md delete mode 100644 labs/04_memory_debugging/buggy_lab4.cpp delete mode 100644 labs/04_memory_debugging/desserts.cpp delete mode 100644 labs/04_memory_debugging/first.txt delete mode 100644 labs/04_memory_debugging/fruits.cpp delete mode 100644 labs/04_memory_debugging/grains.cpp delete mode 100644 labs/04_memory_debugging/last.txt delete mode 100644 labs/04_memory_debugging/middle.txt delete mode 100644 labs/04_memory_debugging/protein.cpp delete mode 100644 labs/04_memory_debugging/veggies.cpp delete mode 100644 labs/05_vectors/README.md delete mode 100644 labs/05_vectors/test_vec.cpp delete mode 100644 labs/05_vectors/vec.h delete mode 100644 labs/06_lists_iterators/README.md delete mode 100644 labs/06_lists_iterators/checkpoint1.cpp delete mode 100644 labs/06_lists_iterators/checkpoint3.cpp delete mode 100644 labs/06_lists_iterators/reference_to_a_pointer.cpp delete mode 100644 labs/06_lists_iterators/reverse_iterator.cpp create mode 100644 labs/list_implementation/README.md create mode 100644 labs/list_implementation/checkpoint1.cpp create mode 100644 labs/list_implementation/checkpoint2.cpp create mode 100644 labs/list_implementation/checkpoint3.cpp create mode 100644 labs/list_implementation/dslist.h create mode 100644 labs/maps/README.md create mode 100644 labs/maps/phonebook.cpp create mode 100644 labs/recursion/README.md create mode 100644 labs/recursion/grid1.txt create mode 100644 labs/recursion/grid2.txt create mode 100644 labs/recursion/grid3.txt create mode 100644 labs/recursion/grid4.txt create mode 100644 labs/recursion/start.cpp create mode 100644 labs/stacks_and_queues/README.md create mode 100644 labs/stacks_and_queues/levelOrder.cpp create mode 100644 labs/trees_I/README.md create mode 100644 labs/trees_I/ds_set.h create mode 100644 labs/trees_I/test_ds_set.cpp diff --git a/labs/04_memory_debugging/README.md b/labs/04_memory_debugging/README.md deleted file mode 100644 index 80d22af..0000000 --- a/labs/04_memory_debugging/README.md +++ /dev/null @@ -1,128 +0,0 @@ -# Lab 4 — Memory Diagrams and Memory Debugging - -## Checkpoint 1 -*estimate: 30-45 minutes* - -- Form a group of 2 members. If the number of students in the room is not divisible by 2, the graduate TA will approve a single team with 3 members. - -- Introduce yourself to your teammate. Share to your teammate something about yourself (e.g. hobbies, sports, favorite music, etc). Learn something new about your teammate (even if you already know the teammate). - -- Our TA/mentor will pick 3 code snippets for each student, from the following 5 code snippets. Following the conventions used in Data Structures lecture for memory diagramming, draw a picture of the stack and the heap that result from executing the code snippet. Use a ‘?’ to represent uninitialized values. - -[Code Snippet 1](fruits.cpp) -[Code Snippet 2](grains.cpp) -[Code Snippet 3](desserts.cpp) -[Code Snippet 4](veggies.cpp) -[Code Snippet 5](protein.cpp) - -Show your diagram to your teammate, and ask the teammate to reverse engineer code for your diagram; at the same time, you reverse engineer code for your teammate's diagram. - -When everyone has finished writing code for the drawing, discuss your answers as a group. Are differences in the original and reverse engineered code valid alternate answers? Or was something misinterpreted? Critique each other’s diagrams. What could be improved or drawn more clearly? What statements need to be added to each example to clean up / prevent memory leaks? - -**To complete this checkpoint**, present your work to a TA/mentor. - -## Checkpoint 2 & 3: - -Checkpoints 2 and 3 focus on using a memory debugger. It is highly recommended that you thoroughly read the instructions for Checkpoint 2 and Checkpoint 3 before starting. - -Memory debuggers will be a useful tool in many of the assignments this semester, and in C++ development if you work with the language outside of this course. While a traditional debugger lets you step through your code and examine variables, a memory debugger instead reports memory-related errors during execution and can help find memory leaks after your program has terminated. The next time you see a “segmentation fault”, or it works on your machine but not on Submitty, try running a memory debugger! - -Read lecture notes [6.7](../../lectures/06_memory#67-memory-debugging), [6.8](../../lectures/06_memory#68-sample-buggy-program), [6.9](../../lectures/06_memory#69-using-dr-memory-httpwwwdrmemoryorg), [6.10](../../lectures/06_memory#610-using-valgrind-httpvalgrindorg), and [6.11](../../lectures/06_memory#610-using-valgrind-httpvalgrindorg), and try to understand the example buggy program code and what DrMemory or Valgrind say about this buggy program. Ask questions if you don't understand the code or if you don't understand the output of DrMemory or the output of Valgrind. - -Please download the following 4 files needed for this lab: - -[buggy_lab4.cpp](./buggy_lab4.cpp) -[first.txt](./first.txt) -[middle.txt](./middle.txt) -[last.txt](./last.txt) - -## Checkpoint 2 -*estimate: 20-40 minutes* - -For Checkpoint 2 of this lab, we will heavily rely on dynamic memory to find the average and smallest number for a set of data from an input file. You will use a memory debugging tool such as DrMemory or Valgrind to fix memory errors and leaks in buggy_lab4.cpp. Make sure to download the provided .txt files as well. -First take a look at the code inside the identifyMeanAndMin() function. You will notice that the syntax used -throughout the program may be a little different than what you are used to. Try and familiarize yourself with -this syntax before you start working on the assignment. What does this line of code do? - -```cpp -*(intArray + *numElements) = readInt; -``` - -Whats going on inside the *for* loop? If you are stuck on this, ask a mentor or TA for help as -soon as possible and refer back to your lecture notes on pointers and arrays. -Once you have done this, compile the program using: -```console -g++ buggy_lab4.cpp -o buggy_lab4.out -g -Wall -``` - -Try running the program normally using: - -```console -./buggy_lab4.out -``` - -You will notice that a segmentation fault occurs. Now run this program using either Valgrind or DrMemory. If running using Valgrind, remember to use --leak-check=yes or --leak-check=full. - -Your memory debugger should give you more context as to why a segmentation fault is occurring, especially if you have compiled with *-g*. To complete this checkpoint, add or modify code in the areas marked Parts 1, 2, and 3 to resolve all memory errors and leaks. As you are working on this, be sure to also think about the questions asked in Checkpoint 3. - -It is highly recommended that you tackle one part at a time. For example, after adding a few lines of code to part 1, you will now receive different memory errors when you recompile and run the program using your memory debugger. Similarly, fixing all memory errors in part 2 will generate different memory errors that should be resolved in part 3. - -In part 2 of buggy_lab4.cpp, the goal is to print out the contents of intArray in reverse order, while also calculating the sum of all elements in the array and keeping track of the smallest number encountered. Solutions that attempt to print the contents of the array in a different manner or end up with the wrong -value for the smallest number found or sum won't be accepted. -Note that: -- You are only allowed to modify or add code when asked to. This would be between the comments that -indicate parts 1, 2, and 3 inside buggy_lab4.cpp. Do not modify other parts of the code or create any -helper functions. -- You are not allowed to declare new variables; the ones provided are more than enough. Hint: how do -we create memory on the heap? -- You are not allowed to use additional data structures (like arrays or vectors). - -**You will receive no credit if you do not follow the above restrictions.** - -**To receive credit for this checkpoint**: Fix buggy_lab4.cpp so that it successfully prints out the average -and smallest number for a given set of data and is free of all memory errors and leaks on your local machine. -Submit buggy_lab4.cpp on Submitty and verify that there are no memory errors there as well and show a -mentor or TA both results. Also explain to a mentor TA what you added or modified in the program to -resolve all memory errors. - -## Checkpoint 3 -*estimate: 15-25 minutes* - -As you work through Checkpoint 2, try and pay close attention the analysis given by DrMemory or Valgrind -and think about the following: - -1. How does the output from your memory debugger differ when you compile your program with the -g -flag compared to when you leave it out? -2. How would you rewrite the for loop in part 2 to use the bracket [] operator instead of pointer syntax? -3. For DrMemory users, you would have encountered all of these errors in parts 1, 2, or 3 of Checkpoint 2: - -```console -UNITIALIZED READ -UNADDRESSABLE ACCESS -INVALID HEAP ARGUMENT -LEAK -``` - -4. What do you think each of these errors mean? -5. For Valgrind users, the errors you will have seen are: -```console -Use of uninitialised value -Invalid write -Invalid read -Conditional jump or move depends on uninitialised value(s) -Mismatched free() / delete / delete [] -? bytes in ? blocks are definitely lost in loss record ? of ? -``` - -**To receive credit for this checkpoint**, discuss your answers to these questions with a TA or mentor. - -## Checkpoint 4 -*remainder of the lab time* - -For this checkpoint, you team up with one or two other students from your lab section. Our TA or mentor will pick one problem from the practice packet for you two (or three) to work together as a group. You should not look at the posted solutions. Work as a group (e.g., pair programming!) to come up with a complete solution to the problem. - -Once you have completed and proofread and triple-checked your answer, launch your C++ development environment. First, focus on getting it to compile. What basic, minimal testing infrastructure is necessary to get your solution to compile? What small bits of syntax were you missing or incorrect or sloppy? Try to avoid making these mistakes on Thursday's test! - -Then, write a simple test case to exercise your solution. Compile and run the code. As time follows, fix bugs in your solution and improve and expand your test cases. - -**To complete this checkpoint**, When about 10 minutes remain in the lab, the TA and mentors will circulate through the room to finalize checkoffs. Show them your initial solution, and explain the typos and errors you found and debugged in working through your computer tested solution. Explain your testing & debugging process. diff --git a/labs/04_memory_debugging/buggy_lab4.cpp b/labs/04_memory_debugging/buggy_lab4.cpp deleted file mode 100644 index 302d8fb..0000000 --- a/labs/04_memory_debugging/buggy_lab4.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include -#include - -#define MAX_ARRAY_SIZE 50 - - - -/** IMPORTANT: SOLUTIONS THAT DO NOT FOLLOW BELOW INSTRUCTIONS WILL RECEIVE NO CREDIT - * Do not add or modify code in this file UNLESS ASKED TO! - * You are NOT allowed to declare new variables; use the ones already declared. - * You are NOT allowed to create new helper functions or modify checkCorrectSmallest() -**/ - - - -/** DO NOT MODIFY THIS FUNCTION! **/ -int checkCorrectSmallest(int* smallestNum, const std::string& filename) { - if (filename == "first.txt" || filename == "middle.txt") { - if (*smallestNum != 1) {return -1;} - } - - else if (filename == "last.txt") { - if (*smallestNum != 22) {return -1;} - } - - return 1; -} - -/** A function that will identify the mean and smallest number - * in a set of data provided that there are at most 50 numbers - * in the file. - * - * @param filename: The name of a file that contains a list of numbers. - * - * Task: Add or modify code in the appropriate sections to fix memory and logic errors - * without using data structures (such as an array or vector) and without using any - * additional memory on the stack. -**/ - -void identifyMeanAndMin(const std::string& filename) { - int* numElements; - int* sum; - int* smallestNum; - float* avg; - - /** PART 1: ADD CODE BELOW **/ - - - - - /** PART 1: ADD CODE ABOVE **/ - - *numElements = 0; - *sum = 0; - - int readInt; - int* intArray = new int[MAX_ARRAY_SIZE]; - std::ifstream input(filename); - - while (input >> readInt) { - *(intArray + *numElements) = readInt; - *numElements += 1; - } - - std::cout << "Printing the contents of the array in reverse order: "; - - /** PART 2: MODIFY CODE BELOW **/ - - for (int i = MAX_ARRAY_SIZE; i >= -1; i--) { - // If we're at the beginning of the for loop, initalize *smallestNum - // Else, compare *smallestNum to current element in the for loop - if (i == MAX_ARRAY_SIZE) { - *smallestNum = *(intArray + i); - } - - else { - if (*smallestNum > *(intArray + i)) { - *smallestNum = *(intArray + i); - } - } - - /** PART 2: MODIFY CODE ABOVE **/ - - *sum += *(intArray + i); - std::cout << *(intArray + i) << " "; - } - - std::cout << std::endl; - if (checkCorrectSmallest(smallestNum, filename) == -1) { - std::cout << "ERROR: incorrect value for smallest number" << std::endl; - return; - } - - *avg = *sum / float(*numElements); - std::cout << "The average for the set of numbers in " << filename << " is " - << *avg << " and the smallest number is " << *smallestNum - << std::endl; - - /** PART 3: ADD AND/OR MODIFY CODE BELOW **/ - - delete intArray; - - /** PART 3: ADD AND/OR MODIFY CODE ABOVE **/ -} - -int main() { - identifyMeanAndMin("first.txt"); - std::cout << std::endl; - - identifyMeanAndMin("middle.txt"); - std::cout << std::endl; - - identifyMeanAndMin("last.txt"); - return 0; -} - diff --git a/labs/04_memory_debugging/desserts.cpp b/labs/04_memory_debugging/desserts.cpp deleted file mode 100644 index 7adc7a9..0000000 --- a/labs/04_memory_debugging/desserts.cpp +++ /dev/null @@ -1,10 +0,0 @@ -bool** cake; -bool pie; -bool fudge; -pie = true; -cake = new bool*[5]; -cake[1] = &pie; -bool* donut = new bool; -*donut = false; -cake[2] = donut; -cake[4] = &fudge; diff --git a/labs/04_memory_debugging/first.txt b/labs/04_memory_debugging/first.txt deleted file mode 100644 index 94ebaf9..0000000 --- a/labs/04_memory_debugging/first.txt +++ /dev/null @@ -1,4 +0,0 @@ -1 -2 -3 -4 diff --git a/labs/04_memory_debugging/fruits.cpp b/labs/04_memory_debugging/fruits.cpp deleted file mode 100644 index be8a768..0000000 --- a/labs/04_memory_debugging/fruits.cpp +++ /dev/null @@ -1,10 +0,0 @@ -int pear = 3; -int* apple; -int banana[pear]; -int* orange; -apple = new int[pear]; -orange = &banana[1]; -apple[0] = 6; -apple[1] = 7; -apple[2] = 8; -*orange = 5; diff --git a/labs/04_memory_debugging/grains.cpp b/labs/04_memory_debugging/grains.cpp deleted file mode 100644 index 32dfed6..0000000 --- a/labs/04_memory_debugging/grains.cpp +++ /dev/null @@ -1,10 +0,0 @@ -float* oat[3]; -oat[1] = new float; -*oat[1] = 3.14; -oat[2] = new float; -*oat[2] = 6.02; -float rice; -float* wheat; -wheat = oat[2]; -float** barley = new float*; -*barley = oat[1]; diff --git a/labs/04_memory_debugging/last.txt b/labs/04_memory_debugging/last.txt deleted file mode 100644 index e0b0db9..0000000 --- a/labs/04_memory_debugging/last.txt +++ /dev/null @@ -1,49 +0,0 @@ -22 -247 -279 -240 -253 -342 -87 -181 -391 -57 -23 -302 -168 -367 -236 -240 -187 -368 -216 -185 -31 -255 -122 -140 -69 -46 -287 -69 -268 -58 -134 -330 -172 -291 -175 -63 -184 -329 -30 -337 -229 -274 -130 -95 -255 -331 -24 -325 -228 diff --git a/labs/04_memory_debugging/middle.txt b/labs/04_memory_debugging/middle.txt deleted file mode 100644 index 19a5cf0..0000000 --- a/labs/04_memory_debugging/middle.txt +++ /dev/null @@ -1,10 +0,0 @@ -89 -34 -13 -5 -2 -1 -3 -8 -21 -55 diff --git a/labs/04_memory_debugging/protein.cpp b/labs/04_memory_debugging/protein.cpp deleted file mode 100644 index fb179f8..0000000 --- a/labs/04_memory_debugging/protein.cpp +++ /dev/null @@ -1,11 +0,0 @@ -int tofu = 3; -int chicken = 2; -double** fish = new double*[tofu]; -for (int beef = 0; beef < tofu; beef++) { - fish[beef] = new double[chicken]; -} -fish[0][0] = 1.41421; -fish[0][1] = 1.61803; -fish[1][0] = 2.71828; -fish[1][1] = 3.14159; -fish[2][0] = 6.02214; diff --git a/labs/04_memory_debugging/veggies.cpp b/labs/04_memory_debugging/veggies.cpp deleted file mode 100644 index ad75f22..0000000 --- a/labs/04_memory_debugging/veggies.cpp +++ /dev/null @@ -1,10 +0,0 @@ -char*** carrot; -char** broccoli; -char* tomato; -char radish = 'q'; -tomato = new char; -*tomato = 'z'; -broccoli = new char*; -*broccoli = tomato; -carrot = new char**; -*carrot = broccoli; diff --git a/labs/05_vectors/README.md b/labs/05_vectors/README.md deleted file mode 100644 index ed173b7..0000000 --- a/labs/05_vectors/README.md +++ /dev/null @@ -1,218 +0,0 @@ -# Lab 5 — Vec Implementation - -## Checkpoint 1 -*estimate: 20 minutes* - -- Team up with one student in your lab section. MEET SOMEONE NEW! You may not work -with someone who was on your team for Lab 4. Ask a TA or mentor to help you find a partner. -If the number of students in the room is not even, the graduate TA will approve a single team with 3 -members. -- Introduce yourself to your teammate. Ask them to share something about themselves (e.g. hobbies, -sports, favorite music, etc.) Learn something new about your teammate (even if you already know -them). -- There are two sets of functions below. Each student in your team takes one set and working on it. - -For each function below, assign different letters to each of the data sizes that at first glance might have impact -on the running time of the function. Be sure to consider integer value, size of vector, and length of string. -Then give the big O notation of the function in terms of those variables. - -### Student 1 - -```cpp -int foobar (const std::vector &a, int b) { - int answer = 0; - for (int i = 0; i < a.size(); i+=b) { - answer++; - } - return answer; -} -``` - -```cpp -void foo2 (const std::vector &a, std::string &b) { - b.clear(); - for (int i = 0; i < a.size(); i++) - { - if (a[i] > 0) - b.push_back('+'); - else - b.push_back('-'); - } -} -``` - -```cpp -std::vector foo3 (const std::vector &a, const std::string &b) { - return std::vector(b.size(),a.size()); -} -``` - -```cpp -int foo3 (const std::vector &a, const std::string& b) { - int ret = 0; - for (int i=0; i foo4 (const std::vector &a) { - std::vector answer = a; - for (int i = 0; i < a.size(); i++) { - if(a[i] < (a[a.size()-1]*a[a.size()-1])){ - answer.erase(answer.end()-1); - } - } - return answer; -} -``` - -```cpp -std::vector foo5 (const std::vector &a, int b) { - std::vector ret; - for(int i=0; i &a, int b) { - int answer = 0; - for (int i = 0; i < a.size(); i+=b) { - answer++; - } - return answer; -} -``` - -```cpp -std::vector bar2 (const std::vector &a) { - std::vector answer; - for (int i = 0; i < a.size(); i++) { - answer.push_back(a[i].size()); - } - return answer; -} -``` - -```cpp -std::vector bar3 (const std::vector &a) { - std::vector answer; - for (int i = 0; i < a.size(); i++) { - answer.push_back(std::string(a[i],'+')); - } - return answer; -} -``` - -```cpp -void bar3 (std::vector &a, const std::string &b) { - for (int i = 0; i < a.size(); i++) { - a[i] = b; - } -} -``` - -```cpp -std::vector bar4 (const std::vector &a) { - std::vector answer; - if(!a.empty()){ - for (int i = 0; i < std::min(a[0].size(), a.size()); i++) { - answer.insert(answer.begin(),a[i].size()); - } - } - return answer; -} -``` - -```cpp -void bar5 (std::vector &a) { - for (int i = 0; i < a.size(); i++){ - if (a[i] > 0){ - a.erase(a.begin() + i); - i--; - } - } -} -``` - -When you finish, discuss these problems with your teammate. If your teammate hasn’t finished, please help -them (but without just doing the problems for them). - -Once you are both finished, type these examples into your C++ editor, add print statements, and confirm your answers are correct. What print statements will be most helpful? In your terminal, instead of running just - -```console -./a.out -``` - -try running - -```console -time ./a.out -``` - -and reading the real time, which is how long your program took to run. How does this change as you increase or decrease each of the data size variables you identified above? - -**To complete this checkpoint**, as a team, present your work to a TA/mentor. - -## Checkpoint 2 -*estimate: 30-40 minutes* - -Write a templated non-member function named remove_matching_elements that takes in two arguments, -a vector of type Vec<T> and an element of type T, and returns the number of elements that matched the -argument and were successfully removed from the vector. The order of the other elements should stay -the same. For example, if v, a Vec<int> object contains 6 elements: 11 22 33 11 55 22 and you call -remove_matching_elements(v,11), that call should return 2, and v should now contain: 22 33 55 22. -You should not create a new vector in your function. -Add several test cases to test_vec.cpp to show that the function works as expected. What is the order -notation of your solution in terms of n the size of the vector, and e the number of occurences of the input -element in the vector? - -*Note*: when you are in a non-member function, and you want to use the iterator, which is a member type of the Vec<T> class, you have to use the *typename* keyword before the Vec<T>:: scope. For example, if you want to define an iterator named *itr*, you can do it like this: - -```console -typename Vec::iterator itr -``` - -without this *typename* keyword, if you define the iterator *itr* like this: - -```console -Vec::iterator itr -``` - -you will get a compiler error saying: - -```console -error: need ‘typename’ before ‘Vec::iterator’ because ‘Vec’ is a dependent scope -``` - -And the reason that this keyword *typename* is needed, is because without it, the compiler would think that Vec<T>::iterator is a member variable of the Vec<T> class, but this *typename* explicitly tells the compiler that Vec<T>::iterator is a type, rather than a member variable. - -**To complete this checkpoint**, show a TA your debugged solution for remove_matching_elements and -be prepared to discuss the order notation of the function. - -## Checkpoint 3 -*estimate: 30 minutes* - -Add a print member function to Vec to aid in debugging. (Note, neither remove_matching_elements nor -print are part of the STL standard for vector). You should print the current information stored in the -variables capacity, m_size, and m_data. Use the print function to confirm your remove_matching_elements -function is debugged. Also, write a test case that calls push_back many, many times (hint, use a for loop!) -and observe how infrequently re-allocation of the m_data array is necessary. -To verify your code does not contain memory errors or memory leaks, use Valgrind and/or Dr. Memory on -your local machine – see instructions on the course webpage: Memory Debugging. Also, submit your code -to the homework server (in the practice space for lab 4), which is configured to run the memory debuggers -for this exercise. To verify that you understand the output from Valgrind and/or Dr. Memory, temporarily -add a simple bug into your implementation to cause a memory error or memory leak. - -**To complete this checkpoint**, show a TA your tested & debugged program. Be prepared to demo and -discuss the Valgrind and/or Dr. Memory output: with and without memory errors and memory leaks AND -on your local machine and on the homework server. diff --git a/labs/05_vectors/test_vec.cpp b/labs/05_vectors/test_vec.cpp deleted file mode 100644 index 762f0ed..0000000 --- a/labs/05_vectors/test_vec.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include -using namespace std; - -#include "vec.h" - -int main() { - - // --------------------------------------------------- - // initialize v1 with 10 values... the multiples of 5 - Vec v1( 10, 0 ); - int i; - for ( i = 0; i < v1.size(); i++) { - v1[i] = 5 * i; - } - cout << "v1.size() = " << v1.size() << ". Should be 10.\n"; - cout << "Contents of v1 (multiples of 5):"; - for ( i = 0; i v2( v1 ); - v2[ 9 ] = v2[ 0 ]; - v2[ 8 ] = v2[ 1 ]; - v2[ 7 ] = v2[ 2 ]; - v2[ 6 ] = v2[ 3 ]; - v2[ 5 ] = v2[ 4 ]; - cout << "Contents of v1 (still multiples of 5):"; - for ( i = 0; i v3; - v3 = v2; - v3.clear(); - cout << "\nAfter copying v2 to v3 and clearing v3, v2.size() = " - << v2.size() << " and v3.size() = " << v3.size() << endl; - cout << "Contents of v2 (should be unchanged):"; - for ( i = 0; i z; - for ( i = 0; i<5; ++i ) - z.push_back( sqrt( double(10*(i+1)) )); - cout << "Contents of vector z: "; - for (int j = 0; j < z.size(); j++ ) - cout << " " << z[j]; - cout << endl; - - - - // ADD MORE TEST CASES HERE - - - return 0; -} diff --git a/labs/05_vectors/vec.h b/labs/05_vectors/vec.h deleted file mode 100644 index 255d0aa..0000000 --- a/labs/05_vectors/vec.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef Vec_h_ -#define Vec_h_ -// Simple implementation of the vector class, revised from Koenig and Moo. This -// class is implemented using a dynamically allocated array (of templated type T). -// We ensure that that m_size is always <= capacity and when a push_back or resize -// call would violate this condition, the data is copied to a larger array. - -template class Vec { - -public: - // TYPEDEFS - typedef T* iterator; - typedef const T* const_iterator; - - // CONSTRUCTORS, ASSIGNMNENT OPERATOR, & DESTRUCTOR - Vec() { this->create(); } - Vec(int n, const T& t = T()) { this->create(n, t); } - Vec(const Vec& v) { copy(v); } - Vec& operator=(const Vec& v); - ~Vec() { delete [] m_data; } - - // MEMBER FUNCTIONS AND OTHER OPERATORS - T& operator[] (int i) { return m_data[i]; } - const T& operator[] (int i) const { return m_data[i]; } - void push_back(const T& t); - iterator erase(iterator p); - void resize(int n, const T& fill_in_value = T()); - void clear() { delete [] m_data; create(); } - bool empty() const { return m_size == 0; } - int size() const { return m_size; } - - // ITERATOR OPERATIONS - iterator begin() { return m_data; } - const_iterator begin() const { return m_data; } - iterator end() { return m_data + m_size; } - const_iterator end() const { return m_data + m_size; } - -private: - // PRIVATE MEMBER FUNCTIONS - void create(); - void create(int n, const T& val); - void copy(const Vec& v); - - // REPRESENTATION - T* m_data; // Pointer to first location in the allocated array - int m_size; // Number of elements stored in the vector - int capacity; // Number of array locations allocated, m_size <= capacity -}; - -// Create an empty vector (null pointers everywhere). -template void Vec::create() { - m_data = NULL; - m_size = capacity = 0; // No memory allocated yet -} - -// Create a vector with size n, each location having the given value -template void Vec::create(int n, const T& val) { - m_data = new T[n]; - m_size = capacity = n; - for (T* p = m_data; p != m_data + m_size; ++p) - *p = val; -} - -// Assign one vector to another, avoiding duplicate copying. -template Vec& Vec::operator=(const Vec& v) { - if (this != &v) { - delete [] m_data; - this -> copy(v); - } - return *this; -} - -// Create the vector as a copy of the given vector. -template void Vec::copy(const Vec& v) { - this->capacity = v.capacity; - this->m_size = v.m_size; - this->m_data = new T[this->capacity]; - - // Copy the data - for (int i = 0; i < this->m_size; ++i) - this -> m_data[ i ] = v.m_data[ i ]; -} - -// Add an element to the end, resize if necesssary. -template void Vec::push_back(const T& val) { - if (m_size == capacity) { - // Allocate a larger array, and copy the old values - - // Calculate the new allocation. Make sure it is at least one. - capacity *= 2; - if (capacity < 1) capacity = 1; - - // Allocate and copy the old array - T* new_data = new T[capacity]; - for (int i=0; i typename Vec::iterator Vec::erase(iterator p) { - // remember iterator and T* are equivalent - for (iterator q = p; q < m_data+m_size-1; ++q) - *q = *(q+1); - m_size --; - return p; -} - -// If n is less than or equal to the current size, just change the size. If n is -// greater than the current size, the new slots must be filled in with the given value. -// Re-allocation should occur only if necessary. push_back should not be used. -template void Vec::resize(int n, const T& fill_in_value) { - if (n <= m_size) - m_size = n; - else { - // If necessary, allocate new space and copy the old values - if (n > capacity) { - capacity = n; - T* new_data = new T[capacity]; - for (int i=0; i a; -unsigned int i; -for ( i=1; i<10; ++i ){ - a.push_back( i*i ); -} -std::list::reverse_iterator ri; -for( ri = a.rbegin(); ri != a.rend(); ++ri ){ - std::cout << *ri << std::endl; -} -``` - -This code will print out the values 81, 64, 49, . . . , 1, in order, on separate lines. You can also compile and run this [example program](reverse_iterator.cpp). - -Observe the type for the reverse iterator, the use of the functions rbegin and rend to provide iterators that delimit the bounds on -the reverse iterator, and the use of the ++ operator to take one step backwards through the list. It is very -important to realize that rbegin and end are NOT the same thing! One of the challenges here will be -determining when to stop (when you’ve reached the halfway point in the list). You may use an integer -counter variable to help you do this. - -For this checkpoint you should not use *erase*, or *insert*, or the *push* or *pop* functions. -Note, you’ll probably need to add the keyword typename in front of your templated iterator types to unconfuse the compiler. - -```cpp -typename std::list::iterator itr = data.begin(); -``` - -**To complete this checkpoint**, show a TA your debugged functions to reverse STL vectors and STL lists -by element swapping. Be sure to ask your TA/mentors any questions you have about regular vs. reverse -iterators for lists and vectors. - -## Checkpoint 2: Reverse with STL List Using Insert/Erase/Push/Pop -*estimate: TBD* - -Form a team of 4. You may form a team of 2 or 3 only with approval from your graduate lab TA. - -Each student should make a copy of their solution file for Checkpoint 1B. And then, each student should -rewrite their STL list reverse function: - - **STUDENT 1** Using only front(), pop_front(), and insert(). - - **STUDENT 2** Using only back(), pop_back(), insert(), and iterator increment. - - **STUDENT 3** Using only erase(), push_front(), and iterator dereference. - - **STUDENT 4** Using only erase(), push_back(), iterator dereference, and iterator decrement. -Each of these solutions is allowed to use a for or while loop and a temporary variable to store a single -element, but should not use any auxiliary data structures (no array, no vector, no additional list, etc.) - -Note that these solutions are quite different than the algorithms that reverse a vector or list by swapping -values. If the algorithm is paused and printed midway, the vector contents will be different than the algorithms -for Checkpoint 1. (Go ahead, add more calls to the print function in the middle of your reverse function.) Test -and debug your own code before helping your teammates. Discuss the similarities and differences between -the solutions to each version of the reverse function. - -**To complete this checkpoint**, as a team, present your debugged solutions to a TA or mentor. - -## Checkpoint 3: Reversing a Homemade Linked List -*estimate: TBD* - -This checkpoint is an individual checkpoint. (But you can ask your teammates questions if you get stuck.) -Pull out some paper. Following the conventions from lecture, draw a picture of a “homemade” singly-linked -list that stores the values 1, 2, 3, and 4. Make a variable on the stack named my_list of type Node* that -points to the first node in the chain (the node storing the value 1). The 4 node objects should be separate -blobs of memory dynamically-allocated on the heap. - -Now, modify this diagram to reverse the list – you can do this by only changing pointers! You should use the -existing node objects. Don’t copy the entire diagram or make any new nodes. You should not change the -values inside of any node – don’t swap values like we did for Checkpoint 1. -Then, write pseudo-code to reverse this list, just changing the pointers as you diagrammed above. You may -use helper variables (of type Node*) but no other data structures or variables. Remember that when we -directly manipulate homemade linked lists we don’t use iterators. - -Finally, read the starter code of checkpoint 3: [checkpoint3.cpp](checkpoint3.cpp). Complete the reverse function using your diagram and pseudocode as a guide. Test and debug the code. Add a few additional test cases to the main function to ensure your code works with an empty list, -and lists with one or two values. Also add a test or two of a node chain with something other than ints. -If you have time, write 2 versions of this function, one version should be iterative (using a for or while loop) -and one version should be recursive. - -**Note**: this reverse function takes a pointer as its argument, but we are passing this pointer by reference, because we want to modify this pointer. To understand the concept of passing a pointer by reference, you are recommended to read and run this [example program](reference_to_a_pointer.cpp). - -**To complete this checkpoint**, show a TA or mentor your diagram and your debugged function(s) to -reverse a homemade singly-linked list. diff --git a/labs/06_lists_iterators/checkpoint1.cpp b/labs/06_lists_iterators/checkpoint1.cpp deleted file mode 100644 index e7b9ea4..0000000 --- a/labs/06_lists_iterators/checkpoint1.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include -#include - - -template -void print(std::vector &data, const std::string &label) { - std::cout << label << " "; - for (int i = 0; i < data.size(); i++) { - std::cout << " " << data[i]; - } - std::cout << std::endl; -} - - -template -void reverse(std::vector &data) { - - // FILL IN THIS FUNCTION - -} - - -int main() { - std::vector data; - data.push_back(1); - data.push_back(2); - data.push_back(3); - data.push_back(4); - data.push_back(5); - data.push_back(6); - data.push_back(7); - - print(data,"before:"); - reverse(data); - print(data,"after: "); - - std::vector data2; - data2.push_back("apple"); - data2.push_back("banana"); - data2.push_back("carrot"); - data2.push_back("date"); - - print(data2,"before:"); - reverse(data2); - print(data2,"after: "); -} diff --git a/labs/06_lists_iterators/checkpoint3.cpp b/labs/06_lists_iterators/checkpoint3.cpp deleted file mode 100644 index 95ed1bd..0000000 --- a/labs/06_lists_iterators/checkpoint3.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include - - -// Note: It's ok that all the member variables are public for this -// tiny class. -template -class Node { -public: - T value; - Node *ptr; -}; - - - -template -void print(Node *data, const std::string &label) { - std::cout << label; - Node *tmp = data; - while (tmp != NULL) { - std::cout << " " << tmp->value; - tmp = tmp->ptr; - } - std::cout << std::endl; -} - - - -template -void reverse(Node* &input) { - - // FILL IN THIS FUNCTION - -} - - - - -int main() { - - // manually create a linked list of nodes with 4 elements - Node* my_list = new Node; - my_list->value = 1; - my_list->ptr = new Node; - my_list->ptr->value = 2; - my_list->ptr->ptr = new Node; - my_list->ptr->ptr->value = 3; - my_list->ptr->ptr->ptr = new Node; - my_list->ptr->ptr->ptr->value = 4; - my_list->ptr->ptr->ptr->ptr = NULL; - - - print(my_list,"my_list before"); - reverse(my_list); - print(my_list,"my_list after "); - - - // Note: We are not deleting any of the Nodes we created... so this - // program has memory leaks! - -} - -// =========================================================================== diff --git a/labs/06_lists_iterators/reference_to_a_pointer.cpp b/labs/06_lists_iterators/reference_to_a_pointer.cpp deleted file mode 100644 index f7682e7..0000000 --- a/labs/06_lists_iterators/reference_to_a_pointer.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* This example demonstrates the usage of passing a pointer by reference. - * It is needed when you want to modify the pointer. - */ - -#include - -// function to modify the value of a pointer through reference -void modifyPointer(int* & ptr, int& newValue) { - ptr = &newValue; // assign the address of newValue to the pointer -} - -int main() { - int value = 42; - int* ptr = &value; - - // print the original value of the pointer - std::cout << "Original value of pointer: " << *ptr << std::endl; - - int newValue = 100; // new value to assign to the pointer - // pass the pointer by reference to the function, so that we can change the pointer - modifyPointer(ptr, newValue); - - // print the modified value of the pointer - std::cout << "Modified value of pointer: " << *ptr << std::endl; - - return 0; -} - diff --git a/labs/06_lists_iterators/reverse_iterator.cpp b/labs/06_lists_iterators/reverse_iterator.cpp deleted file mode 100644 index 2a73cd6..0000000 --- a/labs/06_lists_iterators/reverse_iterator.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include - -int main(){ - - std::list a; - unsigned int i; - for ( i=1; i<10; ++i ){ - a.push_back( i*i ); - } - - std::list::reverse_iterator ri; - for( ri = a.rbegin(); ri != a.rend(); ++ri ){ - std::cout << *ri << std::endl; - } - - return 0; -} diff --git a/labs/list_implementation/README.md b/labs/list_implementation/README.md new file mode 100644 index 0000000..381335b --- /dev/null +++ b/labs/list_implementation/README.md @@ -0,0 +1,77 @@ +# Lab 7 — List Implementation + +## Checkpoint 1 +*estimate: 20-30 minutes* + +The implementation of the dslist class in [dslist.h](dslist.h) is incomplete. In particular, the class is missing the destroy_list +private member function that is used by the destructor and the clear member function. The provided test +case in [checkpoint1.cpp](checkpoint1.cpp) works “fine”, so what’s the problem? + +Before we fix the problem, let’s use Dr. Memory and/or Valgrind to look at the details more carefully. +You should use the memory debugging tools both on your local machine and by submitting the files to +the homework server. Study the memory debugger output carefully. The output should match your +understanding of the problems caused by the missing destroy_list implementation. Ask a TA if you +have any questions. + +Now write and debug the destroy_list function and then re-run the memory debugger (both locally and on +the submission server) to show that the memory problems have been fixed. Also finish the implementation +of the push_front, pop_front, and pop_back functions. + +**To complete this checkpoint**, show a TA the implementation and memory debugger output before and +after writing destroy_list. + +## Checkpoint 2: +*estimate: 20-30 minutes* + +The PushBack() and the PrintList() function are used in [checkpoint2.cpp](checkpoint2.cpp), but their definitions are missing, please complete these two functions and make sure the program runs and produces the following output. + +```console +$ g++ checkpoint2.cpp +$ ./a.out +Linked List of NodeA nodes: 1 -> 2 -> 3 -> 4 -> 5 -> nullptr +Linked List of NodeB nodes: 1 -> 1.41421 -> 1.73205 -> 2 -> 2.23607 -> nullptr +``` + +**Note**: Hardcoding the PrintList() function to just print the above two messages is strictly prohibited. Also, your functions must be templated functions. + +**To complete this checkpoint**, show a TA the implementation and the output of your program. + +## Checkpoint 3: Merge Two Lists. +*estimate: 30-40 minutes* + +Given two doubly-linked lists: linked list A and linked list B, and both linked lists are sorted. Data in linked list A is sorted in an ascending order. Data in linked list B is also sorted in an ascending order. Merge these two lists such that data in the merged list is still sorted in an ascending order. + +More specifically, complete the mergeLists() function in [checkpoint3.cpp](checkpoint3.cpp), such that the program prints the following output. + +```console +$ g++ checkpoint3.cpp +$ ./a.out +1 3 5 7 9 +2 4 6 8 10 +1 2 3 4 5 6 7 8 9 10 +10 9 8 7 6 5 4 3 2 1 +``` + +**To complete this checkpoint**, explain to a TA your implementation and show the output of your program. + + + + diff --git a/labs/list_implementation/checkpoint1.cpp b/labs/list_implementation/checkpoint1.cpp new file mode 100644 index 0000000..3e3a35f --- /dev/null +++ b/labs/list_implementation/checkpoint1.cpp @@ -0,0 +1,73 @@ +#include +#include +#include + +#include "dslist.h" + +int main() { + + // ======================================= + // CHECKPOINT 1 + + // create a list of the sqrt of the first 10 integers + dslist 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::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; +} + diff --git a/labs/list_implementation/checkpoint2.cpp b/labs/list_implementation/checkpoint2.cpp new file mode 100644 index 0000000..e3631d8 --- /dev/null +++ b/labs/list_implementation/checkpoint2.cpp @@ -0,0 +1,82 @@ +/* This program attempts to create two linked list, one linked list consists of nodes of class NodeA type, + * the other linked list consists of nodes of class NodeB type, but we would like to use the same + * PushBack() function to append nodes to the end of these two linked lists, and use the same PrintList() function + * to print the elements in both lists, and thus we need to make the PushBack() and the PrintList() templated functions. + */ + +#include +#include + +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; +} diff --git a/labs/list_implementation/checkpoint3.cpp b/labs/list_implementation/checkpoint3.cpp new file mode 100644 index 0000000..6474b38 --- /dev/null +++ b/labs/list_implementation/checkpoint3.cpp @@ -0,0 +1,94 @@ +#include + +template +class Node { +public: + T value; + Node* next; + Node* 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 +Node* mergeLists(Node* head_A, Node* head_B) { +} + +int main() { + // create 5 nodes and link them to form a linked list, this is linked list A. + Node* head_A = new Node(1); + Node* second_A = new Node(3); + Node* third_A = new Node(5); + Node* fourth_A = new Node(7); + Node* fifth_A = new Node(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* 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* head_B = new Node(2); + Node* second_B = new Node(4); + Node* third_B = new Node(6); + Node* fourth_B = new Node(8); + Node* fifth_B = new Node(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* head_C; + Node* 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; +} diff --git a/labs/list_implementation/dslist.h b/labs/list_implementation/dslist.h new file mode 100644 index 0000000..c59ef55 --- /dev/null +++ b/labs/list_implementation/dslist.h @@ -0,0 +1,268 @@ +#ifndef dslist_h_ +#define dslist_h_ +// A simplified implementation of the STL list container class, +// including the iterator, but not the const_iterators. Three +// separate classes are defined: a Node class, an iterator class, and +// the actual list class. The underlying list is doubly-linked, but +// there is no dummy head node and the list is not circular. +#include + +// ----------------------------------------------------------------- +// NODE CLASS +template +class Node { +public: + Node() : next_(NULL), prev_(NULL) {} + Node(const T& v) : value_(v), next_(NULL), prev_(NULL) {} + + // REPRESENTATION + T value_; + Node* next_; + Node* prev_; +}; + +// A "forward declaration" of this class is needed +template class dslist; + +// ----------------------------------------------------------------- +// LIST ITERATOR +template +class list_iterator { +public: + // default constructor, copy constructor, assignment operator, & destructor + list_iterator(Node* 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& operator++() { // pre-increment, e.g., ++iter + ptr_ = ptr_->next_; + return *this; + } + list_iterator operator++(int) { // post-increment, e.g., iter++ + list_iterator temp(*this); + ptr_ = ptr_->next_; + return temp; + } + list_iterator& operator--() { // pre-decrement, e.g., --iter + ptr_ = ptr_->prev_; + return *this; + } + list_iterator operator--(int) { // post-decrement, e.g., iter-- + list_iterator temp(*this); + ptr_ = ptr_->prev_; + return temp; + } + // the dslist class needs access to the private ptr_ member variable + friend class dslist; + + // Comparions operators are straightforward + bool operator==(const list_iterator& r) const { + return ptr_ == r.ptr_; } + bool operator!=(const list_iterator& r) const { + return ptr_ != r.ptr_; } + +private: + // REPRESENTATION + Node* ptr_; // ptr to node in the list +}; + +// ----------------------------------------------------------------- +// LIST CLASS DECLARATION +// Note that it explicitly maintains the size of the list. +template +class dslist { +public: + // default constructor, copy constructor, assignment operator, & destructor + dslist() : head_(NULL), tail_(NULL), size_(0) {} + dslist(const dslist& old) { copy_list(old); } + dslist& operator= (const dslist& old); + ~dslist() { destroy_list(); } + + typedef list_iterator 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& old); + void destroy_list(); + + //REPRESENTATION + Node* head_; + Node* tail_; + unsigned int size_; +}; + + // ----------------------------------------------------------------- +// LIST CLASS IMPLEMENTATION +template +dslist& dslist::operator= (const dslist& old) { + // check for self-assignment + if (&old != this) { + destroy_list(); + copy_list(old); + } + return *this; +} + +template +void dslist::push_front(const T& v) { + + + + + +} + +template +void dslist::pop_front() { + + + + +} + +template +void dslist::push_back(const T& v) { + Node* newp = new Node(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 +void dslist::pop_back() { + + + + + +} + +// do these lists look the same (length & contents)? +template +bool operator== (dslist& left, dslist& right) { + if (left.size() != right.size()) return false; + typename dslist::iterator left_itr = left.begin(); + typename dslist::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 +bool operator!= (dslist& left, dslist& right){ return !(left==right); } + template +typename dslist::iterator dslist::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 +typename dslist::iterator dslist::insert(iterator itr, const T& v) { + ++size_ ; + Node* p = new Node(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 +void dslist::copy_list(const dslist& 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(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* old_p = old.head_->next_; + // copy the remainder of the old list, one node at a time + while (old_p) { + tail_->next_ = new Node(old_p->value_); + tail_->next_->prev_ = tail_; + tail_ = tail_->next_; + old_p = old_p->next_; + } +} + +template +void dslist::destroy_list() { + + + + + + + +} + +#endif diff --git a/labs/maps/README.md b/labs/maps/README.md new file mode 100644 index 0000000..dcb41f8 --- /dev/null +++ b/labs/maps/README.md @@ -0,0 +1,50 @@ +# 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. diff --git a/labs/maps/phonebook.cpp b/labs/maps/phonebook.cpp new file mode 100644 index 0000000..60991fb --- /dev/null +++ b/labs/maps/phonebook.cpp @@ -0,0 +1,34 @@ +// A simple "caller ID" program + +#include +#include +#include +using namespace std; + +// add a number, name pair to the phonebook +void add(vector &phonebook, int number, string const& name) { + phonebook[number] = name; +} + +// given a phone number, determine who is calling +void identify(const vector & 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 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); +} diff --git a/labs/recursion/README.md b/labs/recursion/README.md new file mode 100644 index 0000000..5396cd8 --- /dev/null +++ b/labs/recursion/README.md @@ -0,0 +1,158 @@ +# 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. +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 with +the line number in your source code. + +```console +b +``` + +gdb will confirm with a message like this: +Breakpoint 2 at 0xwhatever: file start.cpp, line +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. is the +breakpoint number with the incorrect line number. + +```console +delete +``` + +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. diff --git a/labs/recursion/grid1.txt b/labs/recursion/grid1.txt new file mode 100644 index 0000000..46fcad3 --- /dev/null +++ b/labs/recursion/grid1.txt @@ -0,0 +1,7 @@ +1 0 +2 1 2 3 +3 3 3 4 +5 5 + +0 0 +4 4 diff --git a/labs/recursion/grid2.txt b/labs/recursion/grid2.txt new file mode 100644 index 0000000..22f1824 --- /dev/null +++ b/labs/recursion/grid2.txt @@ -0,0 +1,11 @@ +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 diff --git a/labs/recursion/grid3.txt b/labs/recursion/grid3.txt new file mode 100644 index 0000000..74ca962 --- /dev/null +++ b/labs/recursion/grid3.txt @@ -0,0 +1,28 @@ +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 + diff --git a/labs/recursion/grid4.txt b/labs/recursion/grid4.txt new file mode 100644 index 0000000..4b27116 --- /dev/null +++ b/labs/recursion/grid4.txt @@ -0,0 +1,41 @@ +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 + diff --git a/labs/recursion/start.cpp b/labs/recursion/start.cpp new file mode 100644 index 0000000..856009f --- /dev/null +++ b/labs/recursion/start.cpp @@ -0,0 +1,130 @@ +// Starting code for Checkpoints 2 and 3. This includes +// functions to read the grid and to output it. + +#include +#include +#include +#include + + +// 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 >& 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 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 one_row_of_ys(max_y+1, GRID_CLEAR); + std::vector > 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::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 > & 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; + 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; +} + diff --git a/labs/stacks_and_queues/README.md b/labs/stacks_and_queues/README.md new file mode 100644 index 0000000..a605384 --- /dev/null +++ b/labs/stacks_and_queues/README.md @@ -0,0 +1,75 @@ +# 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 +``` + +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 +``` + +Do not change the *main* function. Do not change the *levelOrderTraversal* function, except this line: + +```cpp +std::queue 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 +#include +``` + +However, you can include the STL list library like this: + +```cpp +#include +``` + +Still, do not change the *main* function, and do not change the *levelOrderTraversal* function, except this line: + +```cpp +std::queue 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. diff --git a/labs/stacks_and_queues/levelOrder.cpp b/labs/stacks_and_queues/levelOrder.cpp new file mode 100644 index 0000000..60285a5 --- /dev/null +++ b/labs/stacks_and_queues/levelOrder.cpp @@ -0,0 +1,110 @@ +#include +#include +#include + +// 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> levelOrderTraversal(TreeNode* root) { + std::vector> result; + + if (root == nullptr) { + return result; + } + + std::queue myQueue; + myQueue.push(root); + + while (!myQueue.empty()) { + int size = myQueue.size(); + std::vector 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; +} + diff --git a/labs/trees_I/README.md b/labs/trees_I/README.md new file mode 100644 index 0000000..1f91a9b --- /dev/null +++ b/labs/trees_I/README.md @@ -0,0 +1,57 @@ +# 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. diff --git a/labs/trees_I/ds_set.h b/labs/trees_I/ds_set.h new file mode 100644 index 0000000..fcf82cf --- /dev/null +++ b/labs/trees_I/ds_set.h @@ -0,0 +1,155 @@ +// 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 +#include + +// ------------------------------------------------------------------- +// TREE NODE CLASS +template +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 ds_set; + +// ------------------------------------------------------------------- +// TREE NODE ITERATOR CLASS +template +class tree_iterator { +public: + tree_iterator() : ptr_(NULL) {} + tree_iterator(TreeNode* 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* ptr_; +}; + +// ------------------------------------------------------------------- +// DS SET CLASS +template +class ds_set { +public: + ds_set() : root_(NULL), size_(0) {} + ds_set(const ds_set& old) : size_(old.size_) { + root_ = this->copy_tree(old.root_); } + ~ds_set() { this->destroy_tree(root_); root_ = NULL; } + ds_set& operator=(const ds_set& old) { + if (&old != this) { + this->destroy_tree(root_); + root_ = this->copy_tree(old.root_); + size_ = old.size_; + } + return *this; + } + + typedef tree_iterator iterator; + + int size() const { return size_; } + bool operator==(const ds_set& 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& 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* p = root_; + while (p->left) p = p->left; + return iterator(p); + } + iterator end() const { return iterator(NULL); } + +private: + // REPRESENTATION + TreeNode* root_; + int size_; + + // PRIVATE HELPER FUNCTIONS + TreeNode* copy_tree(TreeNode* old_root) { + // Implemented in Lab 10 + + + + + + + + + + + } + + void destroy_tree(TreeNode* p) { /* Implemented in Lecture 18 */ } + + iterator find(const T& key_value, TreeNode* 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 insert(const T& key_value, TreeNode*& p) { + if (!p) { + p = new TreeNode(key_value); + this->size_++; + return std::pair(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(p), false); + } + + int erase(T const& key_value, TreeNode* &p) { /* Implemented in Lecture 19 or 20 */ } + + void print_in_order(std::ostream& ostr, const TreeNode* 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* p, int depth) const { + if (p) { + print_as_sideways_tree(ostr, p->right, depth+1); + for (int i=0; ivalue << "\n"; + print_as_sideways_tree(ostr, p->left, depth+1); + } + } +}; + +#endif diff --git a/labs/trees_I/test_ds_set.cpp b/labs/trees_I/test_ds_set.cpp new file mode 100644 index 0000000..589cff7 --- /dev/null +++ b/labs/trees_I/test_ds_set.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +#include "ds_set.h" + +int main() { + + // build a set + ds_set set1; + + std::pair< ds_set::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::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 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; +}