【ACM专项练习#02】整行字符串、输入vector、打印图形、处理n组数据以及链表操作等
输入整行字符串
平均绩点
题目描述
每门课的成绩分为A、B、C、D、F五个等级,为了计算平均绩点,规定A、B、C、D、F分别代表4分、3分、2分、1分、0分。
输入
有多组测试样例。每组输入数据占一行,由一个或多个大写字母组成,字母之间由空格分隔。
输出
每组输出结果占一行。如果输入的大写字母都在集合{A,B,C,D,F}中,则输出对应的平均绩点,结果保留两位小数。否则,输出“Unknown”。
样例输入
A B C D F
B F F C C A
D C E F
样例输出
2.00
1.83
Unknown
输入输出知识点
如果要一次性获取一行字符串作为输入,请使用getline函数+cin
#include <iostream>
#include <string>
//获取一行字符串输入
string s;//用于保存输入字符串
getline(cin, s);//用于从输入流读取一行文本
string s;
cin >> s;
//获取一个字符输入
char c;
c = cin.get();
代码
#include <iostream>
#include <string>
using namespace std;
int main(){
string inputLevel;
while(getline(cin, inputLevel)){
float sum = 0;
int count = 0;
int f = 1;
for(int i = 0; i < inputLevel.size(); ++i){
if(inputLevel[i] == 'A') {sum += 4; count++;}
else if(inputLevel[i] == 'B') {sum += 3; count++;}
else if(inputLevel[i] == 'C') {sum += 2; count++;}
else if(inputLevel[i] == 'D') {sum += 1; count++;}
else if(inputLevel[i] == 'F') {sum += 0; count++;}
else if(inputLevel[i] == ' ') continue;
else{
f = 0;
cout << "Unknown" << endl;
break;
}
}
if(f) printf("%.2f\n", sum / count);
}
return 0;
}
输入值并放入vector中
摆平积木
题目描述
小明很喜欢玩积木。一天,他把许多积木块组成了好多高度不同的堆,每一堆都是一个摞一个的形式。然而此时,他又想把这些积木堆变成高度相同的。但是他很懒,他想移动最少的积木块来实现这一目标,你能帮助他吗?
输入
输入包含多组测试样例。每组测试样例包含一个正整数n,表示小明已经堆好的积木堆的个数。
接着下一行是n个正整数,表示每一个积木堆的高度h,每块积木高度为1。其中1<=n<=50,1<=h<=100。
测试数据保证积木总数能被积木堆数整除。
当n=0时,输入结束。
输出
对于每一组数据,输出将积木堆变成相同高度需要移动的最少积木块的数量。
在每组输出结果的下面都输出一个空行。
样例输入
6
5 2 4 1 7 5
0
样例输出
5
输入输出知识点
先分析一下题目,题目要求我们把所有积木堆摆成同样高度
那我们就得将所有积木堆的高度加起来取平均值才能知道每个积木堆要摆多高
然后就是将超过平均值的积木堆减去平均值得到该积木堆需要移动的次数,小于平均值的积木堆不用管,因为我们只需要从高的积木堆取积木
那么就涉及到怎么求平均值,用之前的方法累加虽然可以,但是就没办法再去取到输入的单个数值了
所以需要使用数组来保存输入的元素
具体来说就是创建一个数组,然后在遍历这个数组的时候用cin给每个下标位置赋值
vector<int> stack(n, 0);
for(int i = 0; i < n; ++i){
cin >> stack[i];
...
}
代码
#include <iostream>
#include<vector>
using namespace std;
int main(){
int n;
while(cin >> n){
if(n == 0) break;
int sum = 0;
vector<int> stack(n, 0);
for(int i = 0; i < n; ++i){
cin >> stack[i];
sum += stack[i];
}
int average = sum / n;
int moveTimes = 0;
for(int stackHigh : stack){
if(stackHigh > average){
moveTimes += stackHigh - average;
}
}
cout << moveTimes << endl;
cout<< endl;
}
}
共同祖先
题目描述
小明发现和小宇有共同祖先!现在小明想知道小宇是他的长辈,晚辈,还是兄弟。
输入
输入包含多组测试数据。每组首先输入一个整数N(N<=10),接下来N行,每行输入两个整数a和b,表示a的父亲是b(1<=a,b<=20)。小明的编号为1,小宇的编号为2。
输入数据保证每个人只有一个父亲。
输出
对于每组输入,如果小宇是小明的晚辈,则输出“You are my younger”,如果小宇是小明的长辈,则输出“You are my elder”,如果是同辈则输出“You are my brother”。
样例输入
5
1 3
2 4
3 5
4 6
5 6
6
1 3
2 4
3 5
4 6
5 7
6 7
样例输出
You are my elder
You are my brother
代码
#include<iostream>
#include<vector>
using namespace std;
int main(){
int n, a, b, xm, xy;
vector<int> nums(30, 0);
while(cin >> n){
while(n--){
cin >> a >> b;
nums[a] = b;
}
int xmLen = 0, xyLen = 0;
xm = nums[1];
while(xm != 0){
xm = nums[xm];
xmLen++;
}
xy = nums[2];
while(xy != 0){
xy = nums[xy];
xyLen++;
}
if (xmLen > xyLen) cout << "You are my elder" << endl;
else if (xmLen == xyLen) cout << "You are my brother" << endl;
else cout << "You are my younger" << endl;
}
}
取输入整数的每一位值
奇怪的信
题目描述
有一天, 小明收到一张奇怪的信, 信上要小明计算出给定数各个位上数字为偶数的和。
例如:5548,结果为12,等于 4 + 8 。
小明很苦恼,想请你帮忙解决这个问题。
输入
输入数据有多组。每组占一行,只有一个整整数,保证数字在32位整型范围内。
输出
对于每组输入数据,输出一行,每组数据下方有一个空行。
样例输入
415326
3262
样例输出
12
10
代码
只要别忘了通过取模的方式获取每个位上的数值的方法,这题就是好做的
#include<iostream>
#include<vector>
using namespace std;
int main(){
int n, num;
while(cin >> n){
int res = 0;
while(n != 0){
num = n % 10;
n /= 10;
if(num % 2 == 0) res += num;
}
cout << res << endl;
cout << endl;
}
}
运营商活动
题目描述
小明每天的话费是1元,运营商做活动,手机每充值K元就可以获赠1元,一开始小明充值M元,问最多可以用多少天? 注意赠送的话费也可以参与到奖励规则中
输入
输入包括多个测试实例。每个测试实例包括2个整数M,K(2<=k<=M<=1000)。M=0,K=0代表输入结束。
输出
对于每个测试实例输出一个整数,表示M元可以用的天数。
样例输入
2 2
4 3
13 3
0 0
样例输出
3
5
19
提示
注意第三组数据「13 3」结果为什么是19呢, 13/3=4,获得4元奖励。 13%3=1,还剩下1元,4+1=5,5元继续参加奖励规则。 5/3=1,获得1元奖励。 5%3=2,剩下2元,1+2=3,3元继续参与奖励规则。 3/3=1,获得1元奖励。 3%3=0,剩下0元,1+0=1。 1元不能参与剩下奖励。所以一共可以使用的天数是 13+4+1+1=19
代码
在每次充值后,小明会获得额外的1元话费,因此他的总话费是充值金额 M 加上额外的话费。如果我们把每次充值后获得的额外话费也视为一次充值,那么小明每次充值后都会获得额外的 k 元话费。
假设小明一开始充值了 M 元钱,那么他会获得 M / k 元额外话费,以及 M % k 元剩余的钱。其中,M % k 就表示小明无法凑成一次充值的剩余金额。
接着,小明可以利用获得的 M / k 元额外话费再次充值,于是他又会获得 (M / k) / k 元额外话费和 (M / k) % k 元剩余的钱。以此类推,小明可以一直通过充值来获得额外的话费。每次充值所获得的额外话费都是上一次剩余的钱除以 k 得到的。
因此,我们可以用一个 while 循环来模拟小明不断充值的过程,直到无法再获得额外的话费为止。在每次循环中,我们需要计算出当前剩余的钱数 count(包括上一轮的剩余钱数和赠送的额外话费),并将其除以 k 得到额外的话费和剩余的钱数(即下一轮的 M 和 M % k)。最终,小明的总话费就是所有充值所获得的额外话费和剩余话费之和。
因此,m % k 代表了小明一开始充值后剩余的钱数,即无法凑成一次充值的金额,也可以看作是额外话费的一部分。
为什么要凑成一次充值的金额?
在题目中,每充值 K 元就可以获赠 1 元话费,因此,当小明的钱数不能凑成一次充值的金额时,他就无法获得额外的话费了。为了最大化利用每次充值可以获得的额外话费,小明需要确保每次充值的金额都是 K 的整数倍。这样,他就可以获得尽可能多的额外话费,从而最大化使用他的钱数。
例如,假设小明一开始充值了 20 元钱,K=5,那么他可以分别在第 5、10、15、20 天充值,每次充值 5 元,这样他就可以获得 4 元额外话费,总共获得 24 元话费,可以用于 24 天。
如果他在第 4 天充值了 4 元,那么他只能获得 3 元额外话费,总共获得 23 元话费,只能用于 23 天。因此,为了最大化使用他的钱数,小明需要确保每次充值的金额都是 K 的整数倍,也就是凑成一次充值的金额。
#include<iostream>
using namespace std;
int main(){
int m, k;
while(cin >> m >> k){
if(m == 0 && k == 0) break;
int sum = m + m / k;//可以得到的总话费
int extra = m / k + m % k;//额外话费包括:送的+充值时不够K元剩下的
while(extra / k != 0){
sum += extra / k;
extra = extra / k + extra % k;
}
cout << sum << endl;
}
}
简单来说就是:我现在有 M 元,但是因为每充 K 元就送1元话费,所以我要把 M 元尽量分 M/K 次来充,这样每次我就能额外获得 M/K 元
而无法除完的部分,就是剩余话费extra(送的+充值时不够K元剩下的)
然后我们拿剩的话费继续充值,还是以除K的方式充,每次能得到的额外话费就是 extra/K,加到总话费中,直到没有额外话费就结束
打印图形
打印数字图形
题目描述
先要求你从键盘输入一个整数n(1<=n<=9),打印出指定的数字图形。
输入
输入包含多组测试数据。每组输入一个整数n(1<=n<=9)。
输出
对于每组输入,输出指定的数字图形。
注意:每行最后一个数字后没有任何字符。
样例输入
5
样例输出
1
121
12321
1234321
123454321
1234321
12321
121
1
输入输出知识点
这里考察的是输出的技巧
像上面这种图形可以分成上下两部分进行打印
以上半部分为例,假设n = 3,应该怎么打印?
上半部分是由空格、递增的数、递减的数这三部分组成的,分别打印即可。打印时通过循环控制,优先考虑空格
先观察。n为3时,第一行的空格有2个,递增的数有一个,递减的没有;
以此类推,再通过两次for循环控制打印元素的个数(做的时候画图对照就知道该怎么设置循环初始值了)
注意,这只是打印,不要考虑指针会不会重叠,打印东西的时候只需要考虑打几个就行
代码
#include<iostream>
using namespace std;
void printTop(int n){
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= n - i; ++j){//打印空格
cout << " ";
}
for(int j = 1; j <= i; ++j){//打印递增数字
cout << j;//递增肯定从小到大
}
for(int j = i - 1; j >= 1; --j){//打印递减数字
cout << j;//递减肯定从大到小(基于此再去想怎么控制这个数以满足规则)
}
cout << endl;//记得换行
}
}
void printDown(int n){//逻辑一样,只是i的初始值需要修改为从大到小
for(int i = n - 1; i >= 1; --i){
for(int j = 1; j <= n - i; ++j){
cout << " ";
}
for(int j = 1; j <= i; ++j){
cout << j;
}
for(int j = i - 1; j >= 1; --j){
cout << j;
}
cout << endl;
}
}
int main(){
int n;
while(cin >> n){
if(n < 1 || n > 9) cout << "error" << endl;
printTop(n);
printDown(n);
}
}
镂空三角形
题目描述
把一个字符三角形掏空,就能节省材料成本,减轻重量,但关键是为了追求另一种视觉效果。在设计的过程中,需要给出各种花纹的材料和大小尺寸的三角形样板,通过电脑临时做出来,以便看看效果。
输入
每行包含一个字符和一个整数n(0<n<41),不同的字符表示不同的花纹,整数n表示等腰三角形的高。显然其底边长为2n-1。如果遇到@字符,则表示所做出来的样板三角形已经够了。
输出
每个样板三角形之间应空上一行,三角形的中间为空。行末没有多余的空格。每条结果后需要再多输出一个空行。
样例输入
X 2
A 7
@
样例输出
X
XXX
A
A A
A A
A A
A A
A A
AAAAAAAAAAAAA
输入输出知识点
“打印数字图形”的升级版
还是要遵循分别打印空格和字符的逻辑,如果不清楚要打印多少,就根据示例中每行空格和字符的个数来推导
做的时候记得画图
代码
#include<iostream>
using namespace std;
void printSpace(int n){
for(int i = 0; i < n; ++i) cout << " ";
}
void printChar(int n, char a){
for(int i = 0; i < n; ++i) cout << a;
}
int main(){
char edge;
int n;
while(cin >> edge){
if(edge == '@') break;
cin >> n;
if(n < 0 || n > 41) cout << "error" << endl;
if(n == 1) cout << edge << endl;
for(int i = 1; i <= n - 1; ++i){
printSpace(n - i);//打印空格
if(i == 1){
cout << edge;
}else{
cout << edge;
printSpace(2 * i - 3);//打印中间的空格
cout << edge;
}
cout << endl;
}
if(n > 1){//当n大于1就要打印次变底边
printChar(2 * n - 1, edge);
cout << endl;
}
cout << endl;
}
}
处理n组数据输入
句子缩写
题目描述
输出一个词组中每个单词的首字母的大写组合。
输入
输入的第一行是一个整数n,表示一共有n组测试数据。(输入只有一个n,没有多组n的输入)
接下来有n行,每组测试数据占一行,每行有一个词组,每个词组由一个或多个单词组成;每组的单词个数不超过10个,每个单词有一个或多个大写或小写字母组成;
单词长度不超过10,由一个或多个空格分隔这些单词。
输出
请为每组测试数据输出规定的缩写,每组输出占一行。
样例输入
1
ad dfa fgs
样例输出
ADF
提示
注意:单词之间可能有多个空格
输入输出知识点
1、演示了怎么把一个小写字符转换为大小
用ASCII码
2、演示了如何处理n组数据输入
在循环外用cin接收n,然后在while中n--
3、字符串输入后会带有一个回车,需要处理掉
用tmp = getchar();处理
代码
#include<iostream>
using namespace std;
char char2Capital(char& c){ // 小写变大写
if(c >= 'a' && c <= 'z') c -= 32;
return c;
}
int main(){
int n;
string res, s;
cin >> n;
char tmp;
tmp = getchar(); // 吸收一个回车,因为输入n之后,要输入一个回车
while(n--){//不能--n
res = "";
getline(cin, s);
res += char2Capital(s[0]);
for(int i = 1; i < s.size() - 1; ++i){
if(s[i] == ' ' && s[i + 1] != ' '){
res += char2Capital(s[i + 1]);
}
}
cout << res << endl;
}
}
神秘字符
题目描述
考古学家发现墓碑上有神秘的字符。经过仔细研究,发现原来这是开启古墓入口的方法。
墓碑上有2行字符串,其中第一个串的长度为偶数,现在要求把第2个串插入到第一个串的正中央,如此便能开启墓碑进入墓中。
输入
输入数据首先给出一个整数n,表示测试数据的组数。(整个输入中,只有一个n)
然后是n组数据,每组数据2行,每行一个字符串,长度大于0,小于50,并且第一个串的长度必为偶数。
输出
请为每组数据输出一个能开启古墓的字符串,每组输出占一行。
样例输入
2
asdf
yu
rtyu
HJK
样例输出
asyudf
rtHJKyu
输入输出知识点
1、在获取两个字符串输入时,需要使用一个char变量接收其后的回车键
具体就是
char tmp;
...
tmp = getchar();
代码
#include<iostream>
#include<string>
using namespace std;
int main(){
int n;
char tmp;
cin >> n;
string a, b;
while(n--){
cin >> a;
tmp = getchar();
cin >> b;
string res = "";
for(int i = 0; i < a.size() / 2; ++i){//遍历,将a的前半部分加到res中
res += a[i];
}
res += b;
for(int i = a.size() / 2; i < a.size(); ++i){//处理后半部分
res += a[i];
}
cout << res << endl;
}
}
位置互换
题目描述
给定一个长度为偶数位的字符串,请编程实现字符串的奇偶位互换。
输入
输入包含多组测试数据。
输入的第一行是一个整数n,表示有测试数据。(整个输入中,只有一个n)
接下来是n组测试数据,保证串长为偶数位(串长<=50)。
输出
请为每组测试数据输出奇偶位互换后的结果,每组输出占一行。
样例输入
2
0aa0
bb00
样例输出
a00a
bb00
代码
#include<iostream>
#include<string>
using namespace std;
void swap(char &a, char &b) { // 交换两个字符串
char tmp = a;
a = b;
b = tmp;
}
int main(){
int n;
string s;
cin >> n;
while(n--){
cin >> s;
// for(int l = 0, r = s.size() - 1; l < r; l++, r--){//是相邻奇偶位交换
// swap(s[l], s[r]);
// }
for (int i = 0; i < s.size() - 1; i += 2) { // 在s字符串上原地修改
swap(s[i], s[i + 1]);
}
cout << s << endl;
}
}
出栈合法性
题目描述
已知自然数1,2,...,N(1<=N<=100)依次入栈,请问序列C1,C2,...,CN是否为合法的出栈序列。
输入
输入包含多组测试数据。
每组测试数据的第一行为整数N(1<=N<=100),当N=0时,输入结束。
第二行为N个正整数,以空格隔开,为出栈序列。
输出
对于每组输入,输出结果为一行字符串。
如给出的序列是合法的出栈序列,则输出Yes,否则输出No。
样例输入
5
3 4 2 1 5
5
3 5 1 4 2
0
样例输出
Yes
No
代码
使用一个vector接收输入的数组,方法跟前面的类似
创建一个栈,使用for将自然数1,2,...,N(1<=N<=100)模拟加入栈
同时比较当前的栈顶元素是否与vector中对应下标的一致,一致就弹出并移动索引
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
int main(){
int n;
vector<int> nums(105, 0);
while(cin >> n){
if(n == 0) break;
for(int i = 0; i < n; ++i) cin >> nums[i];
int index = 0;
stack<int> st;
for(int i = 1; i <= n; ++i){
st.push(i);
while(!st.empty() && st.top() == nums[index]){
st.pop();
index++;
}
}
if(st.empty() && index == n) cout << "Yes" <<endl;
else cout << "No" <<endl;
}
}
ACM中链表的基本操作
输入
输入数据只有一组,第一行有n+1个整数,第一个整数是这行余下的整数数目n,后面是n个整数。
这一行整数是用来初始化列表的,并且输入的顺序与列表中的顺序相反,也就是说如果列表中是1、2、3那么输入的顺序是3、2、1。
第二行有一个整数m,代表下面还有m行。每行有一个字符串,字符串是“get”,“insert”,“delete”,“show”中的一种。
如果是“get”,代表获得第a个元素。(a从1开始计数)
如果是“delete”,代表删除第a个元素。(a从1开始计数)
如果是“insert”,则其后跟着两个整数a和e,代表在第a个位置前面插入e。(a从1开始计数)
“show”之后没有正数,直接打印链表全部内容。
输出
如果获取成功,则输出该元素;
如果删除成功,则输出“delete OK”;
如果获取失败,则输出“get fail”
如果删除失败,则输出“delete fail”
如果插入成功,则输出“insert OK”,否则输出“insert fail”。
如果是“show”,则输出列表中的所有元素,如果列表是空的,则输出“link list is empty”
注:所有的双引号均不输出。
样例输入
3 3 2 1
21
show
delete 1
show
delete 2
show
delete 1
show
delete 2
insert 2 5
show
insert 1 5
show
insert 1 7
show
insert 2 5
show
insert 3 6
show
insert 1 8
show
get 2
样例输出
1 2 3
delete OK
2 3
delete OK
2
delete OK
Link list is empty
delete fail
insert fail
Link list is empty
insert OK
5
insert OK
7 5
insert OK
7 5 5
insert OK
7 5 6 5
insert OK
8 7 5 6 5
7
提示
初始化链表的元素是倒序的,这个使用题目中创建列表的方法(从头部插入)就可以了。
代码
下面为标答,我自己写的时候虽然都写出来了,但是总是解答错误
主要思路就是定义链表节点,然后直接按照以前的做法,依次实现链表获取指定节点、头插、尾插、指定位置插入、删除指定节点即可,最后根据题目的具体题意再调用各函数来实现所需的功能
#include<iostream>
using namespace std;
// 定义链表节点结构体
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val):val(val), next(nullptr){}
};
int _size = 0;
LinkedNode* _dummyHead = new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
// 获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点
int get(int index) {
if (index > (_size - 1) || index < 0) {
return -1;
}
LinkedNode* cur = _dummyHead->next;
while(index--){ // 如果--index 就会陷入死循环
cur = cur->next;
}
return cur->val;
}
// 在链表最前面插入一个节点,插入完成后,新插入的节点为链表的新的头结点
void addAtHead(int val) {
LinkedNode* newNode = new LinkedNode(val);
newNode->next = _dummyHead->next;
_dummyHead->next = newNode;
_size++;
}
// 在链表最后面添加一个节点
void addAtTail(int val) {
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(cur->next != nullptr){
cur = cur->next;
}
cur->next = newNode;
_size++;
}
// 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果index大于链表的长度,则返回空
// 如果index小于0,则在头部插入节点
int addAtIndex(int index, int val) {
if(index > _size) return -1;
if(index < 0) return -1;
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
return 0;
}
// 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的
int deleteAtIndex(int index) {
if (index >= _size || index < 0) {
return -1;
}
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur ->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
//delete命令指示释放了tmp指针原本所指的那部分内存,
//被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
//如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
//如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
tmp=nullptr;
_size--;
return 0;
}
// 打印链表
int printLinkedList() {
LinkedNode* cur = _dummyHead;
if (cur->next == nullptr) return -1;
while (cur->next != nullptr) {
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
return 0;
}
int main() {
int n, a, m, t, z;
string s;
cin >> n;
LinkedNode* head = nullptr;
while (n--) {
cin >> a;
addAtHead(a);
}
cin >> m;
while (m--) {
cin >> s;
if (s == "show") {
if (printLinkedList() == -1) cout << "Link list is empty" << endl;
}
if (s == "delete") {
cin >> t;
// 本题的删除索引是从1开始,函数实现是从0开始,所以这里是 t - 1
if (deleteAtIndex(t - 1) == -1) cout << "delete fail" << endl;
else cout << "delete OK" << endl;
}
if (s == "insert") {
cin >> t >> z;
if (addAtIndex(t - 1, z) == -1) cout << "insert fail" << endl;
else cout << "insert OK" << endl;
}
if (s == "get") {
cin >> t;
int getValue = get(t - 1);
if (getValue == -1) cout << "get fail" << endl;
else cout << getValue << endl;
}
}
}
构造单链表并反转
题目描述
根据一个整数序列构造一个单链表,然后将其反转。
例如:原单链表为 2 3 4 5 ,反转之后为5 4 3 2
输入
输入包括多组测试数据,每组测试数据占一行,第一个为大于等于0的整数n,表示该单链表的长度,后面跟着n个整数,表示链表的每一个元素。整数之间用空格隔开
输出
针对每组测试数据,输出包括两行,分别是反转前和反转后的链表元素,用空格隔开
如果链表为空,则只输出一行,list is empty
样例输入
5 1 2 3 4 5
0
样例输出
1 2 3 4 5
5 4 3 2 1
list is empty
提示
本题用数组,也是可有过的,输入一遍数组,然后倒叙输出。不过建议大家用本题来链表操作
代码
技巧点在于,如何处理输入的链表数据,如何存放
实际上处理这种输入我们只需要两个变量就可以了,一个是n,用于判断我们应该接收多少个节点值;另一个是nodeVal,用于循环接收输入的节点值,并直接用来构造链表。
#include<iostream>
using namespace std;
struct ListNode{
int val;
ListNode* next;
ListNode(int val): val(val), next(nullptr){}
};
ListNode* reverseList(ListNode* head){//双指针法
ListNode* pre = nullptr;
ListNode* cur = head;
while(cur){
ListNode* tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
void printListNode(ListNode* head){
ListNode* cur = head;
while(cur != nullptr){
cout << cur->val << ' ';
cur = cur->next;
}
cout << endl;
}
int main(){
int n, nodeVal;
ListNode* dummy = new ListNode(0);
while(cin >> n){
if (n == 0) {
cout << "list is empty" << endl;
continue;
}
ListNode* cur = dummy;
while(n--){
cin >> nodeVal;
ListNode* node4add = new ListNode(nodeVal);
cur->next = node4add;
cur = cur->next;
}
printListNode(dummy->next);
printListNode(reverseList(dummy->next));
}
}
删除单链表中的重复数据
题目描述
根据一个递增的整数序列构造有序单链表,删除其中的重复元素
输入
输入包括多组测试数据,每组测试数据占一行,第一个为大于等于0的整数n,表示该单链表的长度,后面跟着n个整数,表示链表的每一个元素。整数之间用空格隔开
输出
针对每组测试数据,输出包括两行,分别是删除前和删除后的链表元素,用空格隔开
如果链表为空,则只输出一行,list is empty
样例输入
5 1 2 3 4 5
5 1 1 2 2 3
0
样例输出
1 2 3 4 5
1 2 3 4 5
1 1 2 2 3
1 2 3
list is empty
代码
题目只是要一个输出,所以没有必要真的去删除某个节点,只需要在打印时跳过就行
#include<iostream>
using namespace std;
struct ListNode{
int val;
ListNode* next;
ListNode(int val): val(val), next(nullptr){}
};
// ListNode* removeDuplicate(ListNode* head){
// ListNode* cur = head;
// while(cur){
// ListNode* tmp = cur;
// cur = cur->next;
// if(cur->val == tmp->val){
// cur = tmp;
// cur->next = cur->next->next;
// }
// }
// return head;
// }
// 过滤掉重复元素输出,不用真的删除
void removeDuplicate(ListNode* head) {
ListNode* cur = head;
while (cur != nullptr && cur->next != nullptr) {
if (cur->val != cur->next->val) cout << cur->val << " ";
cur = cur->next;
}
cout << cur->val << " ";//最后一个节点不满足while没被输出,这里补上
cout << endl;
}
void printListNode(ListNode* head){
ListNode* cur = head;
while(cur){
cout << cur->val << " ";
cur = cur->next;
}
cout << endl;
}
int main(){
int n, nodeVal;
ListNode* dummy = new ListNode(0);
while(cin >> n){
if (n == 0) {
cout << "list is empty" << endl;
continue;
}
ListNode* cur = dummy;
while(n--){
cin >> nodeVal;
ListNode* node4add = new ListNode(nodeVal);
cur->next = node4add;
cur = cur->next;
}
printListNode(dummy->next);
//去重
removeDuplicate(dummy->next);
}
}