C++笔试强训第六天
Posted 不 良
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++笔试强训第六天相关的知识,希望对你有一定的参考价值。
选择题
1.
解析:十进制转换为八进制就是不断的除8,取余数。十进制转换成其他进制的数就是除以进制,取余。
解析:注意
printf
的转换,%%
只会打印一个%
,所以选A。
解析:由于
()
的原因p先和*
结合,说明p是一个指针,char (*p)[16]
是一个数组指针,指向一个char类型的字符数组,该数组中有16个元素。
解析:a表示首元素的地址,对与二维数组,首元素是第0行数组的地址。明显D选项还是一个地址。
解析:
define
是直接替换其所在位置就行,替换之后正常计算就可以,注意优先级就可以。
解析:
解析:free之后不会自动的把指针变量制成NULL,需要手动处理。
解析:题目中为二维数组,数组名表示数组首元素的地址,
a
是二维数组数组名,表示数组首元素的地址,在二维数组中,数组首元素的地址即第一行的地址,第一行的数组名可以用a[0]表示;*(a+1)
可以表示成*(&a[0]+1)
,&a[0]是int(*)[5]类型,+1之后到二维数组的第二行,所以*(a+1)
就是a[1]
,a[1]就是第二行数组名,也表示第二行首元素的地址&a[1][0]
;+2是向后移动2个int*
类型的大小。所以选B。
解析:64位编译器下指针大小为8个字节大小,所以a4占8个字节大小;a1(0 ~ 7),a2(8~ 9),浪费(10 ~ 11),a3(12~ 15),a4(16~23),共24个字节的大小,也是最大对齐数8的整型倍。选A。
解析:本题考查递归,递归类题目可以通过画图来解决。
编程题
1.不要二
解析:这里理解输入顺序为行、列。
( (x1-x2) * (x1-x2) + (y1-y2) * (y1-y2) ) == 4 1 + 3 = 4 3 + 1 = 4 2 + 2 = 4 0 + 4 = 4 4 + 0 = 4 前三种情况都不存在 只能是x1=x2 (y1-y2) = 2 或者 y1=y2 x1-x2 = 2 假设数组全是1,经过上面的公式变换之后 1 1 1 1 1 1 --> 1 1 1 1 0 0 也就是v[i][j]和v[i+2][j]、v[i][j]和v[i][j+2] 只能有一个有蛋糕
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
int row,col;//row行,col列
cin >> row >> col;
vector<vector<int>> a; //设定一个二维数组
a.resize(row);//定义行数
for(int i = 0; i < row; i++) //一行一行的进行初始化
a[i].resize(col,1);//全部初始化为1
int count = 0;
for(int i = 0; i < row; i++)
for(int j = 0; j < col; j++)
if(a[i][j] == 1) //判单是否为1
count++;
if(i + 2 < row) //判断是否在二维数组之内
a[i+2][j] = 0;
if(j + 2 < col) //判断是否在二维数组之内
a[i][j+2] = 0;
cout << count;
return 0;
// 64 位输出请用 printf("%lld")
答案解析:
本题看起来很难,实际是一个中等难度的题。本题如果没记错,是一个往年网易的笔试题,大家可以看到大厂的题的难度。
本题的重点是要读懂题意,并且需要多读两遍,才能读懂,本题本质就是在二维数组中每个坐标去放蛋糕,一个坐标位置放了蛋糕,跟他欧几里得距离为2的位置不能放蛋糕,这个就是关键点。对于两个格子坐标(x1,y1),(x2,y2)的欧几里得距离为: ( (x1-x2) * (x1-x2) + (y1-y2) * (y1-y2) ) 的算术平方根 。也就是说:如果(x1,y1)放了蛋糕,则满足 ( (x1-x2) * (x1-x2) + (y1-y2) * (y1-y2) ) == 4的(x2,y2)不能放蛋糕。
( (x1-x2) * (x1-x2) + (y1-y2) * (y1-y2) ) == 4看起来是一个无解的表达式。
但是可以进行加法表达式分解:
1+3=4
3+1=4
2+2=4
0+4=4
4+0=4
仔细分析前三个表达式是不可能的,因为(x1-x2) * (x1-x2)表达式结果不能等于2或3。也就是说( (x1-x2) * (x1-x2) 和(y1-y2) * (y1-y2) )两个表达式一个等于0,一个等于4.可以看出:假设放蛋糕的位置是(x1,y1),则不能放蛋糕的位置(x2,y2),满足x1==x2
,y1-y2==2
或者x1-x2==2
,y1==y2
。
【解题思路】:
仔细读理解了上面的题目解读,本题就非常简单了,使用vector<vector>定义一个二维数组,resize开空间并初始化,每个位置初始化为1,表示当蛋糕,a[i][j]
位置放蛋糕,则可以标记处a[i
][j+2]和a[i+1][j]
位置不能放蛋糕,遍历一遍二维数组,标记处不能放蛋糕的位置,统计也就统计出了当蛋糕的位置数。
// 直接暴力计算,默认所有蛋糕的位置标记成1,不能放的地方标记成0
// 1 1 0 0 1 1
// 1 1 0 0 1 1
// 0 0 1 1 0 0
// 0 0 1 1 0 0
#include<iostream>
#include<vector>
using namespace std;
int main()
int w,h,res = 0;
cin >> w >> h;//w行,h列
vector<vector<int>> a;
a.resize(w);
for(auto& e : a)
e.resize(h, 1);
for(int i=0;i<w;i++)
for(int j=0;j<h;j++)
if(a[i][j]==1)
res++;
// 标记不能放蛋糕的位置
if((i+2)<w)
a[i+2][j] = 0;
if((j+2)<h)
a[i][j+2] = 0;
cout << res;
return 0;
2.把字符串转换成整数
解析:先检查字符串中是否有除数字字符及
'-'\\'+'
外的其他字符,如果有直接返回;如果没有,要先检查首个字符是否为'+'、'-'
,考虑如果为'-'
,需要设立一个flag
进行标记,当第一个字符为'+'、'-'
时,先忽视,对下一个位置开始的数字串进行处理,-'0'
得到对应的数字,每次*10
加上新的数字……在最后检查标记位,如果为true
,说明第一个字符为'-'
,需要进行num = 0 - num
处理,得到结果。
class Solution
public:
bool IsWord(string str) //检查字符串中是否有字母
int i = 0;
for (i = 1; i < str.size(); i++)
if ((str[i] >= 'a' && str[i] <= 'z') || (str[i] >= 'A' && str[i] <= 'Z'))
return false;
return true;
int StrToInt(string str)
int i = 0;
/*if (!IsWord(str))
cout << "0";
return 0;
*/
for (i = 1; i < str.size();i++) //从1开始检查字符串中受否有其他字符
if ((str[i] < '0' ) || (str[i] > '9'))
cout << "0";
return 0;
i = 0;
bool flag = false;//标记其首字符为+或者-
if (str[0] == '-')
flag = true;//如果首字符为-则改变flag,同时i++
i++;
if (str[0] == '+')
i++;//如果首字符为+不改变flag,但是i++
long long num = 0;
//起始条件不能确定,因为不确定str[0]是否是+或者-还是直接数字开头
for (i; i < str.size(); i++)
int nums = str[i] - '0';
num = num * 10 + nums;
if (flag)
num = 0 - num;
cout << num;
return num;
;
答案解析:解题思路非常简单,就是上次计算的结果*10,相当于10进制进位,然后加当前位的值。
例如:“123”转换的结果是
sum=0 sum*10+1->1 sum*10+2->12 sum*10+3->123
本题的关键是要处理几个关键边界条件:
- 空字符串
- 正负号处理
- 数字串中存在非法字符
class Solution
public:
int StrToInt(string str)
if(str.empty())
return 0;
int symbol = 1;
if(str[0] == '-') //处理负号
symbol = -1;
str[0] = '0'; //这里是字符'0',不是0
else if(str[0] == '+') //处理正号
symbol = 1;
str[0] = '0';
int sum = 0;
for(int i=0;i<str.size();++i)
if(str[i] < '0' || str[i] > '9')
sum = sum *10 + str[i] - '0';
return symbol * sum;
;
C++笔试强训第二天
选择题
解析:考查
printf
,%
后面-
表示输出左对齐,输出左对齐30个字符格式为%-30f
,.
后面表示精度。%e
字符以指数形势输出,可以认为是double
类型(也就是小数点后保留6位)的指数。为%f
字符表示输出格式为double
类型。所以上面题目要求的格式为%-30.4f
。C语言中要求我们掌握的各种输出如下:
整形输出:%d整型输出,%ld长整型输出,%u以十进制数输出unsigned型数据(无符号数);
进制输出:%o以八进制数形式输出整数,%x以十六进制数形式输出整数;
字符输出:%c用来输出一个字符,%s用来输出一个字符串;
浮点数输出:%f用来输出实数,以小数形式输出,%e以指数形式输出实数,%g根据大小自动选f格式或e格式,且不输出无意义的零。
解析:本题考查
const
修饰及指针相关知识。指针常量:指针是个常量,即指针的指向不能改变,指针指向空间的内容可以改变;
常量指针:指针指向的空间是个常量,指针指向空间的值不能改变,指针的指向可以改变。
我们判断是指针常量还是常量指针通常是看
const
和*
所在的位置,如int *const p3 = &i
中const
后面紧跟的就是指针变量p3
,这就是指针常量,指针的指向不能改变,指针指向空间的内容可以改变;而int const *p2 = &i
中const
后面紧跟的是*p2
,此时就是常量指针,指针的指向可以改变指针,指向空间的值不能改变。(1)中
const
修饰的是*p1
,是常量指针,指针的值不能改变,指针的指向可以改变。只不过该指针没有给初始值,正确;(2)中
const
修饰的是*p2
,常量指针,指针的值不能改变,指针的指向可以改变,正确;(3)中
p2 = &j
改变了指针p2
的指向,正确;(4)中
const
修饰的是p3
,是指针常量即指针的指向不能修改,指向空间的内容可以修改,正确;(5)中修改了
p3
指向空间的内容,没有修改指针指向,正确;(6)中想修改
p2
指向空间的内容,但是p2
是常量指针,不能够修改,错误;(7)中修改了
p3
指针的指向,但是p3
是指针常量,不能修改指针指向,错误。所以本题中错误的就是(6)、(7)。
解析:本题主要考查常量字符串。
acX
和acY
都是存在栈区的字符数组,acX
和acY
的区别在于acX
存在\\0
,而acY
中不存在\\0
,所以acX
的空间比acY
的空间大。而szX
和szY
字符指针指向的常量字符串是存在常量区(静态区)的,szX
和szY
这两个字符指针指向的是同一个地址,常量区的内容只能读不能被修改,所以D错误。
解析:数组名表示数组首元素的地址,
int *b = a
意思就是指针b
指向b
的地址,而b
表示的数组首元素的地址;*b
得到的就是数组中第一个元素即1,*b += 2
就是将数组中第一个元素修改为3;b + 2
表示的是数组中第3个元素即数组元素3的地址,*(b + 2) = 2
表示的是将数组中第3个元素修改为2;b++
则表示b
此时指向的地址是数组中第2个元素的地址。经过上面的修改之后数组就变为[3,2,2,4]
则输出语句中*b
表示的就是数组中第2个元素,即2;*(b+2)
表示的是第4个元素,即4。所以最后输出结果为2,4。
解析:宏定义在程序预处理的时候是在需要的地方直接展开的,所以不会检查参数的正确性,但可以减少函数调用所需的栈帧和压栈的消耗,可以提高运算效率;宏的嵌套过多可能会出现优先级的问题,容易出错且可读性差。我们平常使用要尽量避免宏定义常量,可以使用
const
修饰的常量,有类型的检查。所以选B。
解析:本题主要考查数组传参。数组传参形参可以是数组,也可以是指针。如下面例子:
int arr[10]传参,他的参数用数组接收可以是 int arr[]或者int arr[10];用指针接收是int* arr。
int* arr[10]传参,他的参数用数组接收是int* arr[10],用指针接收是int** arr
解析:本题考查指针数组、函数指针及函数指针数组。
指针数组:存放指针的数组。
函数指针:int(*pf)(int,int)中,int是函数返回类型,pf是函数指针变量,(int,int)是函数的参数类型,在调用函数的时候可以通过函数指针
int ret = (*pf)(a,b)
,使用函数指针时,也可以省略*
如int ret = pf(a,b)
。函数指针数组:可以存放多个返回类型相同和参数相同的函数的地址。可以写成
int(*pf[5])(int, int)
形式。根据题目描述,可以理解为该变量是一个数组,里面有10个元素,每个元素都是一个函数指针,该函数指针返回类型是整型,参数是整数。所以该数组为函数指针数组,所以选D。A表示的是一个指针数组,数组元素类型为
int*
;B表示一个数组指针,指向数组的指针,存放数组的地址,a是数组指针变量;C表示的是一个返回类型为int
,函数参数为int
的函数指针变量。
解析:可以假设数组
a
中元素为"ABZ"
,则当i = 0
时,即第一个元素A
时,A选项中,元素下标为负数,排除;当i = 1
即元素为B
时,B中数组下标也为负数,排除;C选项中count
数组下标随着i
变化,当数组中元素相同时,并不会累加。如数组元素为"AAA"
时,并不能统计出A
的个数是多少。D中当a
数组元素为"ABZ"
时,count
数组元素下标是逆着来的,也就是字符A
在数组count
中的下标表示为26,所以打印的时候先输出的是Z
的个数,D正确。
解析:本题考查结构体内存对齐和位段。题目中
unsigned
就是指unsigned int
,位段的空间上是按照需要以4个字节(int
)或者1个字节(char
)的方式来开辟的。因为是unsigned int
类型,所以每次开辟4个字节。刚开始存储a
时开辟4个字节即32个bit位,使用19个还剩13个,大于b
所要使用的11个bit位,b
继续使用,使用之后还剩2个不够c
使用,需要再开辟4个字节的空间,c
使用4个bit之后还剩28个,不够d
使用,再开辟4个字节用来存储d
。此时已经使用12个字节;而char
类型变量index
需要1个字节,所以总共使用了13个字节,但是结构体必须是最大对齐数的整数倍,即必须是4的倍数,所以是16。
解析:
&数组名
表示取的是整个数组的地址。所以&a
取的就是整个数组的地址,数组a
中有4个元素,所以数组a
的大小就是16个字节,(&a + 1)
表示跳过整个数组,指向数组后面的那个地址,ptr - 1
表示的是指向数组中最后一个元素。所以结果为4。
编程题
1.排序子序列
解析:暴力解法,一个一个遍历和比较,如
1 2 3 3 2 1
,最开始先拿1和2比较,再比较2和3,依次向后进行比较;当非递增非递减序列停止的时候count++。注意为了避免a[i+1]
的越界,我们在数组后加一个零,因为由题干可知,所有的数据都比0大且0在第n个位置,所以不会对结果有影响。
从这道题可以抽象出来一个模型:在一个数组中截取一段连续的性质相同的序列,可以采用while–if–while模型。
while(A) if(B) while(A&&B) ++i; if(C) while(A&&C) ++i; //.......
该模型的意思是,找到同时满足A和B的序列,其中A为i前进条件,B为元素所满足的条件。当序列中下一个元素不满足B的时候,同时退出while与if语句。方便查找满足另一个条件C的序列。
#include <iostream>
#include <vector>
using namespace std;
int main()
int n = 0;
cin >> n;
vector<int> arr;
//多开辟一个空间,防止越界情况的发生
arr.resize(n+1);
int i = 0;
int count = 0;
for(int i = 0; i < n; i++)
cin >> arr[i];
i = 0;
while(i < n)
//非递增序列
if(arr[i] > arr[i+1])
while(i < n && arr[i] >= arr[i+1])
i++;
count++;
i++;
else if(arr[i] == arr[i+1]) //相等
i++;
else
//非递减序列
while(i < n && arr[i] <= arr[i+1])
i++;
count++;
i++;
cout << count;
return 0;
答案解析:基本思路和上面一样
本题依次比较整个数组
a[i+1]>a[i] ,则进入非递减序列判断,直到遍历到下一个值不大于等于为止count++,然后进行下一位
置的判断a[i+1]<a[i],则进入非递增序列判断,直到遍历到下一个值不小于等于为止count++,然后进行下一位
置的判断a[i+1] == a[i]不进行操作,++i进行下一位置遍历,因为相等既可以属于非递增序列,也可以属于非递减
序列。
本题注意点:本题开始比较a[i+1]与a[i]进行比较,为了避免越界,数组定义为n+1个,同时给a[n] = 0;
a[n] = 0带来的影响,我们分为三种情况讨论:若到a[n-1] 的最后一组是非递减序列,当i=n-1,a[i] >a[i+1],因为前面的数都是大于0的,这个输入
条件已经说明了(去看看题目输入条件描述),里面的循环结束,i++,count++,i==n,外面的循环结
束。若到a[n-1] 的最后一组是非递增序列,当i=n-1,a[i] >a[i+1],因为前面的数都是大于0的,这个输入
条件已经说明了(去看看题目输入条件描述),循环再走一次,i++, i== n,里面的循环结束,i++,
count++,i==n+1,外面的循环结束。第三种情况 1 2 1 2 1最后一个数是单独的情况,后面补个0,序列变成1 2 1 2 1 0,当走完全面的序列
i==n-1时,a[i] > a[i+1],进入判断出一个非递增序列,count++,i++,循环结束。也就是说数组最后一个位置多增加一个0,不会影响第1、2情况的判断,主要是帮助第3情况的正确判
断。
#include<iostream>
#include<vector>
using namespace std;
// 本题牛客测试用例不全,至少应该增加以下两组测试用例
// 输入:
// 4
// 1 3 2 3
// 输出:2
// 输入:
// 6
// 3 2 1 1 2 3
// 输出:2
int main()
int n;
cin >> n;
// 注意这里多给了一个值,是处理越界的情况的比较,具体参考上面的解题思路
vector<int> a;
a.resize(n + 1);//这里有个坑,这个题越界了牛客测不出来,给n,并且不写a[n] = 0;不会报错,但是最好写上
a[n] = 0;
//读入数组
int i = 0;
for (i = 0; i < n; ++i)
cin >> a[i];
i = 0;
int count = 0;
while (i < n)
// 非递减子序列
if (a[i] < a[i + 1])
while (i < n && a[i] <= a[i + 1])
i++;
count++;
i++;
else if (a[i] == a[i + 1])
i++;
else // 非递增子序列
while (i < n && a[i] >= a[i + 1])
i++;
count++;
i++;
cout << count << endl;
return 0;
2.倒置字符串
解析:整体思路可以先将整个字符串逆置,再从前往后依次将每个单词逆置。使用string类中的getline函数输入字符串,再使用
reverse
函数将整个字符串逆置,这个函数有两个参数,参数为迭代器这里可以将迭代器理解为指针,``reverse函数要带上头文件
algorithm。如上面示例,整个字符串逆置之后为
.gnijieb ekil I`,再依次将每个单词逆置。注意不能越界。
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main()
string s1;
getline(cin,s1);//输入字符串
//s1.begin()代表字符串开始位置,s1.end()代表字符串结束位置,这里可以将迭代器理解为指针,那么在字符串.gnijieb ekil I中s1.begin()指向开始位置'.',s1.end()指向字符串结尾即指向'\\0'位置。
reverse(s1.begin(),s1.end());//迭代器为左闭右开,即[s1.begin(),s1.end()),所以逆置的时候不包括'\\0'
for(int i = 0; i < s1.size(); i++)
int tmp = i;//tmp记录每次逆置的开始位置
while(i < s1.size() && s1[i] != ' ')//当s1[i] == ' '时或者当i < s1.size(),结束循环,进行逆置
i++;
//逆置,字符串.gnijieb ekil I
//第一次逆置tmp = 0,i = 8,即s1[i] == ' ',左闭右开,即对[0,7]之间的字符串逆置,逆置之后字符串为beijing. ekil I;
//第二次逆置,tmp = 9,i = 13,即s1[i] == ' ',左闭右开,即对[9,12]之间的字符串逆置,逆置之后字符串为beijing. like I;
//第三次逆置,tmp = 14,i = 15,即i < s1.size()不成立,对[14]这个字符串进行处理,逆置之后字符串为beijing. like I;
reverse(s1.begin() + tmp,s1.begin() + i); //左闭右开
cout << s1;
答案解析:
思路一:先将整个字符串逆置过来,再遍历字符串,找出每个单词,对单词逆置。这里我们使用了stl算法中的reverse,所以这里使用迭代器遍历string。和上面一样。
思路二:直接利用cin>>s接收输入,遇到空格就结束了,自然就分割开了每个单词,其次将每次接收到的单词拼接到之前串的前面就逆置过来了。代码如下:
#include <iostream>
#include <string>
using namespace std;
// cin读取string时自动会被空格分隔开,用另一个字符串存储进行逆序输出
int main()
string s1, s2;
cin >> s2;
while (cin >> s1)
s2 = s1 + " " + s2;
cout << s2 << endl;
return 0;
以上是关于C++笔试强训第六天的主要内容,如果未能解决你的问题,请参考以下文章