Compare commits
8 Commits
48dd0a80eb
...
4054554203
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4054554203 | ||
|
|
2c07e10ea4 | ||
|
|
716d955a73 | ||
|
|
5d67ac91fc | ||
|
|
841c1874da | ||
|
|
97dc4c3881 | ||
|
|
c715f539fa | ||
|
|
4b2597520c |
3
.vscode/settings.json
vendored
@@ -50,6 +50,7 @@
|
|||||||
"sstream": "cpp",
|
"sstream": "cpp",
|
||||||
"stdexcept": "cpp",
|
"stdexcept": "cpp",
|
||||||
"streambuf": "cpp",
|
"streambuf": "cpp",
|
||||||
"typeinfo": "cpp"
|
"typeinfo": "cpp",
|
||||||
|
"cassert": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
292
hws/matrix_class/Matrix.cpp
Normal 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
@@ -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
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
HOMEWORK 3: MATRIX CLASS
|
HOMEWORK 3: MATRIX CLASS
|
||||||
|
|
||||||
|
|
||||||
NAME: < insert name >
|
NAME: Jinshan Zhou
|
||||||
|
|
||||||
|
|
||||||
COLLABORATORS AND OTHER RESOURCES:
|
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
|
LMS, etc.), and all of the resources (books, online reference
|
||||||
material, etc.) you consulted in completing this assignment.
|
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
|
Remember: Your implementation for this assignment must be done on your
|
||||||
own, as described in "Academic Integrity for Homework" handout.
|
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
|
on an array will take time proportional to the number of elements in
|
||||||
the array.
|
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
|
Valgrind/Dr. Memory, std::cout & print, etc.)? How did you test the
|
||||||
"corner cases" of your Matrix class design & implementation?
|
"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:
|
MISC. COMMENTS TO GRADER:
|
||||||
(optional, please be concise!)
|
(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.
|
||||||
@@ -33,10 +33,8 @@ int main(){
|
|||||||
std::cout << "Completed all simple tests." << std::endl;
|
std::cout << "Completed all simple tests." << std::endl;
|
||||||
|
|
||||||
//Uncomment this to allocate a lot of 100x100 matrices so leaks will be bigger.
|
//Uncomment this to allocate a lot of 100x100 matrices so leaks will be bigger.
|
||||||
/*
|
|
||||||
BatchTest(100,0.1,100,100,50);
|
BatchTest(100,0.1,100,100,50);
|
||||||
std::cout << "Completed all batch tests." << std::endl;
|
std::cout << "Completed all batch tests." << std::endl;
|
||||||
*/
|
|
||||||
|
|
||||||
StudentTest();
|
StudentTest();
|
||||||
std::cout << "Completed all student tests." << std::endl;
|
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
|
//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//////////////////////
|
////////////////Utility functions//////////////////////
|
||||||
|
|
||||||
/* Function that quickly populates a rows x cols matrix with values from
|
/* Function that quickly populates a rows x cols matrix with values from
|
||||||
|
|||||||
@@ -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**.
|
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: don’t 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 don’t forget
|
Use good coding style when you design and implement your program. Organize your program into functions: don’t 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 don’t forget
|
||||||
to comment your code! Use the provided template [README.txt](./README.txt) file for notes you want the grader to read.
|
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
|
## 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)
|
- 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)
|
- 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)
|
- 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 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)
|
- 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)
|
- DATA REPRESENTATION (7 pts)
|
||||||
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 353 KiB After Width: | Height: | Size: 353 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
@@ -65,12 +65,8 @@ vector<string>::iterator p;
|
|||||||
vector<string>::const_iterator q;
|
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.
|
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
|
- The dereference operator is used to access the value stored at an element of the container.
|
||||||
p = enrolled.begin();
|
|
||||||
*p = "012312";
|
|
||||||
```
|
|
||||||
changes the first entry in the *enrolled* vector.
|
|
||||||
|
|
||||||
- We can use arrow and the dot operator like this:
|
- We can use arrow and the dot operator like this:
|
||||||
```cpp
|
```cpp
|
||||||
@@ -85,13 +81,139 @@ next or previous element of any container.
|
|||||||
- Iterators can be compared using the == and != operators.
|
- Iterators can be compared using the == and != operators.
|
||||||
- Iterators can be assigned, just like any other variable.
|
- Iterators can be assigned, just like any other variable.
|
||||||
- Vector iterators have several additional operations:
|
- Vector iterators have several additional operations:
|
||||||
- Integer values may be added to them or subtracted from them. This leads to statements like
|
- Integer values may be added to them or subtracted from them.
|
||||||
enrolled.erase(enrolled.begin() + 5);
|
|
||||||
- Vector iterators may be compared using operators like <, <=, etc.
|
- Vector iterators may be compared using operators like <, <=, etc.
|
||||||
- For most containers (other than vectors), these “random access” iterator operations are not legal and
|
- 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.
|
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<double>::iterator, but if *a* is a std::vector<std::string> 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. Here’s the equivalent code using subscripting:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
a[i] = 6.0;
|
||||||
|
cout << a[i] << endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
- Here’s 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
|
- Lists are our second standard-library container class. (Vectors were the first.) Both lists & vectors store
|
||||||
sequential data that can shrink or grow.
|
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,
|
STL lists and vectors each have a special member function called erase. In particular, given list of ints s,
|
||||||
consider the example:
|
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
|
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.
|
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
|
- 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.
|
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
|
- The call returns an iterator that points to the newly added element. Variants on the basic insert function are
|
||||||
also defined.
|
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 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)
|
- [Leetcode problem 58: Length of Last Word](https://leetcode.com/problems/length-of-last-word/). Solution: [p58_lengthoflastword.cpp](../../leetcode/p58_lengthoflastword.cpp)
|
||||||
|
|||||||
@@ -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
|
- Iterator Implementation (in Vectors)
|
||||||
* 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
|
|
||||||
- Building our own basic linked lists:
|
- Building our own basic linked lists:
|
||||||
– Stepping through a list
|
– Stepping through a list
|
||||||
– Basic operations
|
– Basic operations
|
||||||
|
|
||||||
## 10.1 Review: Iterators and Iterator 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<double>::iterator, but if *a* is a std::vector<std::string> 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. Here’s the equivalent code using subscripting:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
a[i] = 6.0;
|
|
||||||
cout << a[i] << endl;
|
|
||||||
```
|
|
||||||
|
|
||||||
- Here’s 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?
|
What is the output of this program?
|
||||||
|
|
||||||
```cpp
|
```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
|
## 10.3 Working towards our own version of the STL list
|
||||||
|
|
||||||
- Our discussion of how the STL list<T> is implemented has been intuitive: it is a “chain” of objects.
|
- Our discussion of how the STL list<T> is implemented has been intuitive: it is a “chain” of objects.
|
||||||
|
|||||||
145
lectures/10_linked_lists/vec.h
Normal 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;
|
||||||
|
}
|
||||||
33
lectures/10_linked_lists/vec_main.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||