Compare commits

...

8 Commits

Author SHA1 Message Date
JamesFlare1212
4054554203 add hw3 solution 2025-02-10 20:49:28 -05:00
Jidong Xiao
2c07e10ea4 adding iterator implementation code 2025-02-10 20:49:28 -05:00
Jidong Xiao
716d955a73 adding the iterator implementation code 2025-02-10 20:49:28 -05:00
Jidong Xiao
5d67ac91fc move iterator examples to lec 9 2025-02-10 20:49:28 -05:00
Jidong Xiao
841c1874da remove enroll example 2025-02-10 20:49:28 -05:00
Jidong Xiao
97dc4c3881 move iterators examples here 2025-02-10 20:49:28 -05:00
Jidong Xiao
c715f539fa renaming 2025-02-10 20:49:28 -05:00
Jidong Xiao
4b2597520c hw4 2025-02-10 20:49:28 -05:00
52 changed files with 834 additions and 170 deletions

View File

@@ -50,6 +50,7 @@
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"typeinfo": "cpp"
"typeinfo": "cpp",
"cassert": "cpp"
}
}

292
hws/matrix_class/Matrix.cpp Normal file
View File

@@ -0,0 +1,292 @@
#include "Matrix.h"
#include <iostream>
#include <iomanip>
//helper function to allocate memory for a matrix of size r x c and fill it with "fill"
void Matrix::allocateMemory(unsigned int r, unsigned int c, double fill) {
if(r == 0 || c == 0) {
rows = 0;
cols = 0;
data = nullptr;
return;
}
rows = r;
cols = c;
data = new double*[rows];
for (unsigned int i = 0; i < rows; i++) {
data[i] = new double[cols];
for (unsigned int j = 0; j < cols; j++) {
data[i][j] = fill;
}
}
}
//helper function to deallocate memory
void Matrix::deallocateMemory() {
if(data != nullptr) {
for (unsigned int i = 0; i < rows; i++) {
delete [] data[i];
}
delete [] data;
data = nullptr;
}
rows = 0;
cols = 0;
}
//default constructor: creates an empty 0 x 0 matrix
Matrix::Matrix() : rows(0), cols(0), data(nullptr) { }
//parameterized constructor
Matrix::Matrix(unsigned int r, unsigned int c, double fill) : rows(0), cols(0), data(nullptr) {
allocateMemory(r, c, fill);
}
//copy constructor
Matrix::Matrix(const Matrix& other) : rows(0), cols(0), data(nullptr) {
allocateMemory(other.rows, other.cols, 0.0);
for (unsigned int i = 0; i < rows; i++) {
for (unsigned int j = 0; j < cols; j++) {
data[i][j] = other.data[i][j];
}
}
}
//destructor
Matrix::~Matrix() {
deallocateMemory();
}
//assignment operator
Matrix& Matrix::operator=(const Matrix& other) {
if (this == &other)
return *this;
deallocateMemory();
allocateMemory(other.rows, other.cols, 0.0);
for (unsigned int i = 0; i < rows; i++) {
for (unsigned int j = 0; j < cols; j++) {
data[i][j] = other.data[i][j];
}
}
return *this;
}
//returns the number of rows
unsigned int Matrix::num_rows() const {
return rows;
}
//returns the number of columns
unsigned int Matrix::num_cols() const {
return cols;
}
//clears the matrix by deallocating its memory
void Matrix::clear() {
deallocateMemory();
}
bool Matrix::get(unsigned int row, unsigned int col, double &value) const {
if(row >= rows || col >= cols) {
return false;
}
value = data[row][col];
return true;
}
//modifier
bool Matrix::set(unsigned int row, unsigned int col, double value) {
if(row >= rows || col >= cols) {
return false;
}
data[row][col] = value;
return true;
}
//equality operator
bool Matrix::operator==(const Matrix& other) const {
//two matrices are equal if dimensions match
//and every element is equal within a small epsilon
if(rows != other.rows || cols != other.cols)
return false;
for (unsigned int i = 0; i < rows; i++) {
for (unsigned int j = 0; j < cols; j++) {
if (fabs(data[i][j] - other.data[i][j]) > 1e-10)
return false;
}
}
return true;
}
bool Matrix::operator!=(const Matrix& other) const {
return !(*this == other);
}
//multiplies every element by the given coefficient
void Matrix::multiply_by_coefficient(double coeff) {
for (unsigned int i = 0; i < rows; i++) {
for (unsigned int j = 0; j < cols; j++) {
data[i][j] *= coeff;
}
}
}
//swaps two rows of the matrix (by swapping the row pointers)
bool Matrix::swap_row(unsigned int row1, unsigned int row2) {
if(row1 >= rows || row2 >= rows) {
return false;
}
double* temp = data[row1];
data[row1] = data[row2];
data[row2] = temp;
return true;
}
//transposes the matrix in place
void Matrix::transpose() {
if(rows == 0 || cols == 0)
return;
unsigned int newRows = cols;
unsigned int newCols = rows;
double** newData = new double*[newRows];
for (unsigned int i = 0; i < newRows; i++) {
newData[i] = new double[newCols];
for (unsigned int j = 0; j < newCols; j++) {
newData[i][j] = data[j][i];
}
}
deallocateMemory();
rows = newRows;
cols = newCols;
data = newData;
}
//adds another matrix to this one element-wise
bool Matrix::add(const Matrix& other) {
if(rows != other.rows || cols != other.cols)
return false;
for (unsigned int i = 0; i < rows; i++) {
for (unsigned int j = 0; j < cols; j++) {
data[i][j] += other.data[i][j];
}
}
return true;
}
//subtracts another matrix from this one element-wise
bool Matrix::subtract(const Matrix& other) {
if(rows != other.rows || cols != other.cols)
return false;
for (unsigned int i = 0; i < rows; i++) {
for (unsigned int j = 0; j < cols; j++) {
data[i][j] -= other.data[i][j];
}
}
return true;
}
//returns a dynamically allocated copy of the specified row
//caller must delete[] the returned array
double* Matrix::get_row(unsigned int row) const {
if(row >= rows)
return NULL;
double* rowArray = new double[cols];
for (unsigned int j = 0; j < cols; j++) {
rowArray[j] = data[row][j];
}
return rowArray;
}
//returns a dynamically allocated copy of the specified column
//caller must delete[] the returned array
double* Matrix::get_col(unsigned int col) const {
if(col >= cols)
return NULL;
double* colArray = new double[rows];
for (unsigned int i = 0; i < rows; i++) {
colArray[i] = data[i][col];
}
return colArray;
}
//divides the matrix into four quadrants and returns a pointer to an array of four matrices
//each quadrant size = ceil(rows/2) x ceil(cols/2)
Matrix* Matrix::quarter() const {
if (rows == 0 || cols == 0) {
return nullptr;
}
// Determine quadrant size so that all four quadrants are identical.
// For overlapping, use ceil for both dimensions.
unsigned int q_rows = (rows % 2 == 0) ? (rows / 2) : (rows / 2 + 1);
unsigned int q_cols = (cols % 2 == 0) ? (cols / 2) : (cols / 2 + 1);
// For an overlapping quarter, the top quadrants start at row 0,
// the bottom quadrants start at floor(rows/2), similarly for columns.
unsigned int start_row_bottom = rows / 2;
unsigned int start_col_right = cols / 2;
Matrix* quadrants = new Matrix[4]{
Matrix(q_rows, q_cols, 0.0), // Upper Left
Matrix(q_rows, q_cols, 0.0), // Upper Right
Matrix(q_rows, q_cols, 0.0), // Lower Left
Matrix(q_rows, q_cols, 0.0) // Lower Right
};
// Fill Upper Left quadrant from original (starting at (0,0)).
for (unsigned int i = 0; i < q_rows; i++) {
for (unsigned int j = 0; j < q_cols; j++) {
double value;
if (i < rows && j < cols && get(i, j, value))
quadrants[0].set(i, j, value);
}
}
// Fill Upper Right quadrant from original (starting at (0, start_col_right)).
for (unsigned int i = 0; i < q_rows; i++) {
for (unsigned int j = 0; j < q_cols; j++) {
double value;
if (i < rows && (j + start_col_right) < cols && get(i, j + start_col_right, value))
quadrants[1].set(i, j, value);
}
}
// Fill Lower Left quadrant from original (starting at (start_row_bottom, 0)).
for (unsigned int i = 0; i < q_rows; i++) {
for (unsigned int j = 0; j < q_cols; j++) {
double value;
if ((i + start_row_bottom) < rows && j < cols && get(i + start_row_bottom, j, value))
quadrants[2].set(i, j, value);
}
}
// Fill Lower Right quadrant from original (starting at (start_row_bottom, start_col_right)).
for (unsigned int i = 0; i < q_rows; i++) {
for (unsigned int j = 0; j < q_cols; j++) {
double value;
if ((i + start_row_bottom) < rows && (j + start_col_right) < cols &&
get(i + start_row_bottom, j + start_col_right, value))
quadrants[3].set(i, j, value);
}
}
return quadrants;
}
//overloaded output operator to print the matrix
// 4 x 4 matrix:
// [ 14 14 14 14
// 14 14 14 14
// 14 9 14 14
// 14 14 14 13 ]
std::ostream& operator<<(std::ostream& out, const Matrix& m) {
out << m.rows << " x " << m.cols << " matrix:" << std::endl;
out << "[ ";
for (unsigned int i = 0; i < m.rows; i++) {
for (unsigned int j = 0; j < m.cols; j++) {
out << m.data[i][j];
if(j < m.cols - 1)
out << " ";
}
if(i < m.rows - 1)
out << std::endl << " ";
}
out << " ]";
return out;
}

76
hws/matrix_class/Matrix.h Normal file
View File

@@ -0,0 +1,76 @@
#ifndef MATRIX_H
#define MATRIX_H
#include <iostream>
#include <cmath>
class Matrix {
private:
unsigned int rows;
unsigned int cols;
double** data;
//allocate memory and fill with a given value
void allocateMemory(unsigned int r, unsigned int c, double fill);
//deallocate memory
void deallocateMemory();
public:
////Constructors
Matrix();
Matrix(unsigned int r, unsigned int c, double fill);
//copy constructor
Matrix(const Matrix& other);
////Destructor
~Matrix();
////Accessors
Matrix& operator=(const Matrix& other);
unsigned int num_rows() const;
unsigned int num_cols() const;
//deallocates memory and resets rows/cols to 0)
void clear();
//if (row, col) is within bounds, stores the element in `value` and returns true;
//otherwise, returns false
bool get(unsigned int row, unsigned int col, double &value) const;
////Modifier
//if (row, col) is within bounds, sets the element to `value` and returns true;
//otherwise, returns false.
bool set(unsigned int row, unsigned int col, double value);
//multiplies every element in the matrix by the provided coefficient
void multiply_by_coefficient(double coeff);
//swaps two rows of the matrix. Returns true if both indices are valid,
//false otherwise
bool swap_row(unsigned int row1, unsigned int row2);
//transposes the matrix in place (switches rows and columns).
void transpose();
//adds another matrix to this one element-wise (if dimensions match) and returns true;
//otherwise, returns false.
bool add(const Matrix& other);
//subtracts another matrix from this one element-wise (if dimensions match)
//and returns true; otherwise, returns false.
bool subtract(const Matrix& other);
//returns a new dynamically allocated array (of size num_cols)
//containing the elements in the specified row.
double* get_row(unsigned int row) const;
//returns a new dynamically allocated array (of size num_rows)
//containing the elements in the specified column.
double* get_col(unsigned int col) const;
//divides the matrix into four quadrants and returns a pointer to an array of
//4 Matrix objects: Upper Left, Upper Right, Lower Left, Lower Right.
//each quadrant is of size ceil(rows/2) x ceil(cols/2)
//and the quadrants overlap when the dimensions are odd.
Matrix* quarter() const;
////Operators
//equality operator: two matrices are equal if they have the same dimensions and
//every corresponding element differs by no more than a small epsilon.
bool operator==(const Matrix& other) const;
//inequality operator
bool operator!=(const Matrix& other) const;
//overloaded output operator for printing the matrix
friend std::ostream& operator<<(std::ostream& out, const Matrix& m);
};
#endif

View File

@@ -1,7 +1,7 @@
HOMEWORK 3: MATRIX CLASS
NAME: < insert name >
NAME: Jinshan Zhou
COLLABORATORS AND OTHER RESOURCES:
@@ -10,13 +10,13 @@ List the names of everyone you talked to about this assignment
LMS, etc.), and all of the resources (books, online reference
material, etc.) you consulted in completing this assignment.
< insert collaborators / resources >
cmath fabs method. Lecture notes about ** pointers and destructors.
Remember: Your implementation for this assignment must be done on your
own, as described in "Academic Integrity for Homework" handout.
ESTIMATE OF # OF HOURS SPENT ON THIS ASSIGNMENT: < insert # hours >
ESTIMATE OF # OF HOURS SPENT ON THIS ASSIGNMENT: 14 hr
@@ -27,25 +27,25 @@ number of columns. You should assume that calling new [] or delete []
on an array will take time proportional to the number of elements in
the array.
get
get -> O(1)
set
set -> O(1)
num_rows
num_rows -> O(1)
get_column
get_column -> O(m)
operator<<
operator<< -> O(m*n)
quarter
quarter -> O(m*n)
operator==
operator== -> O(m*n)
operator!=
operator!= -> O(m*n)
swap_rows
swap_rows -> O(1)
rref (provided in matrix_main.cpp)
rref (provided in matrix_main.cpp) -> O(m^2 * n)
@@ -55,6 +55,17 @@ What tools did you use (gdb/lldb/Visual Studio debugger,
Valgrind/Dr. Memory, std::cout & print, etc.)? How did you test the
"corner cases" of your Matrix class design & implementation?
I used gdb inside VSCode to debug my program. I also used the information
from the submitty autograder (Dr. Memory) to help me find bugs in my code.
I basiclly find what's name and function of the methods of Martix class,
and change my code bit by bit to fit the expected output.
MISC. COMMENTS TO GRADER:
(optional, please be concise!)
REFLECTION:
As the pointer adding up, the complexity of the program will increase.
I think it is important to understand where do pointers points to and
delete them when they are not needed anymore. It's quiet tricky and I
got a lot of memory leaks in my code at first. But the error message in
Dr. Memory shows which line caused the problem, so I was able to fix it.

View File

@@ -33,10 +33,8 @@ int main(){
std::cout << "Completed all simple tests." << std::endl;
//Uncomment this to allocate a lot of 100x100 matrices so leaks will be bigger.
/*
BatchTest(100,0.1,100,100,50);
std::cout << "Completed all batch tests." << std::endl;
*/
StudentTest();
std::cout << "Completed all student tests." << std::endl;
@@ -199,10 +197,128 @@ void SimpleTest(){ //well behaved getrow/read after
}
//Write your own test cases here
void StudentTest(){
void StudentTest() {
//Test transpose
Matrix m(2, 3, 0);
m.set(0, 0, 1);
m.set(0, 1, 2);
m.set(0, 2, 3);
m.set(1, 0, 4);
m.set(1, 1, 5);
m.set(1, 2, 6);
m.transpose();
assert(m.num_rows() == 3);
assert(m.num_cols() == 2);
double val;
m.get(0, 0, val);
assert(double_compare(val, 1.0));
m.get(0, 1, val);
assert(double_compare(val, 4.0));
m.get(1, 0, val);
assert(double_compare(val, 2.0));
m.get(2, 1, val);
assert(double_compare(val, 6.0));
//Test quarter with odd dimensions
Matrix q(5, 5, 1);
Matrix* quarters = q.quarter();
assert(quarters != nullptr);
//each quadrant should be 3x3 (ceiling: (5+1)/2 == 3)
assert(quarters[0].num_rows() == 3);
assert(quarters[0].num_cols() == 3);
assert(quarters[1].num_rows() == 3);
assert(quarters[1].num_cols() == 3);
assert(quarters[2].num_rows() == 3);
assert(quarters[2].num_cols() == 3);
assert(quarters[3].num_rows() == 3);
assert(quarters[3].num_cols() == 3);
//verify that the quadrants hold the expected values.
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
quarters[0].get(i, j, val);
assert(double_compare(val, 1.0));
quarters[1].get(i, j, val);
assert(double_compare(val, 1.0));
quarters[2].get(i, j, val);
assert(double_compare(val, 1.0));
quarters[3].get(i, j, val);
assert(double_compare(val, 1.0));
}
}
delete[] quarters;
//Test add and subtract
Matrix a(2, 2, 2);
Matrix b(2, 2, 3);
assert(a.add(b)); // Now a is all 5's.
double v;
a.get(0, 0, v);
assert(double_compare(v, 5.0));
assert(a.subtract(b)); // Now a is back to all 2's.
a.get(0, 0, v);
assert(double_compare(v, 2.0));
//Test multiply by coefficient
a.multiply_by_coefficient(2.5); //Now a is all 5's (2*2.5).
a.get(0, 0, v);
assert(double_compare(v, 5.0));
//Test get_row
double* row = a.get_row(0);
assert(row != nullptr);
assert(double_compare(row[0], 5.0));
assert(double_compare(row[1], 5.0));
delete[] row;
// Test get_col
double* col = a.get_col(1);
assert(col != nullptr);
assert(double_compare(col[0], 5.0));
assert(double_compare(col[1], 5.0));
delete[] col;
//Test clear
a.clear();
assert(a.num_rows() == 0 && a.num_cols() == 0);
//Test swap_row
Matrix s(3, 2, 0);
s.set(0, 0, 1); s.set(0, 1, 2);
s.set(1, 0, 3); s.set(1, 1, 4);
s.set(2, 0, 5); s.set(2, 1, 6);
//swap row 0 and row 2
assert(s.swap_row(0, 2));
s.get(0, 0, val);
assert(double_compare(val, 5.0));
s.get(0, 1, val);
assert(double_compare(val, 6.0));
s.get(2, 0, val);
assert(double_compare(val, 1.0));
s.get(2, 1, val);
assert(double_compare(val, 2.0));
//invalid swap should return false.
assert(!s.swap_row(0, 3));
//Test copy constructor and assignment operator.
Matrix orig(2, 3, 7);
Matrix copy(orig); // Using copy constructor.
Matrix assign;
assign = orig; // Using assignment operator.
//change orig to ensure copy and assign remain unchanged.
orig.set(0, 0, 10);
orig.get(0, 0, val);
assert(double_compare(val, 10.0));
copy.get(0, 0, val);
assert(double_compare(val, 7.0));
assign.get(0, 0, val);
assert(double_compare(val, 7.0));
//Test out-of-bound get and set
assert(!orig.get(5, 5, val));
assert(!orig.set(5, 5, 3.0));
}
////////////////Utility functions//////////////////////
/* Function that quickly populates a rows x cols matrix with values from

View File

@@ -222,11 +222,11 @@ Of course you can change std::cout to a file stream so as to print the these sym
In this assignment, **you must use std::list to store the businesses which match with what the user is searching for, and you must implement at least one class**.
Use good coding style when you design and implement your program. Organize your program into functions: dont put all the code in main! Be sure to read the [Homework Policies](https://www.cs.rpi.edu/academics/courses/spring24/csci1200/homework_policies.php) as you put the finishing touches on your solution. Be sure to make up new test cases to fully debug your program and dont forget
Use good coding style when you design and implement your program. Organize your program into functions: dont put all the code in main! Be sure to read the [Homework Policies](https://www.cs.rpi.edu/academics/courses/spring25/csci1200/homework_policies.php) as you put the finishing touches on your solution. Be sure to make up new test cases to fully debug your program and dont forget
to comment your code! Use the provided template [README.txt](./README.txt) file for notes you want the grader to read.
You must do this assignment on your own, as described in the [Collaboration Policy & Academic Integrity](https://www.cs.rpi.edu/academics/courses/spring24/csci1200/academic_integrity.php) page. If you did discuss the problem or error messages, etc. with anyone, please list their names in your README.txt file.
You must do this assignment on your own, as described in the [Collaboration Policy & Academic Integrity](https://www.cs.rpi.edu/academics/courses/spring25/csci1200/academic_integrity.php) page. If you did discuss the problem or error messages, etc. with anyone, please list their names in your README.txt file.
**Due Date**: 02/15/2024, Thursday, 10pm.
**Due Date**: 02/13/2025, Thursday, 10pm.
## Rubric
@@ -244,7 +244,6 @@ You must do this assignment on your own, as described in the [Collaboration Poli
- Function bodies containing more than one statement are placed in the .h file. (okay for templated classes) (-2)
- Functions are not well documented or are poorly commented, in either the .h or the .cpp file. (-1)
- At least one function is excessively long (i.e., more than 200 lines). (-1)
- Overly cramped. (-1)
- Poor file organization: Puts more than one class in a file (okay for very small helper classes) (-1)
- Poor choice of variable names: non-descriptive names (e.g. 'vec', 'str', 'var'), single-letter variable names (except single loop counter), etc. (-2)
- DATA REPRESENTATION (7 pts)

View File

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View File

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View File

Before

Width:  |  Height:  |  Size: 353 KiB

After

Width:  |  Height:  |  Size: 353 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -65,12 +65,8 @@ vector<string>::iterator p;
vector<string>::const_iterator q;
```
defines two (uninitialized) iterator variables. An iterator is used to modify the elements of the container. A const_iterator is used when you want to traverse the elements of the container without modifying them.
- The dereference operator is used to access the value stored at an element of the container. The code:
```cpp
p = enrolled.begin();
*p = "012312";
```
changes the first entry in the *enrolled* vector.
- The dereference operator is used to access the value stored at an element of the container.
- We can use arrow and the dot operator like this:
```cpp
@@ -85,13 +81,139 @@ next or previous element of any container.
- Iterators can be compared using the == and != operators.
- Iterators can be assigned, just like any other variable.
- Vector iterators have several additional operations:
- Integer values may be added to them or subtracted from them. This leads to statements like
enrolled.erase(enrolled.begin() + 5);
- Integer values may be added to them or subtracted from them.
- Vector iterators may be compared using operators like <, <=, etc.
- For most containers (other than vectors), these “random access” iterator operations are not legal and
therefore prevented by the compiler. The reasons will become clear as we look at their implementations.
## 9.6 A New Datatype: The list Standard Library Container Class
## 9.6 Iterator Operation Examples
- An iterator type is defined by each STL container class. For example:
```cpp
std::vector<double>::iterator v_itr;
std::list<std::string>::iterator l_itr;
std::string::iterator s_itr;
```
- An iterator is assigned to a specific location in a container. For example:
```cpp
v_itr = vec.begin() + i; // i-th location in a vector
l_itr = lst.begin(); // first entry in a list
s_itr = str.begin(); // first char of a string
```
*Note*: We can add an integer to vector and string iterators, but not to list iterators.
- The contents of the specific entry referred to by an iterator are accessed using the * dereference operator:
In the first and third lines, *v itr and *l itr are l-values. In the second, *s_itr is an r-value.
```cpp
*v_itr = 3.14;
cout << *s_itr << endl;
*l_itr = "Hello";
```
- Stepping through a container, either forward and backward, is done using increment (++) and decrement (--)
operators:
```cpp
++itr; itr++; --itr; itr--;
```
These operations move the iterator to the next and previous locations in the vector, list, or string. The
operations do not change the contents of container!
- Finally, we can change the container that a specific iterator is attached to as long as the types match.
Thus, if v and w are both std::vector<double>, then the code:
```cpp
v_itr = v.begin();
*v_itr = 3.14; // changes 1st entry in v
v_itr = w.begin() + 2;
*v_itr = 2.78; // changes 3rd entry in w
```
works fine because v_itr is a std::vector&lt;double&gt;::iterator, but if *a* is a std::vector&lt;std::string&gt; then
```cpp
v_itr = a.begin();
```
is a syntax error because of a type clash!
## 9.7 Additional Iterator Operations for Vector (& String) Iterators
- Initialization at a random spot in the vector:
```cpp
v_itr = v.begin() + i;
```
Jumping around inside the vector through addition and subtraction of location counts:
```cpp
v_itr = v_itr + 5;
```
moves p 5 locations further in the vector. These operations are constant time, O(1) for vectors.
- These operations are not allowed for list iterators (and most other iterators, for that matter) because of the
way the corresponding containers are built. These operations would be linear time, O(n), for lists, where n is
the number of slots jumped forward/backward. Thus, they are not provided by STL for lists.
- Students are often confused by the difference between iterators and indices for vectors. Consider the following
declarations:
```cpp
std::vector<double> a(10, 2.5);
std::vector<double>::iterator p = a.begin() + 5;
unsigned int i=5;
```
- Iterator p refers to location 5 in vector a. The value stored there is directly accessed through the * operator:
```cpp
*p = 6.0;
cout << *p << endl;
```
- The above code has changed the contents of vector a. Heres the equivalent code using subscripting:
```cpp
a[i] = 6.0;
cout << a[i] << endl;
```
- Heres another common confusion:
```cpp
std::list<int> lst; lst.push_back(100); lst.push_back(200);
lst.push_back(300); lst.push_back(400); lst.push_back(500);
```
```cpp
std::list<int>::iterator itr,itr2,itr3;
itr = lst.begin();// itr is pointing at the 100
++itr; // itr is now pointing at 200
*itr += 1; // 200 becomes 201
// itr += 1; // does not compile! can't advance list iterator like this
```
```cpp
itr = lst.end(); // itr is pointing "one past the last legal value" of lst
itr--; // itr is now pointing at 500;
itr2 = itr--; // itr is now pointing at 400, itr2 is still pointing at 500
itr3 = --itr; // itr is now pointing at 300, itr3 is also pointing at 300
```
```cpp
// dangerous: decrementing the begin iterator is "undefined behavior"
// (similarly, incrementing the end iterator is also undefined)
// it may seem to work, but break later on this machine or on another machine!
itr = lst.begin();
itr--; // dangerous!
itr++;
assert (*itr == 100); // might seem ok... but rewrite the code to avoid this!
```
## 9.8 A New Datatype: The list Standard Library Container Class
- Lists are our second standard-library container class. (Vectors were the first.) Both lists & vectors store
sequential data that can shrink or grow.
@@ -265,7 +387,7 @@ int main() {
}
```
## 9.7 Erase & Iterators
## 9.9 Erase & Iterators
STL lists and vectors each have a special member function called erase. In particular, given list of ints s,
consider the example:
@@ -293,14 +415,14 @@ p = s.erase(p);
Even though the erase function has the same syntax for vectors and for list, the vector version is O(n), whereas
the list version is O(1). Play this [animation](https://jidongxiao.github.io/CSCI1200-DataStructures/animations/lists/iterator/index.html) to see why.
## 9.8 Insert
## 9.10 Insert
- Similarly, there is an insert function for STL lists that takes an iterator and a value and adds a link in the
chain with the new value immediately before the item pointed to by the iterator.
- The call returns an iterator that points to the newly added element. Variants on the basic insert function are
also defined.
## 9.9 Leetcode Exercises
## 9.11 Leetcode Exercises
- [Leetcode problem 27: Remove Element](https://leetcode.com/problems/remove-element/). Solution: [p27_removeelement.cpp](../../leetcode/p27_removeelement.cpp)
- [Leetcode problem 58: Length of Last Word](https://leetcode.com/problems/length-of-last-word/). Solution: [p58_lengthoflastword.cpp](../../leetcode/p58_lengthoflastword.cpp)

View File

@@ -1,147 +1,12 @@
# Lecture 10 --- Vector Iterators & Linked Lists
# Lecture 10 --- Iterator Implementation & Linked Lists
<!--- Keep an eye for an update on Student Excuse Absence Policy which would go in effect from HW4
* Please remember incorporating the new rule may result in
+ grading process gets delayed
+ you will receive grades late
+ will affect your grade inquiry time
+ may affect when you receive the solutions or see the actual rubric
+ if that HW was due before exam then be ready to receive feedback on that after exams
* More detailed info soon-->
- Review of iterators
- Iterator Implementation (in Vectors)
- Building our own basic linked lists:
Stepping through a list
Basic operations
## 10.1 Review: Iterators and Iterator Operations
- An iterator type is defined by each STL container class. For example:
```cpp
std::vector<double>::iterator v_itr;
std::list<std::string>::iterator l_itr;
std::string::iterator s_itr;
```
- An iterator is assigned to a specific location in a container. For example:
```cpp
v_itr = vec.begin() + i; // i-th location in a vector
l_itr = lst.begin(); // first entry in a list
s_itr = str.begin(); // first char of a string
```
*Note*: We can add an integer to vector and string iterators, but not to list iterators.
- The contents of the specific entry referred to by an iterator are accessed using the * dereference operator:
In the first and third lines, *v itr and *l itr are l-values. In the second, *s_itr is an r-value.
```cpp
*v_itr = 3.14;
cout << *s_itr << endl;
*l_itr = "Hello";
```
- Stepping through a container, either forward and backward, is done using increment (++) and decrement (--)
operators:
```cpp
++itr; itr++; --itr; itr--;
```
These operations move the iterator to the next and previous locations in the vector, list, or string. The
operations do not change the contents of container!
- Finally, we can change the container that a specific iterator is attached to as long as the types match.
Thus, if v and w are both std::vector<double>, then the code:
```cpp
v_itr = v.begin();
*v_itr = 3.14; // changes 1st entry in v
v_itr = w.begin() + 2;
*v_itr = 2.78; // changes 3rd entry in w
```
works fine because v_itr is a std::vector&lt;double&gt;::iterator, but if *a* is a std::vector&lt;std::string&gt; then
```cpp
v_itr = a.begin();
```
is a syntax error because of a type clash!
## 10.2 Additional Iterator Operations for Vector (& String) Iterators
- Initialization at a random spot in the vector:
```cpp
v_itr = v.begin() + i;
```
Jumping around inside the vector through addition and subtraction of location counts:
```cpp
v_itr = v_itr + 5;
```
moves p 5 locations further in the vector. These operations are constant time, O(1) for vectors.
- These operations are not allowed for list iterators (and most other iterators, for that matter) because of the
way the corresponding containers are built. These operations would be linear time, O(n), for lists, where n is
the number of slots jumped forward/backward. Thus, they are not provided by STL for lists.
- Students are often confused by the difference between iterators and indices for vectors. Consider the following
declarations:
```cpp
std::vector<double> a(10, 2.5);
std::vector<double>::iterator p = a.begin() + 5;
unsigned int i=5;
```
- Iterator p refers to location 5 in vector a. The value stored there is directly accessed through the * operator:
```cpp
*p = 6.0;
cout << *p << endl;
```
- The above code has changed the contents of vector a. Heres the equivalent code using subscripting:
```cpp
a[i] = 6.0;
cout << a[i] << endl;
```
- Heres another common confusion:
```cpp
std::list<int> lst; lst.push_back(100); lst.push_back(200);
lst.push_back(300); lst.push_back(400); lst.push_back(500);
```
```cpp
std::list<int>::iterator itr,itr2,itr3;
itr = lst.begin();// itr is pointing at the 100
++itr; // itr is now pointing at 200
*itr += 1; // 200 becomes 201
// itr += 1; // does not compile! can't advance list iterator like this
```
```cpp
itr = lst.end(); // itr is pointing "one past the last legal value" of lst
itr--; // itr is now pointing at 500;
itr2 = itr--; // itr is now pointing at 400, itr2 is still pointing at 500
itr3 = --itr; // itr is now pointing at 300, itr3 is also pointing at 300
```
```cpp
// dangerous: decrementing the begin iterator is "undefined behavior"
// (similarly, incrementing the end iterator is also undefined)
// it may seem to work, but break later on this machine or on another machine!
itr = lst.begin();
itr--; // dangerous!
itr++;
assert (*itr == 100); // might seem ok... but rewrite the code to avoid this!
```
### Exercise
What is the output of this program?
```cpp
@@ -170,6 +35,10 @@ int main(){
}
```
## 10.2 Iterator Implementation
Here we extend our implemention of vector so as to include iterators. Review the written code here: [vec.h](vec.h) and [vec_main.cpp](vec_main.cpp).
## 10.3 Working towards our own version of the STL list
- Our discussion of how the STL list&lt;T&gt; is implemented has been intuitive: it is a “chain” of objects.

View File

@@ -0,0 +1,145 @@
template <class T>
class vec {
public:
// default constructor
vec(){
m_size = 0;
m_capacity = 2;
m_data = new T[m_capacity];
// std::cout << "calling default constructor" << std::endl;
}
// other constructor
vec(int number, const T& value){
m_size = number;
m_capacity = m_size * 2;
m_data = new T[m_capacity];
for(int i=0; i<m_size; ++i){
m_data[i] = value;
}
}
// copy constructor
vec(const vec& other){
m_size = other.m_size;
m_capacity = other.m_capacity;
m_data = new T[m_capacity];
for(int i=0; i<m_size; ++i){
m_data[i] = other.m_data[i];
}
}
// assignment operator
// a = b = c; a = (b = c);
// a = b;
vec& operator=(const vec& other){
// use this if statement here so as to avoid self-assignment.
if(this != &other){
m_size = other.m_size;
m_capacity = other.m_capacity;
m_data = new T[m_capacity];
for(int i=0; i<m_size; ++i){
m_data[i] = other.m_data[i];
}
}
return *this;
}
// destructor
~vec(){
// std::cout << "calling destructor" << std::endl;
delete [] m_data;
}
int size();
/*int size(){
return m_size;
}*/
T& operator[](int index){
return m_data[index];
}
void push_back(const T& element){
// if we have space
if(m_size < m_capacity){
m_data[m_size] = element;
}else{
// if we don't have space
m_capacity = m_capacity* 2;
T* new_data = new T[m_capacity];
// copy the existing elements to new location
for(int i=0; i<m_size; ++i){
new_data[i] = m_data[i];
}
new_data[m_size] = element;
delete [] m_data;
m_data = new_data;
}
m_size++;
}
void pop_back(){
m_size--;
}
// nested class
class iterator {
private:
T* ptr;
public:
// constructor
iterator(T* p){
ptr = p;
}
// dereference operator
T& operator*(){
return *ptr;
}
T* operator->(){
return ptr;
}
// pre-increment
iterator& operator++(){
++ptr;
return *this;
}
// post-increment
// special syntax here, we must write the int keyword here in the post-increment function.
iterator operator++(int){
iterator temp = *this;
++ptr;
return temp;
}
bool operator!=(const iterator& other){
return (ptr != other.ptr);
}
};
iterator begin();
//iterator begin(){
// we want to have an iterator pointing to the beginning of the vec container.
// return iterator(m_data);
//}
iterator end(){
// we want to have an iterator pointing to the end.
return iterator(m_data + m_size);
}
private:
T* m_data;
int m_capacity; // whole capacity
int m_size; // current size
};
// below we demonstrate that member functions can also be written outside of the templated class definition:
// why we need typename here: The compiler does not automatically assume that vec<T>::iterator is a type.
// Instead, it first assumes that anything after :: is a member variable or function, not a type.
// To explicitly tell the compiler that vec<T>::iterator is a type, we must use the typename keyword:
template <class T>
typename vec<T>::iterator vec<T>::begin(){
return vec<T>::iterator(m_data);
}
template <class T>
int vec<T>::size(){
return m_size;
}

View File

@@ -0,0 +1,33 @@
#include <iostream>
//#include <vector>
#include "vec.h"
int main(){
vec<std::string> teams;
teams.push_back("rpi");
teams.push_back("wpi");
teams.push_back("yale");
teams.push_back("brown");
teams.push_back("cornell");
teams.push_back("colgate");
teams.push_back("miami");
teams.push_back("colorado");
teams.push_back("harvard");
std::cout << "==== teams ==== " << std::endl;
int size = teams.size();
for(int i=0; i<size; ++i){
std::cout << teams[i] << std::endl;
}
// this line calls the other constructor.
vec<double> v(4, 0.0);
v[0] = 13.1; v[2] = 3.14;
vec<double>::iterator itr = v.begin();
std::cout << "the first element of v is: " << *itr << std::endl;
std::cout << "printing elements in v: " << std::endl;
while(itr != v.end()){
std::cout << "print " << *itr << std::endl;
++itr;
}
}