《C++程序设计实践》实验十三
一、课内实验题(共5小题,100分)
题型得分 100
【描述】
矩阵是数学里的一种抽象对象,可以用C++提供的静态数组来表示矩阵,其大小在编译时就已经确定,在运行时无法修改,而且不检查下标是否越界。可以利用教材本章提供的向量类Vector,用向量的方式实现矩阵,用一个指针向量来表示矩阵,其中的每个指针又各指向一个向量,用它们来表示矩阵的行向量。矩阵的逻辑结构如下图所示。
组合向量类Vector,声明并实现一个Matrix类模板,表示矩阵。Matrix类模板包括:
Vector类型的私有数据成员rows,存放矩阵元素。
构造函数,将矩阵的行、列大小设置为给定的参数。
构造函数,将矩阵的行、列大小设置为给定的参数,给矩阵元素赋相同的初始值。
重载下标运算符[]。
访问器函数getRows,用于获取矩阵行数。
访问器函数getColumns,用于获取矩阵列数。
Matrix类模板如下:
template
class Matrix {
public:
Matrix(int row, int column);
Matrix(int row, int column, const T &value);
Vector &operator[](int index);
Vector operator[](int index) const;
int getRows() const;
int getColumns() const;
private:
Vector<Vector*> rows; // 存放矩阵元素
};
以普通函数的形式重载运算符函数,实现矩阵乘法。
template
Matrix operator(const Matrix &lhs, const Matrix &rhs);
printMatrix函数输出矩阵的值。
template
void printMatrix(const Matrix &m);
【输入】
输入3×3矩阵a和矩阵b。
【输出】
矩阵a乘以矩阵b的结果。每个元素输出宽度为4。
【输入示例】
1 1 7
7 5 6
9 6 1
6 1 6
4 1 5
1 5 1
【输出示例】
17 37 18
68 42 73
79 20 85
【来源】
《程序设计基础——以C++为例》第9章实验1。
(20分)
我的答案:
template
class Matrix {
public:
Matrix(int row, int column);
Matrix(int row, int column, const T& value);
Vector& operator[](int index);
Vector operator[](int index) const;
int getRows() const;
int getColumns() const;
private:
Vector<Vector*> rows; // 存放矩阵元素
};
template
Matrix::Matrix(int row, int column) : rows(row) {
for (int i = 0; i < row; i++) {
rows[i] = new Vector(column);
}
}
template
Matrix::Matrix(int row, int column, const T& value) :rows(row) {
for (int i = 0; i < row; i++) {
rows[i] = new Vector(column, value);
}
}
template
Vector& Matrix::operator[](int index) {
return *rows[index];
}
template
Vector Matrix::operator[](int index) const {
return *rows[index];
}
template
int Matrix::getRows() const{
return rows.getSize();
}
template
int Matrix::getColumns() const{
return rows[0]->getSize();
}
template
Matrix operator*(const Matrix& lhs, const Matrix& rhs) {
Matrix res(lhs.getRows(), rhs.getColumns(), 0);
for (int i = 0; i < lhs.getRows(); i++) {
for (int j = 0; j < rhs.getColumns(); j++) {
for (int k = 0; k < lhs.getColumns(); k++) {
res[i][j] += lhs[i][k] * rhs[k][j];
}
}
}
return res;
}
template
void printMatrix(const Matrix& m) {
for (int i = 0; i < m.getRows(); i++) {
for (int j = 0; j < m.getColumns(); j++) {
cout << setw(4) << setfill(’ ') << m[i][j];
}
}
}
题目得分 20
【描述】
给定一个链表,链表的每个节点只含有一个int型元素和Node*指针,该链表共有十个节点,输出链表元素值为奇数的项。部分代码已经写好,请补全output函数即可。
【输入】
输入10个整数。
【输出】
输出奇数值,以空格隔开。
【输入示例】
1 3 4 5 6 7 8 10 11 15
【输出示例】
1 3 5 7 11 15
(20分)
我的答案:
Node *tmpNode = head;
while(tmpNode){
if(tmpNode->value%2){
cout << tmpNode->value << " ";
}
tmpNode = tmpNode->next;
}
题目得分 20
【描述】
本章教材组合向量类Vector实现了一个Stack类模板。也可以用链表的方式实现栈。利用本章教材提供的链表类LinkedList,组合链表类LinkedList来实现一个新的Stack类模板。
template
class Stack {
public:
Stack();
void clear();
bool isEmpty() const;
void push(const T &value);
T pop();
T peek() const;
private:
LinkedList data;
};
【输入】
一系列正整数并入栈,输入-1表示结束,-1不是输入的数据的一部分。
【输出】
输出栈中所有的整数,每个整数后面跟一个空格以与后面的整数区分。
【输入示例】
1 3 5 2 -1
【输出示例】
2 5 3 1
【来源】
《程序设计基础——以C++为例》第9章实验4。
(20分)
我的答案:
template
class Stack {
public:
bool isEmpty() const;
void push(const T& value);
T pop();
T peak() const;
private:
LinkedList data;
};
template
bool Stack::isEmpty() const {
return data.isEmpty();
}
template
void Stack::push(const T& value) {
data.addLast(value);
}
template
T Stack::pop() {
T temp = data.getLast();
data.removeLast();
return temp;
}
template
T Stack::peak() const {
return data.getLast();
}
题目得分 20
【描述】
栈可以应用于算术表达式求值。这里对算术表达式做如下简化:运算符为+、-、、/、%;操作数为单数字(0~9)非负整数。
例如:
(2+5)3-8/3
上述形式的算术表达式也称中缀表达式,因为每个运算符出现在它的两个操作数之间。编译器在求算术表达式值时,往往将中缀表达式转换为后缀表达式。后缀表达式也称逆波兰表达式,由波兰数学家Jan Lukasiewicz发明,指运算符出现在操作数后面的不含括号的算术表达式。
中缀表达式:a+bc,其后缀表达式为:a b c * +。因为的优先级比+高,所以先计算b*c,即b c *;+的操作数是a和b c *,即a b c * +。
中缀表达式:(a+b)*c,其后缀表达式为:a b + c 。先计算圆括号中a+b,即a b +;的操作数是a b +和c,即a b + c 。
中缀表达式:(ab+c)/d+e,其后缀表达式为:a b * c + d / e +。先计算圆括号中ab+c,即a b * c +;/的操作数是a b * c +和d,即a b * c + d /;+的操作数是a b * c + d /和e,即a b * c + d / e +。
后缀表达式求值使用一个存放操作数的栈。求值过程顺序扫描后缀表达式,每次遇到操作数就将它入栈;遇到运算符时,就从栈中弹出两个操作数进行计算,并将结果入栈。到扫描结束,留在栈顶的操作数就是所求算术表达式的值。
例如:
4 2 7 +
如下图一所示,顺序读取操作数4、2、7,并入栈。如下图二所示,读取运算符,操作数7、2出栈,计算27,结果为14并入栈。如下图三所示,读取运算符+,操作数14、4出栈,计算4+14,结果为18并入栈。扫描后缀表达式后,栈中惟一元素即为最终结果。
本章教材组合向量类Vector实现了一个Stack类模板。请组合栈类Stack,声明并实现一个PostfixEvaluation类,求后缀表达式的值。PostfixEvaluation类包括:
string类型的私有数据成员postfixExpression,存放后缀表达式。
Stack类型的私有数据成员operandStack,存放操作数(单数字非负整数)。
私有成员函数getOperands,连续从栈顶出栈两个操作数。
私有成员函数calculate,求出栈的两个操作数的计算结果,并将结果入栈。
私有成员函数isOperator,确定输入的运算符是有效运算符(+、-、*、/、%)
有参构造函数。新建一个后缀表达式对象。
访问器函数getPostfixExpression,获取后缀表达式。
更改器函数setPostfixExpression,设置新的后缀表达式。
成员函数evaluate,计算并返回后缀表达式的值。
PostfixEvaluation类如下:
class PostfixEvaluation {
public:
PostfixEvaluation(const string&postfixExpression); // 构造函数
stringgetPostfixExpression() const; // 获取后缀表达式
void setPostfixExpression(const string&postfixExpression); // 设置后缀表达式
int evaluate(); // 计算并返回后缀表达式值
private:
string postfixExpression; // 存放要计算的后缀表达式
Stack operandStack; // 存放操作数
void getOperands(int &left, int&right); // 操作数出栈
int calculate(int left, int right, char op) const; // 求操作数运算值
bool isOperator(char ch) const; // 是否是运算符
};
【输入】
输入一个后缀表达式。
【输出】
输出后缀表达式的值。
【输入示例1】
4 2 7 * +
【输出示例1】
18
【输入示例2】
8 4 0 % +
【输出示例2】
Divisor cannot be zero!
【输入示例3】
2 3 ^ 4 +
【输出示例3】
Illegal input!
【输入示例4】
4 2 / *
【输出示例4】
Too many operators!
【输入示例5】
1 2 3 +
【输出示例5】
Too many operands!
【来源】
《程序设计基础——以C++为例》第9章实验2。
(20分)
我的答案:
class PostfixEvaluation {
public:
PostfixEvaluation(const string& postfixExpression); // 构造函数
string getPostfixExpression() const; // 获取后缀表达式
void setPostfixExpression(const string& postfixExpression); // 设置后缀表达式
int evaluate(); // 计算并返回后缀表达式值
private:
string postfixExpression; // 存放要计算的后缀表达式
Stack operandStack; // 存放操作数
void getOperands(int& left, int& right); // 操作数出栈
int calculate(int left, int right, char op) const; // 求操作数运算值
bool isOperator(char ch) const; // 是否是运算符
};
void PostfixEvaluation::getOperands(int& left, int& right) {
right = operandStack.pop();
if (operandStack.isEmpty()) {
throw runtime_error(“Too many operators!”);
}
left = operandStack.pop();
}
int PostfixEvaluation::calculate(int left, int right, char op) const {
int value;
if (op == ‘+’)
value = left + right;
else if (op == ‘-’)
value = left - right;
else if (op == ‘')
value = left * right;
else if (op == ‘/’)
value = left / right;
else if (op == ‘%’)
value = left % right;
else
value = INT_MIN;
return value;
}
bool PostfixEvaluation::isOperator(char ch) const {
return ch == ‘+’ || ch == ‘-’ || ch == '’ || ch == ‘/’ || ch == ‘%’;
}
PostfixEvaluation::PostfixEvaluation(const string& postfixExpression) {
this->postfixExpression = postfixExpression;
}
string PostfixEvaluation::getPostfixExpression() const {
return postfixExpression;
}
void PostfixEvaluation::setPostfixExpression(const string& postfixExpression) {
this->postfixExpression = postfixExpression;
}
int PostfixEvaluation::evaluate() {
char op;
int left, right, value;
int flag = 0;
for (int i = 0; i < postfixExpression.length(); i++) {
if (postfixExpression[i]=='^') {
throw runtime_error("Illegal input!");
}
}
for (int i = 0; i < postfixExpression.length(); i++) {
if (!isOperator(postfixExpression[i]) && postfixExpression[i] != ' ') {
operandStack.push(postfixExpression[i] - '0');
}
else if (isOperator(postfixExpression[i])) {
op = postfixExpression[i];
getOperands(left, right);
if (right == 0 && (op == '%' || op == '/')) {
throw runtime_error("Divisor cannot be zero!");
}
value = calculate(left, right, op);
operandStack.push(value);
}
}
operandStack.pop();
if (!operandStack.isEmpty()) {
throw runtime_error("Too many operands!");
}
return value;
}
题目得分 20
【描述】
编写程序,输入若干个正整数,输入-1时输入结束,可以简化修改本章教材提供的链表类LinkedList,用单向链表组织输入的正整数。要求链表按照结点中整数值的大小从大到小排序,不包括最后标识结束的-1。输出单向链表。
【输入】
一系列正整数,输入-1表示结束,-1不是输入的数据的一部分。
【输出】
按照结点中整数值的大小从大到小输出所有的整数,每个整数后面跟一个空格以与后面的整数区分,最后的整数后面没有空格。
【输入示例】
1 3 5 2 -1
【输出示例】
5 3 2 1
【来源】
《程序设计基础——以C++为例》第9章实验3。
(20分)
我的答案:
template
class Node {
public:
Node() {
next = NULL;
}
Node(const T& value) {
this->value = value;
next = NULL;
}
T value;
Node* next;
};
template
class LinkedList {
public:
LinkedList();
void addFirst(const T& value);
void addLast(const T& value);
void add(const T& value);
void removeFirst();
void removeLast();
void removeAt(int index);
T getFirst() const;
T getLast() const;
void clear();
bool isEmpty() const;
int getSize() const;
void print() const;
private:
Node* head, * tail;
int size;
};
template
LinkedList::LinkedList() {
head = tail = NULL;
size = 0;
}
template
void LinkedList::addFirst(const T& value) {
Node* temp = new Node(value);
temp->next = head;
head = temp;
++size;
if (tail == NULL)
tail = head;
}
template
void LinkedList::addLast(const T& value) {
if (tail == NULL)
head = tail = new Node(value);
else {
tail->next = new Node(value);
tail = tail->next;
}
++size;
}
template
void LinkedList::removeFirst() {
if (head == NULL)
throw runtime_error(“空链表!”);
else {
Node* temp = head;
head = head->next;
if (head == NULL)
tail = NULL;
delete temp;
–size;
}
}
template
void LinkedList::removeLast() {
if (tail == NULL)
throw runtime_error(“空链表!”);
else if (head == tail) {
Node* temp = head;
head = tail = NULL;
size = 0;
delete temp;
}
else {
Node* current = head;
while (current->next != tail)
current = current->next;
Node* temp = tail;
tail = current;
tail->next = NULL;
delete temp;
–size;
}
}
template
void LinkedList::removeAt(int index) {
if (index < 0 || index >= size)
throw runtime_error(“下标越界!”);
else if (index == 0)
return removeFirst();
else if (index == size - 1)
return removeLast();
else {
Node* previous = head;
for (int i = 1; i < index; ++i)
previous = previous->next;
Node* current = previous->next;
previous->next = current->next;
delete current;
–size;
}
}
template
T LinkedList::getFirst() const {
if (head == NULL)
throw runtime_error(“空链表!”);
else
return head->value;
}
template
T LinkedList::getLast() const {
if (tail == NULL)
throw runtime_error(“空链表!”);
else
return tail->value;
}
template
void LinkedList::clear() {
while (head != NULL) {
Node* temp = head;
head = head->next;
delete temp;
}
tail = NULL;
}
template
bool LinkedList::isEmpty() const {
return head == NULL;
}
template
int LinkedList::getSize() const {
return size;
}
template
void LinkedList::print() const {
Node* current = head;
while (current != NULL) {
cout << current->value << " ";
current = current->next;
}
cout << endl;
}
template
void LinkedList::add(const T& value) {
Node* temp = new Node(value);
Node* current = head;
Node* previous = NULL;
while (current != NULL && current->value > value) {
previous = current;
current = current->next;
}
if (current == head) {
head = temp;
}
else {
previous->next = temp;
}
temp->next = current;
}
题目得分 20