本文结合PTA专项练习带领读者掌握结构体,刷题为主注释为辅,在代码中理解思路,其它不做过多叙述。
7-1 一帮一
“一帮一学习小组”是中小学中常见的学习组织方式,老师把学习成绩靠前的学生跟学习成绩靠后的学生排在一组。本题就请你编写程序帮助老师自动完成这个分配工作,即在得到全班学生的排名后,在当前尚未分组的学生中,将名次最靠前的学生与名次最靠后的异性学生分为一组。
输入格式:
输入第一行给出正偶数N(≤50),即全班学生的人数。此后N行,按照名次从高到低的顺序给出每个学生的性别(0代表女生,1代表男生)和姓名(不超过8个英文字母的非空字符串),其间以1个空格分隔。这里保证本班男女比例是1:1,并且没有并列名次。
输出格式:
每行输出一组两个学生的姓名,其间以1个空格分隔。名次高的学生在前,名次低的学生在后。小组的输出顺序按照前面学生的名次从高到低排列。
输入样例:
8
0 Amy
1 Tom
1 Bill
0 Cindy
0 Maya
1 John
1 Jack
0 Linda
输出样例:
Amy Jack
Tom Linda
Bill Maya
Cindy John
#include <stdio.h> int main() { int n;scanf("%d",&n); int sex[n];char name[n][9]; for(int i=0;i<n;i++) { scanf("%d %s",&sex[i],name[i]); } int exist[n]={0}; for(int p=0;p<n/2;p++) { for(int q=n-1;q>=n/2;q--) { if(sex[p]!=sex[q]&&exist[p]==0&&exist[q]==0) { printf("%s %s\n",name[p],name[q]); exist[p]=exist[q]=1; } } } }
7-2 考试座位号
每个 PAT 考生在参加考试时都会被分配两个座位号,一个是试机座位,一个是考试座位。正常情况下,考生在入场时先得到试机座位号码,入座进入试机状态后,系统会显示该考生的考试座位号码,考试时考生需要换到考试座位就座。但有些考生迟到了,试机已经结束,他们只能拿着领到的试机座位号码求助于你,从后台查出他们的考试座位号码。
输入格式:
输入第一行给出一个正整数 N(≤1000),随后 N 行,每行给出一个考生的信息:准考证号 试机座位号 考试座位号。其中准考证号由 16 位数字组成,座位从 1 到 N 编号。输入保证每个人的准考证号都不同,并且任何时候都不会把两个人分配到同一个座位上。
考生信息之后,给出一个正整数 M(≤N),随后一行中给出 M 个待查询的试机座位号码,以空格分隔。
输出格式:
对应每个需要查询的试机座位号码,在一行中输出对应考生的准考证号和考试座位号码,中间用 1 个空格分隔。
输入样例:
4
3310120150912233 2 4
3310120150912119 4 1
3310120150912126 1 3
3310120150912002 3 2
2
3 4
输出样例:
3310120150912002 2
3310120150912119 1
#include <stdio.h> int main() { int n;scanf("%d",&n); char sfz[n][20];int sj[n],ks[n]; for(int i=0;i<n;i++) { scanf("%s",sfz[i]); scanf("%d %d",&sj[i],&ks[i]); } int m;scanf("%d",&m); int chaxun[m]; for(int i=0;i<m;i++) { scanf("%d",&chaxun[i]); int s=chaxun[i]; for(int j=0;j<n;j++) { if(s==sj[j]) { printf("%s %d\n",sfz[j],ks[j]); } } } }
7-3 新键表输出
有一个单向链表,头指针为L,结点的数据域是一个整数,将链表L中奇数值的结点重新组成一个新的链表NEW,并输出新建链表。
输入格式:输入若干个整数,以-1结束输入
输出格式:数值之间以一个空格分隔,最后一个数值后面没有空格
输入样例:
1 2 3 4 5 6 7 -1
输出样例:
1 3 5 7
#include <stdio.h> #include <stdlib.h> struct ListNode { int val; struct ListNode *next; }; struct ListNode* createLinkedList(int* arr, int n) { struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode)); struct ListNode* current = dummy; for (int i = 0; i < n; i++) { current->next = (struct ListNode*)malloc(sizeof(struct ListNode)); current->next->val = arr[i]; current->next->next = NULL; current = current->next; } return dummy->next; } void printLinkedList(struct ListNode* head) { while (head) { printf("%d%s", head->val, head->next ? " " : ""); head = head->next; } } struct ListNode* extractOddNodes(struct ListNode* head) { struct ListNode* oddDummy = (struct ListNode*)malloc(sizeof(struct ListNode)); struct ListNode* oddCurrent = oddDummy; struct ListNode* current = head; while (current) { if (current->val % 2 != 0) { oddCurrent->next = (struct ListNode*)malloc(sizeof(struct ListNode)); oddCurrent->next->val = current->val; oddCurrent->next->next = NULL; oddCurrent = oddCurrent->next; } current = current->next; } return oddDummy->next; } int main() { int arr[1000], n = 0; while (scanf("%d", &arr[n]), arr[n] != -1) n++; struct ListNode* head = createLinkedList(arr, n); struct ListNode* newHead = extractOddNodes(head); printLinkedList(newHead); return 0; }
7-4 可怕的素质
亚克星球上的人的素质跟我们福大人比起来,可以说是一个天上,一个地下了。他们去食堂吃饭的时候,很多人都不好好的排队,而是排在某个熟人的后面,或者直接就插到队伍的最前面。这让食堂的工作人员很是恼火。
输入格式:
第一行包含一个整数n(0<n<1000),表示有n个亚克星球上的人去食堂吃饭。
接下来n行,每行1个整数x,表示第i( i从1~n )个人排在了x的后面,当x为0时,表示第i个人排在了最前面。
输出格式:
输出仅一行,包括n个数,以空格作为分隔符,最后一个数的后面没有空格。表示这n个人最后是排成了怎样的队伍。
输入样例:
3
0
0
2
输出样例:
2 3 1
#include <stdio.h> #include <stdlib.h> struct node { //定义链表的结构体 int val; //存放节点的值 struct node* next; //存放指向下一个节点的指针 }; int main() { int pos; //存放输入中每个数的值 int n; //存放输入中的第一个数,即链表的长度 scanf("%d", &n); //读入链表的长度 struct node *head, *p1, *cur; //定义链表的头指针、新节点指针和当前节点指针 for (int i = 1; i <= n; i++) { //循环读入并创建每个节点 scanf("%d", &pos); p1 = (struct node*)malloc(sizeof(struct node)); //动态分配新节点的内存空间 p1->val = i; //为新节点赋值 p1->next = NULL; //新节点指向下一个节点的指针设为空 if (i == 1) { //如果是第一个节点,则将头指针指向新节点 head = p1; } else if (pos == 0) { //如果插入位置为0,则将新节点插入到链表的开头 p1->next = head; head = p1; } else { //否则将新节点插入到链表的指定位置 cur = head; while (cur->val != pos) { //查找指定位置的节点 cur = cur->next; } p1->next = cur->next; //将新节点连接到指定位置的节点后面 cur->next = p1; } } cur = head; while (cur != NULL) { //输出链表中每个节点的值,每个值之间用空格隔开 printf("%d", cur->val); if (cur->next != NULL) { printf(" "); } cur = cur->next; } return 0; }
7-5 找出同龄者
最近,小明开始班级大搜索,他已经知道了班级里每个人的年龄,并且把他们排成了一队,他将从排好队的人里按顺序把和自己同龄的人找出来,并请这些同龄人出队。
输入格式:
第一行输入一个N(0<n<100)表示有N个学生;
接下来有N行,每行两个数据,分别表示某个同学的名字和年龄;
最后一行包含一个整数,表示小明的年龄。
输出格式:
输出仅一行,包含若干学生的姓名,姓名与姓名之间用空格分隔,最后一个名字的末尾没有空格。表示小明的同龄者出队后,队伍中学生的排序。
输入样例:
3
张三 20
李四 22
王五 15
22
输出样例:
张三 王五
#include <stdio.h> int main() { int n;scanf("%d",&n);int count=0; char name[n][20];int age[n]; for(int i=0;i<n;i++) { scanf("%s %d",&name[i],&age[i]); } int k;scanf("%d",&k); for(int i=0;i<n;i++) { if(k==age[i]) { age[i]=-1; count++; } } int j=0; for(int i=0;i<n;i++) { if(age[i]!=-1&&j<n-count-1) { printf("%s ",name[i]); j++; } else if(age[i]!=-1&&j==n-count-1) { printf("%s",name[i]); } } }
7-6 排队
晨跑小组决定从今天开始晨跑了,为了使晨跑的队伍看上去比较整齐,于是小组成员决定按身高从高到低排成一列。现在已知晨跑小组每个成员的身高。小组成员们都想知道自己排在第几位,前面和后面相邻的人分别是谁。请编写程序帮助他们实现吧。
输入格式:
输入第一行包括一个正整数N(1<=N<=1000),表示晨跑小组的人数。
接下来一行有N个浮点数Ai,Ai表示第i个人的身高(1<=i<=N)。(数据中没有两个人身高相同的情况)。
输出格式:
输出N行,每行有三个数据Ri,Fi,Bi。
Ri表示第i个人排在第几位。
Fi表示第 i个人前面相邻人的编号,如果前面没有人,输出0。
Bi表示第i个人后面相邻人的编号,如果后面没有人,输出0。
输入样例:
5
178.5
177.4
165.4
164.6
173.3
输出样例:
1 0 2
2 1 5
4 5 4
5 3 0
3 2 3
#include<stdio.h> #include<stdlib.h> typedef struct student student; struct student{ student *prior; //前驱指针 student *next; //后继指针 int ri, bi, fi,ret; //排名、前一名、后一名和原始编号 double height; //身高 student *arret; //按插入顺序形成的链表 }; student* create(int n){ student *head, *p, *q; head = (student*)malloc(sizeof(student)); head->prior = NULL; head->next = NULL; head->arret = NULL; for (int i = 1; i <= n;i++){ q = (student*)malloc(sizeof(student)); scanf("%lf", &q->height); //输入身高 q->ret = i; //设置原始编号 if(i==1){ head->next = q; head->arret = q; q->arret = NULL; q->prior = head; q->next = NULL; }else { p->next = q; p->arret = q; q->arret = NULL; q->prior = p; q->next = NULL; } p = q; } return head; } void swap(student *p1,student *p2){ student *p1f, *p1b, *p2f, *p2b; p1f = p1->prior; p1b = p1->next; p2f = p2->prior; p2b = p2->next; p1f->next = p1b; p1b->prior = p1f; p1b->next = p2f; p2f->prior = p1b; p2f->next = p2b; if(p2b!=NULL) p2b->prior = p2f; } int main(){ int n; scanf("%d", &n); //输入节点个数 student *stu1; stu1=create(n); //创建双向链表 student *p = stu1->next; for (int i = 1; i < n;i++){ p = stu1->next; for (int j = 1; j < n - i + 1;j++){ if(p->height<p->next->height){ swap(p,p->next); //按身高降序排序 } else p = p->next; } } p = stu1->next; for (int i = 1; i <= n;i++){ if(i==1){ p->fi = 0; p->bi = p->next->ret; }else if(i==n){ p->bi = 0; p->fi = p->prior->ret; }else { p->fi = p->prior->ret; p->bi = p->next->ret; } p->ri = i; p = p->next; } p = stu1->arret; for (int i = 1; i <= n;i++){ printf("%d %d %d\n", p->ri, p->fi, p->bi); //输出节点的相关信息 p = p->arret; } return 0; }
7-7 军训
某部队进行新兵队列训练,将新兵从1开始按顺序依次编号,并排成一行横队。训练的规则如下:从头开始1至2报数,凡报到2的新兵出列,剩下的新兵向小序号方向靠拢;再从头开始进行1至3报数,凡报到3的新兵出列,剩下的向小序号方向靠拢;继续从头开始进行1至2报数。。。;如此这样一直进行,直到剩下的人数不超过3人为止。
输入格式:
第一行输入一个整数T(0<T<100),表示共有T组新兵进行训练;
接下来有T行,每行输入一个整数N(0<N<=5000),代表这组新兵的人数。
输出格式:
输出共T行,每行按照编号从小到大的顺序输出每组新兵进行训练后剩下的新兵编号,编号之间以空格分隔,每行末尾有一个换行符。
输入样例:
2
20
40
输出样例:
1 7 19
1 19 37
#include <iostream> #define MAXSIZE 5001 using namespace std; typedef struct queue { int data[MAXSIZE]; int front; int rear; int capacity; } Queue; void InitQueue(Queue *q) { q->front = 0; q->rear = 0; q->capacity = 0; } void EnterQueue(Queue *q, int &data) { if (MAXSIZE != q->rear) { q->data[++q->rear] = data; q->capacity++; } } int OuterQueue(Queue *q) { int data; if (q->front != q->rear) { data = q->data[++q->front]; q->capacity--; } return data; } int main() { int N, m, data, m1, m2; Queue q1, q2; cin >> N; // 输入测试用例的数量 while (N--) { InitQueue(&q1); InitQueue(&q2); bool isq1 = true; cin >> m; // 输入初始队列的长度 for (int i = 1; i <= m; i++) EnterQueue(&q1, i); // 初始化队列q1 while (q1.capacity > 3 || q2.capacity > 3) // 当队列q1或q2的长度大于3时执行操作 { if (isq1) // 当前操作在队列q1上执行 { m1 = q1.capacity; // 记录当前队列q1的长度 for (int i = 1; i <= m1; i++) { if (i % 2 != 0) // 每个奇数位置的元素出队列并进入q2 { data = OuterQueue(&q1); EnterQueue(&q2, data); } else OuterQueue(&q1); // 偶数位置的元素直接出队列 } isq1 = false; // 切换操作到队列q2上 InitQueue(&q1); // 清空队列q1 } else // 当前操作在队列q2上执行 { m2 = q2.capacity; // 记录当前队列q2的长度 for (int i = 1; i <= m2; i++) { if (i % 3 != 0) // 每个非3的倍数位置的元素出队列并进入q1 { data = OuterQueue(&q2); EnterQueue(&q1, data); } else OuterQueue(&q2); // 3的倍数位置的元素直接出队列 } isq1 = true; // 切换操作到队列q1上 InitQueue(&q2); // 清空队列q2 } } if (isq1) // 打印最终的队列 { cout << q1.data[1]; for (int i = 2; i <= q1.capacity; i++) cout << " " << q1.data[i]; cout << endl; } else { cout << q2.data[1]; for (int i = 2; i <= q2.capacity; i++) cout << " " << q2.data[i]; cout << endl; } } return 0; }
7-8 双链表
实现一个双链表,双链表初始为空,支持 5 种操作:
1.在最左侧插入一个数;
2.在最右侧插入一个数;
3.将第 k 个插入的数删除;
4.在第 k 个插入的数左侧插入一个数;
5.在第 k 个插入的数右侧插入一个数
现在要对该链表进行 M 次操作,进行完所有操作后,从左到右输出整个链表。
注意:题目中第 k 个插入的数并不是指当前链表的第 k 个数。例如操作过程中一共插入了 n 个数,则按照插入的时间顺序,这 n 个数依次为:第 1 个插入的数,第 2 个插入的数,…第 n 个插入的数。
输入格式:
第一行包含整数 M,表示操作次数。
接下来 M 行,每行包含一个操作命令,操作命令可能为以下几种:
1.L x,表示在链表的最左端插入数 x。
2.R x,表示在链表的最右端插入数 x。
3.D k,表示将第 k 个插入的数删除。
4.IL k x,表示在第 k 个插入的数左侧插入一个数。
5.IR k x,表示在第 k 个插入的数右侧插入一个数。
输出格式:
共一行,将整个链表从左到右输出。
数据范围:
1≤M≤100000
所有操作保证合法。
输入样例:
在这里给出一组输入。例如:
10
R 7
D 1
L 3
IL 2 10
D 3
IL 2 7
L 8
R 9
IL 4 7
IR 2 2
输出样例:
在这里给出相应的输出。例如:
8 7 7 3 2 9
#include<bits/stdc++.h> using namespace std; const int N =1e5+10; int e[N],r[N],l[N],idx; void init() { // 初始化,0是左端点,1是右端点 r[0] = 1; l[1] = 0; idx = 2; } void add_to_right(int k,int x) { e[idx] = x; r[idx] = r[k]; l[idx] = k; l[r[k]] = idx; r[k] = idx++; } void remove(int k) { r[l[k]] = r[k]; l[r[k]] = l[k]; } int main() { int m; init(); scanf("%d",&m); while(m--) { string c; cin >> c; if(c == "L") { int x; scanf("%d",&x); add_to_right(0,x); } else if(c == "R") { int x; scanf("%d",&x); add_to_right(l[1],x); } else if(c == "D") { int k; scanf("%d",&k); remove(k + 1); } else if(c == "IL") { int k,x; scanf("%d%d",&k,&x); add_to_right(l[k + 1],x); } else { int k,x; scanf("%d%d",&k,&x); add_to_right(k + 1,x); } } for(int i = r[0]; i != 1; i = r[i]) { printf("%d ",e[i]); } printf("\n"); return 0; }