C++习题(三)

C++习题(三)

题目综述

  1. 已知一个矩阵,存储在一个二维数组中。将矩阵中和值为最大的那一行元素首行对换。
  2. 已知一个方阵,存储在一个二维数组中。用指针访问数组元素的方法,计算方阵中上三角所有元素的和、下三角所有元素的和,以及主对角线上的所有元素和。
  3. 判断字符串是否是“回文”。所谓“回文”是指顺读和逆读都是一样的串,例如串 12321 和 madam 都是回文。
  4. 约瑟夫问题: n 个人围成一圈,从 1 开始顺序编号;游戏开始,首先生成一个 [1 , n] 区间内的随机数,从第一个人开始由 1m 循环报数,报到 m 的人退出圈外,问最后留下的那个人原来的序号。

题目详解

1.已知一个矩阵,存储在一个二维数组中。将矩阵中和值为最大的那一行元素与首行对换。

思路:
  1. 找到 “ 和值最大的行 ”
  2. 将找到的 “ 最大行 ” 和首行整行互换
分步骤处理:
①找到 “ 和值最大的行 ”
  • 先两次遍历来计算每一行的和,然后将求得的和存在一个数组 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.已知一个方阵,存储在一个二维数组中。用指针访问数组元素的方法,计算方阵中上三角所有元素的和、下三角所有元素的和,以及主对角线上的所有元素和。

思路:
  1. 找到每一行 “ 对角线 ” 的位置 (指针的表示形式)
  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 来代替字符数组来完成相同操作。


思路:
  1. 改用sring来存放数据
  2. 过滤非字母字符
  3. 大小写统一
分步骤处理:
①改用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,从第一个人开始由 1m 循环报数,报到 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;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值