笔试强训02|倒置字符串|排序子序列(C++)

单选
1.单选

使用printf函数打印一个double类型的数据,要求:输出为10进制,输出左对齐30个字符,4位精度。以下哪个选项是正确的?
A. %-30.4e
B. %4.30e
C. %-30.4f
D. %-4.30f

解析
  • double数据类型,10进制:是用f而不是e
    • e:按指数类型进行输出
    • %m.nf
  • 左对齐:要输出左对齐,在m前面加个负号
  • 输出的字符占30个字符宽度:m就是30
  • 4位精度:小数点后保留4位,n就是4
    • %-30.4f
  • 选C

2.单选

请找出下面程序中有哪些错误()

int main()
{ 
	int i = 10; 
	int j = 1; 
	const int *p1;//(1) 
	int const *p2 = &i;//(2) 
	p2 = &j;//(3) 
	int *const p3 = &i;//(4) 
	*p3 = 20;//(5) 
	*p2 = 30;//(6) 
	p3 = &j;//(7) 
	return 0; 
}

A. 1,2,3,4,5,6,7
B. 1,3,5,6
C. 6,7
D. 3,5

解析
  • 常量指针:指针所指空间的值不能发生改变,不能通过指针解引用修改指针所指空间的值,但是指针的指向可以发生改变
  • 指针常量:指针本身是一个常量,指针的指向不能发生改变,但是指针所指空间的值是可以发生改变的,可以通过指针解引用改变指针所指空间的值
  • 区分:根据const和*的相对位置
    const在*的左边:常量指针
    const在*的右边:指针常量
  1. 常量指针,无初始化
  2. 常量指针,指向变量i的地址
  3. p2指针的指向由i指向j,因为是常量指针,指针指向可以发生改变
  4. 指针常量,指向变量i的地址
  5. p3解引用修改值,指针常量,可以通过指针解引用修改指向的值
  6. 对指针p2解引用进行赋值,这是一个常量指针,不能通过解引用改变所指空间的值,错
  7. 给p3指针赋值为j变量,改变了p3指针的指向,是指针常量,指针本身是一个常量,指针的指向是不能发生改变的,错
  • 选C

3.单选

下面叙述错误的是()

char acX[]="abc"; 
char acY[]={'a','b','c'}; 
char *szX="abc"; 
char *szY="abc";

A. acX与acY的内容可以修改
B. szX与szY指向同一个地址
C. acX占用的内存空间比acY占用的大
D. szX的内容修改后,szY的内容也会被更改

解析
  • acx和acy是两个字符数组,初始化的方式不同
  • szx和szy都是指针,两个指针的指向都是一个常量字符串
    A:定义的时候是在栈上开辟的,内容是可以修改的
    B:都是指针指向同一个常量字符串,所以指向的是同一个地址
    C:两个初始化不同,acx是字符串初始化,acy是字符数组初始化,acx后面由\0占4个字节,acy后面没有\0占3个字节
    D:szx是一个指针,内容被修改就是指szx的指向发生了改变,常量字符串abc没有发生改变,szy的指向没有发生改变,所以szy的内容不会发生改变,
  • 选D

4.单选

在头文件及上下文均正常的情况下,下列代码的运行结果是()

int a[] = {1, 2, 3, 4}; 
int *b = a; 
*b += 2; 
*(b + 2) = 2; 
b++; 
printf("%d,%d\n", *b, *(b + 2));

A. 1,3
B. 1,2
C. 2,4
D. 3,2

解析
  • int *b = a; 指针b指向数组的首元素
  • *b += 2;先对b进行解引用,获取到数组首元素,然后进行+=2,也就是给1+2=3以后赋给数组首元素
  • *(b + 2) = 2;先给指针b+2,向后偏移两个指针类型大小,因为是int类型数组,向后偏移8个字节,再解引用,获取到数组第三个元素,将它改为2
  • b++;将指针b向后偏移1个指针类型大小,向后偏移4个字节,指向数组第二个元素
  • *b对指针类型解引用进行打印,获取数组第二个元素,2
  • *(b + 2),向后偏移两个指针类型的大小,也就是8个字节,指向4
  • 选C

5.单选

下列关于C/C++的宏定义,不正确的是()
A. 宏定义不检查参数正确性,会有安全隐患
B. 宏定义的常量更容易理解,如果可以使用宏定义常量的话,要避免使用const常量
C. 宏的嵌套定义过多会影响程序的可读性,而且很容易出错
D. 相对于函数调用,宏定义可以提高程序的运行效率

解析

A:宏定义是没有类型安全检测的
B:宏定义没有类型安全检测,并且在预处理阶段进行宏替换,无法进行调试,所以要尽量使用const常量
C:宏定义可能会导致运算符优先级的问题,很容易导致出错
D:宏定义相对于函数调用,没有创建函数栈帧和函数压栈的开销,可以提高程序的运行效率

  • 选B

6.单选

有以下定义:

int a[10]; 
char b[80];

函数声明为:

void sss(char[],int[]);

则正确的函数调用形式是()
A. sss(a,b);
B. sss(char b[],int a[]);
C. sss(b[],a[]);
D. sss(b,a);

解析
  • 定义了一个整形数组和一个char类型数组
  • 函数名是sss,函数返回值是void,第一个参数是char类型数组,第二个参数是int类型数组
  • 函数调用时,参数是数组类型的话,在调用的时候,需要传入数组名就可以,也就是数组的地址
    • A:写反了
    • B:函数参数不需要写数据类型
    • C:不应该+方括号
    • D:正确
  • 选D

7.单选

用变量a给出下面的定义:一个有10个指针的数组,该指针指向一个函数,该函数有一个整形参数并返回一个整型数()
A. int *a[10];
B. int (*a)[10];
C. int (*a)(int);
D. int (*a[10])(int);

解析
  • 方括号运算符的优先级高于*号的优先级
    A:指针数组:a先和方括号结合,表示是一个数组,数组里存放的是int类型的指针
    B:数组指针:因为加了括号,a先和*结合,是一个指针,指针指向的是一个int类型的数组,数组大小为10
    C:函数指针:a先和*结合,表示是一个指针,后边括号里是int,表示这是一个函数指针,指针指向一个函数,该函数有一个int参数,参数值类型也是int
    D:函数指针数组:a先和方括号结合,表示是一个数组,再和*结合,表示是一个指针,数组里存放的是函数指针,函数指针指向一个函数,该函数有一个整形参数,并返回一个整形数
  • 选D

8.单选

以下 C++ 函数的功能是统计给定输入中每个大写字母的出现次数(不需要检查输入合法性,所有字母都为大写),则应在横线处填入的代码为()

void AlphabetCounting(char a[], int n) 
{ 
	int count[26] = {}, i, kind = 10; 
	for (i = 0; i < n; ++i) 
		_________________; 
	for (i = 0; i < 26; ++i) 
	{ 
		printf("%c=%d", _____, _____); 
	} 
}

A.

++count[a[i]-'Z']
'Z'-i 
count['Z'-i]

B.

++count['A'-a[i]]
'A'+i 
count[i]

C.

++count[i] 
i 
count[i]

D.

++count['Z'-a[i]] 
'Z'-i 
count[i]

E.

++count[a[i]] 
'A'+i 
count[a[i]]
解析
  • 这是一个函数的定义,第一个参数是一个字符数组,输入的大写字母;第二个参数是数组的大小
  • 定义一个count数组,大小为26,用来计数A~Z26个字母所对应的出现次数,数组元素中存放的是每个字母出现的次数
  • 第二个for循环,去遍历count这个数组,去统计每个字符出现了多少次
  • 将字符和出现次数打印出来
  • if语句是用来控制分号的
    A:a[i]-'Z'数组里存放的是大写字母A~Z,这里的ASCII码值是<=Z的ASCII码值,所以这里的计算结果<=0,因为数组下标不能小于0,所以A错误
    B:'A'-a[i]最后的大小<=0,因为count数组下标不能小于0,所以B错
    C:i的值是取决于n的,n是字符数组的大小,字符数组有可能比count数组大,可能会越界,所以不满足题意
    D:'Z'-a[i],是字符Z的话,Z-Z等于0,count[0]存的是大写字母Z出现的次数,倒序存储
    E:a[i]保存的是大写字母A~Z,ASCII码值肯定超过26,肯定会产生越界
  • 选D

9.单选

在32位cpu上选择缺省对齐的情况下,有如下结构体定义:

struct A
{ 
		unsigned a : 19; 
		unsigned b : 11; 
	unsigned c : 4; 
		unsigned d : 29; 
	char index; 
};

则sizeof(struct A)的值为()
A. 9
B. 12
C. 16
D. 20

解析
  • 冒号后面的数字表示变量所占多少比特位,去计算它所占的空间有多大
    • a变量是一个unsigned类型,首先会开辟一个4字节空间(32bit),因为是位段,19个bit位会保存在开辟的4个字节里面
    • b变量是一个unsigned类型,占11个比特位,如果空间足够的话,会放在之前开辟的空间,19+11=30
    • c变量是一个unsigned类型,因为之前的空间已经用了30个bit,只剩2个bit,放不下了,只能在开4个字节空间,32个bit位,将c变量放进去,占4个bit位
    • d变量占29个比特位,因为上一个开辟的4个字节的空间只剩28个bit位,是放不下的,只能重新开辟空间,也是4个字节,32个bit位,将29个bit位放进去
    • 最后一个是一个char类型,不同于unsigned类型,需要再开一个1个字节的空间,8个bit位,将index变量放进去
  • 因为有内存对齐的原因,所以结构体最终的整个大小,是最大宽度的整数倍,也就是4字节的整数倍
  • 3*4+1=13,比这个大也就是16个字节
4字节(32):19+11
4字节(32):4
4字节(32):29
1字节(8):
13->16字节
  • 选C

10.单选

下面代码会输出()

int main()
{ 
	int a[4]={1,2,3,4}; 
	int *ptr=(int*)(&a+1); 
	printf("%d",*(ptr-1)); 
}

A. 4
B. 1
C. 2
D. 3

解析
  • 给了个整形数组,里面的元素是1,2,3,4
  • 定义了一个指针ptr,利用数组给它赋值,对数组名进行取地址,变成一个数组指针,指向数组首元素,
  • +1向后偏移指针类型的大小,也就是整个数组类型大小,指向数组最后一个元素的下一个位置
  • ptr-1:往前偏移一个指针类型大小,也就是4个字节,因为ptr指针类型是int类型,指向数组最后一个元素,解引用获取到这个数组的最后一个值4
  • 选A

编程
1.编程题

倒置字符串_牛客题霸_牛客网 (nowcoder.com)
技能
枚举 模拟 字符串 栈

题目
  • 描述
    将一句话的单词进行倒置,标点不倒置。比如 I like beijing. 经过函数后变为:beijing. like I
  • 输入描述:
    每个测试输入包含1个测试用例: I like beijing. 输入用例长度不超过100
  • 输出描述:
    依次输出倒置之后的字符串,以空格分割
  • 示例1
    输入:
    I like beijing.
    输出:
    beijing. like I
解析
I like beijing.

分两步走

  1. 先将整体字符串进行逆置
    gnijieb ekil I ‘\0’
  2. 每个单词进行逆置
    单词与单词之间是一个空格,但是最后一个单词后面是一个\0
    对于C++
    1. s.begin():开始,指向g
    2. s.end():结尾,指向\0
  • 每个单词逆置的时候分别定义两个指针,一个start,一个end
    • start和end之间就是一个区间,表示的就是一个单词
    • 刚开始start指向s.begin(),end也指向start,然后让end走到空格,此时start和end之间就是一个单词
思路
  • 第一步,先将这个单词逆置,逆置后变成beijing
  • 下一步逆置下一个单词,将start指向end的下一个,start=end+1,end再等于start,end再往后走遇到空格,再形成一个空间,对ekil进行逆置,变成like
  • 下一步,start=end+1,指向I,end等于I的地方,end往后走,如果和s.end()相遇,说明又是一个单词,把I逆置一下,还是I
  • 完成题意
注意
reverse里面是`[start, end)`,不包含end
代码
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
	//从标准输入中获取一行字符串,并存储在字符串变量 s 中
	string s;
	getline(cin, s);
	
	//1.进行整体的逆置,即将整个字符串 s 逆序排列
	reverse(s.begin(), s.end());
	
	//定义迭代器start,初始化为字符串的起始位置
	auto start = s.begin();

	//循环处理字符串中的每个单词
	while(start != s.end())
	{
		//定义迭代器 end,初始化为 start
		auto end = start;

		//移动end直到找到空格或者到达字符串末尾
		while(end != s.end() && *end != ' ')
		{
			end++;
		}

		//对单词进行局部逆置,即将单词内部的字符逆序排列
		reverse(start, end);

		//如果还有下一个单词,将start移动到下一个单词的起始位置
		if(end != s.end())
		{
			start = end + 1;
		}
		else
		{
			start = end;
		}
	}
	
	// 输出经过处理后的字符串
	cout << s << endl;
}

2.编程题

排序子序列_牛客笔试题_牛客网 (nowcoder.com)
技能
贪心

题目
  • 牛牛定义排序子序列为一个数组中一段连续的子序列,并且这段子序列是非递增或者非递减排序的。牛牛有一个长度为n的整数数组A,他现在有一个任务是把数组A分为若干段排序子序列,牛牛想知道他最少可以把这个数组分为几段排序子序列.
  • 如样例所示,牛牛可以把数组A划分为[1,2,3]和[2,2,1]两个排序子序列,至少需要划分为2个排序子序列,所以输出2
    输入描述
    输入的第一行为一个正整数n(1 ≤ n ≤ 10^5)
    第二行包括n个整数A_i(1 ≤ A_i ≤ 10^9),表示数组A的每个数字
    输出描述
    输出一个整数表示牛牛可以将A最少划分为多少段排序子序列
    示例1
    输入
    6
    1 2 3 2 2 1
    输出
    2
解析
  • 非递减就是a[i]<=a[i+1],递减就是a[i]>a[i+1]
  • 非递增就是a[i]>=a[i+1],递增就是a[i]<a[i+1]
    1,2,3,4,5:递增排列
    9,8,7,6,5:递减排列
    1,2,3,3,4,5,8,8:非递减排列
    9,8,7,7,6,5,5,2,1:非递增排列
思路
  • i下标从0开始,它每次比较i下标和i+1下标的值,如果
    if(a[i]<a[i+1]),即将进入非递减排序序列,
    if(a[i]==a[i+1]),不用关心,因为不管是非递增还是非递减都有相同的数字
    if(a[i]>a[i+1]),即将进入非递增排序序列
  • 循环条件,i<n,
  • 定义一个计数器count,来记录一下,如果出现i的值和i+1的值的关系发生变化,count++,一直到i全部走完的时候,count就是最后结果
  • i==i+1的情况,不管是在非递增还是非递减里面,都属于本身的情况的一种,只需要让i继续往后++就行
代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int n;
	cin >> n;
	vector<int> a;
	a.resize(n);

	for(int i = 0; i < n; i++)
	{
		cin >> a[i];
	}
	//数组当中已经存在数字
	
	int i = 0;
	int count = 0;
	while(i < n)
	{
		//进入非递减序列
		if(a[i] < a[i+1])
		{
			//注意i的值不能一直加,这里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;
}

为了避免越界情况的产生
a.resize(n+1);
a[n] = 0,放零是因为提供的数据全是正整数,放0没有影响

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	// 从输入中获取整数 n,表示数组的大小
	int n;
	cin >> n;

	// 创建一个大小为 n+1 的整数向量 a,并将最后一位初始化为 0
	vector<int> a;
	a.resize(n+1);
	a[n] = 0;

	// 循环读取 n 个整数到数组 a 中
	for(int i = 0; i < n; i++)
	{
		cin >> a[i];
	}
	//数组当中已经存在数字

	// 初始化迭代器 i 为 0,用于遍历数组
	int 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;
}
  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值