C++习题(三)
题目综述
- 已知一个矩阵,存储在一个二维数组中。将矩阵中和值为最大的那一行元素与首行对换。
- 已知一个方阵,存储在一个二维数组中。用指针访问数组元素的方法,计算方阵中上三角所有元素的和、下三角所有元素的和,以及主对角线上的所有元素和。
- 判断字符串是否是“回文”。所谓“回文”是指顺读和逆读都是一样的串,例如串 12321 和 madam 都是回文。
- 约瑟夫问题: n 个人围成一圈,从 1 开始顺序编号;游戏开始,首先生成一个 [1 , n] 区间内的随机数,从第一个人开始由 1 到 m 循环报数,报到 m 的人退出圈外,问最后留下的那个人原来的序号。
题目详解
1.已知一个矩阵,存储在一个二维数组中。将矩阵中和值为最大的那一行元素与首行对换。
思路:
- 找到 “ 和值最大的行 ”
- 将找到的 “ 最大行 ” 和首行整行互换
分步骤处理:
①找到 “ 和值最大的行 ”
- 先两次遍历来计算每一行的和,然后将求得的和存在一个数组 SumTemp 里
****************** obj是二维数组引用:vector<vector<int>>& obj ******************
//找“和最大”的行,需要进行遍历
//第一次遍历中可以锁定行,第二次遍历就可以针对当前行求和
//创建变量 temp 记录每次外层遍历之后后当前矩阵行的和值
int temp = 0;
//vector数组 SumTemp 用于存放每一行的和值
vector<int> SumTemp;
for (auto i = obj.begin(); i != obj.end(); ++i)
{
//第一次遍历,此时的 i 是迭代器指针, *i 就是当前每一行的数组名(也就是里层的那个vector)
for (auto j = (*i).begin(); j != (*i).end(); ++j)
{
//第二次遍历,此时 *j 就是当前列的数据
//将当前数据记录在 temp 中
temp += (*j);
}
//将所有 temp 放入新的数组 SumTemp 里
SumTemp.push_back(temp);
}
②将找到的 “ 最大行 ” 和首行整行互换
- 用“ 比较 ”的思路来找,先把数组第一个数拿出来交给对照变量,然后遍历数组,将数组中每次遍历到的数和对照组进行比较,如果发现此时遍历出来的数更大,就将对照组换成这个较大的数,同时再设定一变量 MaxIndex 来记录此时较大的那个数在数组的位置 ( 也就是 i )
- 经过整轮遍历之后,MaxIndex 已经存好行和数组 SumTemp 中最大值的位置了,所以现在只需要将原矩阵中首行与 MaxIndex 行互换即可完成任务
//数组SumTemp放置了所有的行和
/*
现考虑用 “ 比较 ” 的思路找到最大值的下标
故设定对照变量 ContrastValue 并用数组第一个数初始化
同时设定变量 MaxIndex 来记录每一轮循环较大的那个数的下标,也用数组第一个位置初始化
*/
int ContrastValue = SumTemp[0];
int MaxIndex = 0;
for (int i = 0; i != SumTemp.size(); ++i)
{
if (SumTemp[i] > ContrastValue)
{
//只要遍历中发现找出来的数比对照变量大,就改变对照变量
int temp = ContrastValue;
ContrastValue = SumTemp[i];
SumTemp[i] = temp;
//同时记录下此时这个较大数的位置
MaxIndex = i;
}
}
//循环后 MaxIndex 的值就是最大行的行数
//现在只要把首行与第 MaxIndex 行互换即可
vector<int> L = obj[0];
obj[0] = obj[MaxIndex];
obj[MaxIndex] = L;
【目标函数的完整代码】
/*1.已知一个矩阵,存储在一个二维数组中。将矩阵中和值为最大的那一行元素与首行对换*/
#include<iostream>
#include<vector>
using namespace std;
/*设计实现的函数*/
//返回值:二维数组
//参数:二维数组
vector<vector<int>> Function(vector<vector<int>>& obj)
{
//创建 temp 记录每次外层遍历之后后当前矩阵行的和值
int temp = 0;
//vector数组 SumTemp 用于存放每一行的和值
vector<int> SumTemp;
//找“和最大”的行,需要进行遍历
//第一次遍历中可以锁定行,第二次遍历就可以针对当前行求和
for (auto i = obj.begin(); i != obj.end(); ++i)
{
//第一次遍历,此时的 i 是迭代器指针, *i 就是当前每一行的数组名(也就是里层的那个vector)
for (auto j = (*i).begin(); j != (*i).end(); ++j)
{
//第二次遍历,此时 *j 就是当前列的数据
//将当前数据记录在 temp 中
temp += (*j);
}
//将所有 temp 放入新的数组 SumTemp 里
SumTemp.push_back(temp);
}
//数组 SumTemp 放置了所有的行和
/*
现考虑用 “ 比较 ” 的思路找到最大值的下标
故设定对照变量 ContrastValue 并用数组第一个数初始化
同时设定变量 MaxIndex 来记录每一轮循环较大的那个数的下标,也用数组第一个位置初始化
*/
int ContrastValue = SumTemp[0];
int MaxIndex = 0;
for (int i = 0; i != SumTemp.size(); ++i)
{
if (SumTemp[i] > ContrastValue)
{
//只要遍历中发现找出来的数比对照变量大,就改变对照变量
int temp = ContrastValue;
ContrastValue = SumTemp[i];
SumTemp[i] = temp;
//同时记录下此时这个较大数的位置
MaxIndex = i;
}
}
//循环后 MaxIndex 的值就是最大行的行数
//现在只要把首行的与第 MaxIndex 行的互换即可
vector<int> L = obj[0];
obj[0] = obj[MaxIndex];
obj[m] = L;
//返回obj
return obj;
}
2.已知一个方阵,存储在一个二维数组中。用指针访问数组元素的方法,计算方阵中上三角所有元素的和、下三角所有元素的和,以及主对角线上的所有元素和。
思路:
- 找到每一行 “ 对角线 ” 的位置 (指针的表示形式)
- 分区域进行元素求和
分步骤处理:
①找到每一行 “ 对角线 ” 的位置 (指针的表示形式)
- 主对角线位置下标表示: Matrix[ i ][ i ] ( i 是一层 for 循环遍历出来的 )
- 主对角线位置指针表示: *( *( Matrix + i ) + i) (内层的 ( Matrix + i ) 表示取第 i 行的数组名,此时还是指针,在括号外加 * 号之后的 *( Matrix + i ) 表示取第 i 行数组,相当于 Matrix[ i ] ,然后再加上 i 后加 * 号就相当于 Matrix[ i ][ i ] )
- vector容器中迭代器表示:*( ( *i ).begin() + Index ),其中vector中的 .begin() 函数能返回迭代器进行循环遍历,而 Index 用来记录当前遍历行数,原理和指针类似,但不可以直接加 i 来进行指针运算
*( ( *i ).begin() + i )
【这里使用 vector 容器编写函数】
②按照各自区域进行元素求和
- 因为三个要求都要先求对角线的位置,故先做对角线和
【求主对角线上的所有元素和的函数的完整代码】
// Diagonal_Sum 函数:求对角线的和
//返回值:主对角线和
//参数:二维数组
int Diagonal_Sum(vector<vector<int>>& obj)
{
int Sum = 0;
//设定变量 Index 来记录行数
int Index = 0;
//找到“主对角线” obj[i][i] 即 *( *( obj + i ) + i ) 即 *( (*i).begin() + Index )
for (auto i = obj.begin(); i != obj.end(); ++i)
{
// *i 就是当前行的vector数组
//而 *((*i).begin() + Index) 就是对角线位置的值
Sum += *((*i).begin() + Index);
//一次循环行数就加1
Index++;
}
//返回主对角线和
return Sum;
}
- 求 上三角阵 / 下三角阵 的和关键在于列元素遍历的 起始位置 和 结束位置
- 对于上三角阵而言,起始位置应为 当前行的对角线元素的后面一个元素 ,即为 ( *i ).begin() + Index + 1 ,结束位置是最后一位元素即为 ( *i ).end()
for ( auto j = ( *i ).begin() + Index + 1 ; j != ( *i ).end() ; ++j )
- 对于下三角阵而言,起始位置应为当前行首元素,即为 ( *i ).begin() ,结束位置是最后一位元素应为 当前行的对角线元素的前一位元素 ,即为 ( *i ).begin() + Index
for ( auto j = ( *i ).begin() ; j != ( *i ).begin() + Index ; ++j )
【求上三角形和下三角形上的所有元素和的函数的完整代码】
// Upper_TriangularMatrix_Sum 函数:求上三角阵的和
//返回值:上三角阵的和
//参数:二维数组
int Upper_TriangularMatrix_Sum(vector<vector<int>>& obj)
{
int Sum = 0;
//设定变量 Index 来记录行数
int Index = 0;
for (auto i = obj.begin(); i != obj.end(); ++i)
{
// *i 就是当前行的vector数组
//而 *((*i).begin() + Index) 就是对角线位置的值
//下一层循环只从对角线后一位元素开始遍历
for (auto j = (*i).begin() + Index + 1; j != (*i).end(); ++j)
{
Sum += (*j);
}
//一次循环行数就加1
Index++;
}
//返回上三角阵和
return Sum;
}
// Lower_TriangularMatrix_Sum 函数:求下三角阵的和
//返回值:下三角阵的和
//参数:二维数组
int Lower_TriangularMatrixSum(vector<vector<int>>& obj)
{
int Sum = 0;
//设定变量 Index 来记录行数
int Index = 0;
for (auto i = obj.begin(); i != obj.end(); ++i)
{
// *i 就是当前行的vector数组
//而 *((*i).begin() + Index) 就是对角线位置的值
//下一层循环只遍历完对角线前一位元素
for (auto j = (*i).begin(); j != (*i).begin() + Index ; ++j)
{
Sum += (*j);
}
//一次循环行数就加1
Index++;
}
//返回下三角阵和
return Sum;
}
3. 判断字符串是否是“回文”。所谓“回文”是指顺读和逆读都是一样的串,例如串 12321 和 madam 都是回文。
【下面是处理回文字符串的原程序】
*****************************【处理回文字符串的原程序】******************************
#include <iostream>
using namespace std;
const int SZ = 100;
int main()
{
char carray[SZ];
int is_palindrome = 1;
cout << "Please input a string .." << endl;
cin.get(carray, SZ);
int len = strlen(carray);
for (int i = 0; i < len/2; i++)
{
if (carray[i] != carray[len - 1 - i])
{
is_palindrome = 0;
break;
}
}
if (is_palindrome)
cout << "The string is a palindrome" << endl;
else
cout << "The string is not a palindrome" << endl;
return 0;
}
要求:重新定义回文为:虑去所有非字母字符(包括空格)后,不考虑字母的大小写,从左向右和从右向左读都相同的词或短语。如“Madam,I’m adam”和“Golf,No Sir, prefer prison flog!”。改写上面程序,用 string 来代替字符数组来完成相同操作。
思路:
- 改用sring来存放数据
- 过滤非字母字符
- 大小写统一
分步骤处理:
①改用sring来存放数据
- 初始化: char carray[SZ] -------------→ string carray
- 字符串输入:cin.get(carray, SZ) ----→ getline(cin,carray)( getline 遇到换行符才会停)
- 获取字符串长度:strlen(carray) -----→ carray.size()
②过滤非字母字符
- 可以通过遍历找到字母字符,交给到新的字符串 temp_carray 中
- 范围选取:
if ( ( carray[ i ] >= ‘a’ && carray[ i ] <= ‘z’ ) || ( carray[ i ] >= ‘A’ && carray[ i ] <= ‘Z’ ) )
- “ 复制 ” 操作:逐个字符写入,同时给定变量 index 记录新字符串的有效长度
① temp_carray[ index ] = carray[ i ]
②if ( index < len) { ++index ; }
③统一大小写字符
- 方法一:引入转换函数 transform
transform(temp_carray.begin(),temp_carray.end(),temp_carray.begin(),::tolower)
其中 ::tolower 是转换小写的参数 - 方法二:每个大写字符在写入 temp_carray 时都加32 , 其余正常赋值
if (carray[ i ] >= ‘A’ && carray[ i ] <= ‘Z’){ temp_carray[ index ] = carray[ i ] + 32; }
else{ temp_carray[ index ] = carray[ i ]; }
【完整代码】
#include<iostream>
#include<string>
using namespace std;
const int SZ = 100;
int main()
{
string carray; //初始化数组
int is_palindrome = 1; //判断标记
//输入字符串
cout << "Please input a string .." << endl;
getline(cin,carray);
int len = carray.size();
//虑去原 carray 中所有非字母字符,同时将大写全部改成小写写入 temp_carray 中
string temp_carray(len,' '); //用空格初始化 temp_carray
//设置变量 index 用作记录 temp_carray 的有效长度
int index = 0;
for(int i = 0; i < len; ++i)
{
if ((carray[i] >= 'a' && carray[i] <= 'z')
|| (carray[i] >= 'A' && carray[i] <= 'Z')) //选中字母字符
{
//对选中的字母字符做大小写统一
if (carray[i] >= 'A' && carray[i] <= 'Z')
{
//将选中的大写字符 acsII 码加 32 改成小写,写入temp_carray 中
temp_carray[index] = carray[i] + 32;
}
else
{
temp_carray[index] = carray[i];
}
//在有效长度不超过原字符串实际长度条件下,写入一个字符,下标自增 1
if (index < len) { ++index; }
}
}
//判定步骤与原程序几乎一致,只是判定的对象变成了 temp_carray 且其长度为 index
for (int i = 0; i < index / 2; i++)
{
if (temp_carray[i] != temp_carray[index - 1 - i])
{
is_palindrome = 0;
break;
}
}
//判定输出
if (is_palindrome)
cout << "The string is a palindrome" << endl;
else
cout << "The string is not a palindrome" << endl;
return 0;
}
4. 约瑟夫问题: n 个人围成一圈,从 1 开始顺序编号;游戏开始,首先生成一个 [1 , n] 区间内的随机数 m,从第一个人开始由 1 到 m 循环报数,报到 m 的人退出圈外,问最后留下的那个人原来的序号。
思路:
- “退出圈外” ←→ 和圈里的 状态 / 展现的形式 不同
可以考虑一开始将所有人都标记成 0 ,要退出圈外的人标记成 1 ,而 “ 报数 ” 时只看标记成 0 的人 - “ 依次报数 ” ←→ 可以看作是遍历的次数( 如 m = 5 ,遍历 5 次就相当于从 1 “ 报数 ” 到 5 )
- “ 一圈 ” ←→ 数据时首尾相连的,在遍历数组的过程中,下标遍历完一次之后要返回开头继续遍历,直到遍历的次数等于 m 为止
- 当报数到 m 并且报到 m 的人 “ 退出圈外 ” 之后,从下一个人开始报数 1 .
- 当圈里只剩下一个人的时候停下
【完整代码】
/*做一个输入总人数,报数从0到几,输出最后没淘汰的人编号的函数*/
void Joseph_Ring(int Total_number,int Max_number)
{
int number = 0; //记录当前报数的数值,范围是 0 -- Max_number
int index = 0; //设定下标范围是 0 -- Total_number
//Circle 数组用来记录圈子里所有人的状态, 0 表示在圈内, 1 表示已经 “ 出圈 ”
vector<int> Circle(Total_number, 0);
//变量 man 记录圈子内还有多少没有被淘汰出去的,初始化为总人数 Total_number
int man = Total_number;
while ( man - 1 )
{
/*设 while 报数循环,只要 man 不等于 1 就继续报数*/
//先进行“ 报数 ”,即对 number 调整,如果轮到已经出圈的人就不动,否则就加1
//而Circle数组中只有 0 和 1 的状态
number = number + (1 - Circle[index]);
if (number == Max_number)
{
Circle[index] = 1; //标记1代表出 “ 圈 ”
number = 0; //清零,从头开始报数
man--; //出圈 1 人,圈内有效人数少 1 个
}
//对index的调整
//因为人是围成一圈的,所以可能会出现报完一圈要返回开头报数的情况
if (index == Circle.size() - 1)
{
//如果下标满了就要重设为 0
index = 0;
}
else
{
//如果下标没满就加 1
++index;
}
}
cout <<"最后留下的人序号为:" << endl;
for (int i = 0; i < Total_number; ++i)
{
if (Circle[i] == 0)
{
cout << i + 1 << " ";
}
}
cout << endl;
}