renaming labs

This commit is contained in:
Jidong Xiao
2025-01-07 17:16:10 -05:00
parent ad8bdb01ee
commit 190e7fd7f4
18 changed files with 1071 additions and 0 deletions

218
labs/vectors/README.md Normal file
View File

@@ -0,0 +1,218 @@
# 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<std::string> &a, int b) {
int answer = 0;
for (int i = 0; i < a.size(); i+=b) {
answer++;
}
return answer;
}
```
```cpp
void foo2 (const std::vector<int> &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<int> foo3 (const std::vector<int> &a, const std::string &b) {
return std::vector<int>(b.size(),a.size());
}
```
```cpp
int foo3 (const std::vector<std::string> &a, const std::string& b) {
int ret = 0;
for (int i=0; i<a.size(); i++){
ret += (a[i] == b);
}
return ret;
}
```
```cpp
std::vector<int> foo4 (const std::vector<int> &a) {
std::vector<int> 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<int> foo5 (const std::vector<int> &a, int b) {
std::vector<int> ret;
for(int i=0; i<a.size(); i++){
if(a[i] < b){
ret.insert(ret.end(),a[i]);
}
}
return ret;
}
```
### Student 2
```cpp
int foobar (const std::vector<std::string> &a, int b) {
int answer = 0;
for (int i = 0; i < a.size(); i+=b) {
answer++;
}
return answer;
}
```
```cpp
std::vector<int> bar2 (const std::vector<std::string> &a) {
std::vector<int> answer;
for (int i = 0; i < a.size(); i++) {
answer.push_back(a[i].size());
}
return answer;
}
```
```cpp
std::vector<std::string> bar3 (const std::vector<int> &a) {
std::vector<std::string> answer;
for (int i = 0; i < a.size(); i++) {
answer.push_back(std::string(a[i],'+'));
}
return answer;
}
```
```cpp
void bar3 (std::vector<std::string> &a, const std::string &b) {
for (int i = 0; i < a.size(); i++) {
a[i] = b;
}
}
```
```cpp
std::vector<int> bar4 (const std::vector<std::string> &a) {
std::vector<int> 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<int> &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 hasnt 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&lt;T&gt; 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&lt;int&gt; 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&lt;T&gt; class, you have to use the *typename* keyword before the Vec&lt;T&gt;:: scope. For example, if you want to define an iterator named *itr*, you can do it like this:
```console
typename Vec<T>::iterator itr
```
without this *typename* keyword, if you define the iterator *itr* like this:
```console
Vec<T>::iterator itr
```
you will get a compiler error saying:
```console
error: need typename before Vec<T>::iterator because Vec<T> is a dependent scope
```
And the reason that this keyword *typename* is needed, is because without it, the compiler would think that Vec&lt;T&gt;::iterator is a member variable of the Vec&lt;T&gt; class, but this *typename* explicitly tells the compiler that Vec&lt;T&gt;::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.

97
labs/vectors/test_vec.cpp Normal file
View File

@@ -0,0 +1,97 @@
#include <iostream>
#include <cmath>
using namespace std;
#include "vec.h"
int main() {
// ---------------------------------------------------
// initialize v1 with 10 values... the multiples of 5
Vec<int> 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<v1.size(); ++i ) {
cout << " " << v1[i];
}
cout << endl;
// --------------------------------------------------------------------------
// make v2 be a copy of v1, but then overwrite the 2nd half with the 1st half
Vec<int> 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<v1.size(); ++i )
cout << " " << v1[i];
cout << endl;
cout << "Contents of v2 (now palindrome):";
for ( i = 0; i<v2.size(); ++i )
cout << " " << v2[i];
cout << endl;
// ------------------------------------------
// make v3 be a copy of v2, but then clear it
Vec<int> 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<v2.size(); ++i ) {
cout << " " << v2[i];
}
cout << endl;
// --------------
// test push back
cout << "\nNow testing push_back. Adding 3, 6, 9 to v2:\n";
v2.push_back( 3 );
v2.push_back( 6 );
v2.push_back( 9 );
cout << "v2 is now: \n";
for ( i = 0; i<v2.size(); ++i ) {
cout << " " << v2[i];
}
cout << endl;
// -----------
// test resize
v1.resize(20,100);
cout << "\nNow testing resize. Resizing v1 to have 20 elements and v2 to have 2 elements\n";
cout << "v1 is now (should have 100s at the end): \n";
for ( i = 0; i<v1.size(); ++i )
cout << " " << v1[i];
cout << endl;
v2.resize(2,100);
cout << "v2 is now: \n";
for ( i = 0; i<v2.size(); ++i )
cout << " " << v2[i];
cout << endl;
// ------------------------
// test of a vec of doubles
cout << "\nStarting from an empty vector, z, of doubles and doing\n"
<< "5 push_backs\n";
Vec<double> 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;
}

141
labs/vectors/vec.h Normal file
View File

@@ -0,0 +1,141 @@
#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 T> 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<T>& 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 <class T> void Vec<T>::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 <class T> void Vec<T>::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 <class T> Vec<T>& Vec<T>::operator=(const Vec<T>& v) {
if (this != &v) {
delete [] m_data;
this -> copy(v);
}
return *this;
}
// Create the vector as a copy of the given vector.
template <class T> void Vec<T>::copy(const Vec<T>& 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 <class T> void Vec<T>::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<m_size; ++i)
new_data[i] = m_data[i];
// Delete the old array and reset the pointers
delete [] m_data;
m_data = new_data;
}
// Add the value at the last location and increment the bound
m_data[m_size] = val;
++ m_size;
}
// Shift each entry of the array after the iterator. Return the iterator,
// which will have the same value, but point to a different element.
template <class T> typename Vec<T>::iterator Vec<T>::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 <class T> void Vec<T>::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<m_size; ++i)
new_data[i] = m_data[i];
delete [] m_data;
m_data = new_data;
}
// Now fill in the remaining values and assign the final size.
for (int i = m_size; i<n; ++i)
m_data[i] = fill_in_value;
m_size = n;
}
}
#endif