实验二:链表
7-1 两个有序链表序列的合并
分数 20
作者 DS课程组
单位 浙江大学
已知两个非降序链表序列S1与S2,设计函数构造出S1与S2合并后的新的非降序链表S3。
输入格式:
输入分两行,分别在每行给出由若干个正整数构成的非降序序列,用−1表示序列的结尾(−1不属于这个序列)。数字用空格间隔。
输出格式:
在一行中输出合并后新的非降序链表,数字间用空格分开,结尾不能有多余空格;若新链表为空,输出NULL
。
输入样例:
1 3 5 -1
2 4 6 8 10 -1
输出样例:
1 2 3 4 5 6 8 10
#include<stdio.h>
#include<stdlib.h>
struct node
{int d;struct node *next;
};
int main()
{struct node *h1,*h2,*t1,*t2,*p1,*p2;h1=(struct node*)malloc(sizeof(struct node));h1->next=NULL;t1=h1;while(1){p1=(struct node*)malloc(sizeof(struct node));scanf("%d",&p1->d);if(p1->d==-1){free(p1);break;}p1->next=NULL;t1->next=p1;t1=p1;}h2=(struct node*)malloc(sizeof(struct node));h2->next=NULL;t2=h2;while(1){p2=(struct node*)malloc(sizeof(struct node));scanf("%d",&p2->d);if(p2->d==-1){free(p2);break;}p2->next=NULL;t2->next=p2;t2=p2;}struct node *h,*t,*p;h=h1;p1=h1->next;p2=h2->next;t=h1;while(p1!=NULL&&p2!=NULL){if(p1->d<p2->d){t->next=p1;t=p1;p1=p1->next;}else{t->next=p2;t=p2;p2=p2->next;}if(p1!=NULL)t->next=p1;elset->next=p2;}p=h->next;if(p==NULL)printf("NULL");else{while(p!=NULL){if(p->next==NULL)printf("%d\n",p->d);elseprintf("%d ",p->d);p=p->next;}}return 0;
}
7-2 两个有序链表序列的交集
分数 20
作者 DS课程组
单位 浙江大学
已知两个非降序链表序列S1与S2,设计函数构造出S1与S2的交集新链表S3。
输入格式:
输入分两行,分别在每行给出由若干个正整数构成的非降序序列,用−1表示序列的结尾(−1不属于这个序列)。数字用空格间隔。
输出格式:
在一行中输出两个输入序列的交集序列,数字间用空格分开,结尾不能有多余空格;若新链表为空,输出NULL
。
输入样例:
1 2 5 -1
2 4 5 8 10 -1
输出样例:
2 5
#include<stdio.h>
#include<stdlib.h>
struct node
{int d;struct node *next;
};
int main()
{struct node *h1,*h2,*t1,*t2,*p1,*p2;h1=(struct node*)malloc(sizeof(struct node));h1->next=NULL;t1=h1;while(1){p1=(struct node*)malloc(sizeof(struct node));scanf("%d",&p1->d);if(p1->d==-1){free(p1);break;}p1->next=NULL;t1->next=p1;t1=p1;}h2=(struct node*)malloc(sizeof(struct node));h2->next=NULL;t2=h2;while(1){p2=(struct node*)malloc(sizeof(struct node));scanf("%d",&p2->d);if(p2->d==-1){free(p2);break;}p2->next=NULL;t2->next=p2;t2=p2;}struct node *h,*t,*p;h=(struct node*)malloc(sizeof(struct node));h->next=NULL;t=h;p1=h1->next;p2=h2->next;while(p1!=NULL&&p2!=NULL){if(p1->d==p2->d){p=(struct node*)malloc(sizeof(struct node));p->d=p1->d;p->next=NULL;t->next=p;t=p;p1=p1->next;p2=p2->next;}else if(p1->d<p2->d)p1=p1->next;elsep2=p2->next;}p=h->next;if(p==NULL)printf("NULL");else{int is_first = 1;while(p!=NULL){if(is_first){printf("%d",p->d);is_first = 0;}elseprintf(" %d",p->d);p=p->next;}}return 0;
}
7-3 重排链表
分数 20
作者 陈越
单位 浙江大学
给定一个单链表 L1→L2→⋯→Ln−1→Ln,请编写程序将链表重新排列为 Ln→L1→Ln−1→L2→⋯。例如:给定L为1→2→3→4→5→6,则输出应该为6→1→5→2→4→3。
输入格式:
每个输入包含1个测试用例。每个测试用例第1行给出第1个结点的地址和结点总个数,即正整数N (≤105)。结点的地址是5位非负整数,NULL地址用−1表示。
接下来有N行,每行格式为:
Address Data Next
其中Address
是结点地址;Data
是该结点保存的数据,为不超过105的正整数;Next
是下一结点的地址。题目保证给出的链表上至少有两个结点。
输出格式:
对每个测试用例,顺序输出重排后的结果链表,其上每个结点占一行,格式与输入相同。
输入样例:
00100 6
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
输出样例:
68237 6 00100
00100 1 99999
99999 5 12309
12309 2 00000
00000 4 33218
33218 3 -1
#include <stdio.h>
#include <stdlib.h>
// 定义节点结构:存储数据和下一个节点的地址
struct Node {int data;int next;
};
int main() {// 节点地址是5位非负整数,最大为99999,因此数组大小设为100000struct Node nodes[100000];// 初始化所有节点的next为-1(表示空地址)for (int i = 0; i < 100000; ++i) {nodes[i].next = -1;}int head, N;// 读取头节点地址和节点总数scanf("%d %d", &head, &N);// 读取N个节点,存入数组(下标=节点地址)for (int i = 0; i < N; ++i) {int addr, data, next;scanf("%d %d %d", &addr, &data, &next);nodes[addr].data = data;nodes[addr].next = next;}// 第一步:提取原链表的节点地址序列(按原顺序)int orig[N]; // 存储原链表的节点地址int len = 0; // 原链表长度(实际等于N)int curr = head;while (curr != -1) {orig[len++] = curr;curr = nodes[curr].next;}// 第二步:双指针法构建重排后的节点地址序列int new_arr[N]; // 存储重排后的节点地址int left = 0, right = len - 1; // 左右指针int idx = 0; // 新序列的下标int flag = 1; // 1:取右指针,0:取左指针(交替)while (left <= right) {if (flag) {new_arr[idx++] = orig[right--]; // 先取末尾节点} else {new_arr[idx++] = orig[left++]; // 再取开头节点}flag = !flag; // 切换指针方向}// 第三步:更新重排后每个节点的next地址for (int i = 0; i < len - 1; ++i) {// 当前节点的next = 下一个节点的地址nodes[new_arr[i]].next = new_arr[i + 1];}// 最后一个节点的next设为-1(空地址)nodes[new_arr[len - 1]].next = -1;// 第四步:按格式输出重排后的链表for (int i = 0; i < len; ++i) {int addr = new_arr[i];int data = nodes[addr].data;int next = nodes[addr].next;// 地址输出为5位,不足补0;next为-1时直接输出-1if (next == -1) {printf("%05d %d %d\n", addr, data, next);} else {printf("%05d %d %05d\n", addr, data, next);}}return 0;
}
7-4 约瑟夫环
分数 20
作者 李廷元
单位 中国民用航空飞行学院
N个人围成一圈顺序编号,从1号开始按1、2、3......顺序报数,报p者退出圈外,其余的人再从1、2、3开始报数,报p的人再退出圈外,以此类推。
请按退出顺序输出每个退出人的原序号。
输入格式:
输入只有一行,包括一个整数N(1<=N<=3000)及一个整数p(1<=p<=5000)。
输出格式:
按退出顺序输出每个退出人的原序号,数据间以一个空格分隔,但行尾无空格。
输入样例:
在这里给出一组输入。例如:
7 3
输出样例:
3 6 2 7 5 1 4
#include<stdio.h>
#include<stdlib.h>
struct node
{int d;struct node *next;
};
int main()
{int n,p;scanf("%d %d",&n,&p);struct node *h,*t,*q;h=(struct node*)malloc(sizeof(struct node));h=NULL;t=NULL;for(int i=1;i<=n;i++){q=(struct node*)malloc(sizeof(struct node));q->d=i;q->next=NULL;if(h==NULL){h=q;t=q;}else{t->next=q;t=q;}}t->next=h;struct node *current=h;struct node *prev=t;int c=0,isFirst=1;while(n>0){current=prev->next;c++;if(c==p){if(isFirst){printf("%d",current->d);isFirst=0;}elseprintf(" %d",current->d);prev->next=current->next;free(current);c=0;n--;}elseprev=prev->next;}return 0;
}
7-5 单链表的创建及遍历
分数 15
作者 陈晓梅
单位 广东外语外贸大学
读入n值及n个整数,建立单链表并遍历输出。
输入格式:
读入n及n个整数。
输出格式:
输出n个整数,以空格分隔(最后一个数的后面没有空格)。
输入样例:
在这里给出一组输入。例如:
2
10 5
输出样例:
在这里给出相应的输出。例如:
10 5
#include<stdio.h>
#include<stdlib.h>
struct node
{int d;struct node *next;
};
int main()
{int n;scanf("%d",&n);struct node *h,*t,*p;h=(struct node*)malloc(sizeof(struct node));h->next=NULL;t=h;for(int i=0;i<n;i++){p=(struct node*)malloc(sizeof(struct node));scanf("%d",&p->d);p->next=NULL;t->next=p;t=p;}p=h->next;while(p!=NULL){if(p->next==NULL)printf("%d",p->d);elseprintf("%d ",p->d);p=p->next;}return 0;
}
7-6 链表去重
分数 20
作者 陈越
单位 浙江大学
给定一个带整数键值的链表 L,你需要把其中绝对值重复的键值结点删掉。即对每个键值 K,只有第一个绝对值等于 K 的结点被保留。同时,所有被删除的结点须被保存在另一个链表上。例如给定 L 为 21→-15→-15→-7→15,你需要输出去重后的链表 21→-15→-7,还有被删除的链表 -15→15。
输入格式:
输入在第一行给出 L 的第一个结点的地址和一个正整数 N(≤105,为结点总数)。一个结点的地址是非负的 5 位整数,空地址 NULL 用 -1 来表示。
随后 N 行,每行按以下格式描述一个结点:
地址 键值 下一个结点
其中地址
是该结点的地址,键值
是绝对值不超过104的整数,下一个结点
是下个结点的地址。
输出格式:
首先输出去重后的链表,然后输出被删除的链表。每个结点占一行,按输入的格式输出。
输入样例:
00100 5
99999 -7 87654
23854 -15 00000
87654 15 -1
00000 -15 99999
00100 21 23854
输出样例:
00100 21 23854
23854 -15 99999
99999 -7 -1
00000 -15 87654
87654 15 -1
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// 定义节点结构:存储键值和下一个节点地址
struct Node {int key; // 节点的键值int next; // 下一个节点的地址(5位整数,-1表示空)
};
int main() {// 节点地址最大为99999,数组大小设为100000(下标=地址)struct Node nodes[100000];// 标记数组:记录某个绝对值是否已出现(键值绝对值≤1e4)int used[10001] = {0}; // 0=未出现,1=已出现int head, N;// 读取链表头地址和节点总数scanf("%d %d", &head, &N);// 第一步:读取所有节点,存入数组(下标=地址)for (int i = 0; i < N; ++i) {int addr, key, next;scanf("%d %d %d", &addr, &key, &next);nodes[addr].key = key;nodes[addr].next = next;}// 第二步:提取原链表的节点顺序(输入节点可能乱序,需按next指针排序)int orig[N]; // 存储原链表节点地址的顺序int orig_len = 0; // 原链表实际长度(等于N)int curr = head;while (curr != -1) {orig[orig_len++] = curr;curr = nodes[curr].next;}// 第三步:拆分去重后的链表(res)和被删除的链表(del)int res[N], del[N]; // 存储两个结果链表的节点地址int res_len = 0, del_len = 0; // 两个链表的长度for (int i = 0; i < orig_len; ++i) {int addr = orig[i];int abs_key = abs(nodes[addr].key); // 计算键值的绝对值if (used[abs_key] == 0) {// 绝对值未出现:加入去重后的链表,标记为已出现used[abs_key] = 1;res[res_len++] = addr;} else {// 绝对值已出现:加入被删除的链表del[del_len++] = addr;}}// 第四步:设置两个结果链表的next指针(确保最后一个节点指向-1)// 处理去重后的链表for (int i = 0; i < res_len - 1; ++i) {nodes[res[i]].next = res[i + 1]; // 当前节点的next指向后一个节点}if (res_len > 0) {nodes[res[res_len - 1]].next = -1; // 最后一个节点指向空}// 处理被删除的链表for (int i = 0; i < del_len - 1; ++i) {nodes[del[i]].next = del[i + 1];}if (del_len > 0) {nodes[del[del_len - 1]].next = -1;}// 第五步:按格式输出两个链表// 输出去重后的链表for (int i = 0; i < res_len; ++i) {int addr = res[i];int next = nodes[addr].next;// 地址格式化为5位,next为-1时直接输出,否则也格式化if (next == -1) {printf("%05d %d %d\n", addr, nodes[addr].key, next);} else {printf("%05d %d %05d\n", addr, nodes[addr].key, next);}}// 输出被删除的链表for (int i = 0; i < del_len; ++i) {int addr = del[i];int next = nodes[del[i]].next;if (next == -1) {printf("%05d %d %d\n", addr, nodes[addr].key, next);} else {printf("%05d %d %05d\n", addr, nodes[addr].key, next);}}return 0;
}
7-7 单链表就地逆置
分数 15
作者 usx程序设计类课程组
单位 绍兴文理学院
输入多个整数,以-1作为结束标志,顺序建立一个带头结点的单链表,之后对该单链表进行就地逆置(不增加新结点),并输出逆置后的单链表数据。
输入格式:
首先输入一个正整数T,表示测试数据的组数,然后是T组测试数据。每组测试输入多个整数,以-1作为该组测试的结束(-1不处理)。
输出格式:
对于每组测试,输出逆置后的单链表数据(数据之间留一个空格)。
输入样例:
1
1 2 3 4 5 -1
输出样例:
5 4 3 2 1
#include<stdio.h>
#include<stdlib.h>
struct node
{int d;struct node *next;
};
int main()
{int t;scanf("%d",&t);struct node *h,*p;for(int i=0;i<t;i++){h=(struct node*)malloc(sizeof(struct node));h->next=NULL;while(1){p=(struct node*)malloc(sizeof(struct node));scanf("%d",&p->d);if(p->d==-1)break;p->next=h->next;h->next=p;}p=h->next;while(p!=NULL){if(p->next==NULL)printf("%d\n",p->d);elseprintf("%d ",p->d);p=p->next;}}return 0;
}
7-8 带头节点的双向循环链表操作
分数 20
作者 achingz
单位 广西科技大学
本题目要求读入一系列整数,依次插入到双向循环链表的头部和尾部,然后顺序和逆序输出链表。
链表节点类型可以定义为
typedef int DataType;
typedef struct LinkedNode{DataType data;struct LinkedNode *prev;struct LinkedNode *next;
}LinkedNode;
链表类型可以定义为
typedef struct LinkedList{int length; /* 链表的长度 */LinkedNode head; /* 双向循环链表的头节点 */
}LinkedList;
初始化链表的函数可声明为
void init_list(LinkedList *list);
分配节点的函数可声明为
LinkedNode *alloc_node(DataType data);
头部插入的函数可声明为
void push_front(LinkedList *list, DataType data);
尾部插入的函数可声明为
void push_back(LinkedList *list, DataType data);
顺序遍历的函数可声明为
void traverse(LinkedList *list);
逆序遍历的函数可声明为
void traverse_back(LinkedList *list);
输入格式:
输入一行整数(空格分隔),以-1结束。
输出格式:
第一行输出链表顺序遍历的结果,第二行输出逆序遍历的结果。
输入样例:
在这里给出一组输入。例如:
1 2 3 4 5 6 -1
输出样例:
5 3 1 2 4 6
6 4 2 1 3 5
#include<bits/stdc++.h>
using namespace std;
struct node{int data;node * next;node *pre;
};
void puf(node *h, node *p)
{if (h->next == NULL) {h->next = p;p->next = h;p->pre = h;} else {p->next = h->next;h->next->pre = p;h->next = p;p->pre = h;}
}
void pub(node *h, node *p)
{if (h->next == NULL) {h->next = p;p->next = h;p->pre = h;} else {node *t = h->next;while (t->next != h) {t = t->next;}t->next = p;p->pre = t;p->next = h;h->pre = p;}
}
void shun(node *h)
{node *p = h->next;if (p != h) { // 添加判断条件,避免只有一个节点时的输出问题printf("%d", p->data);p = p->next;}while (p != h) {printf(" %d", p->data);p = p->next;}printf("\n");
}
void ni(node *h)
{node *p = h->pre;if (p != h) { // 添加判断条件,避免只有一个节点时的输出问题cout << p->data;p = p->pre;}while (p != h) {printf(" %d", p->data);p = p->pre;}
}
int main()
{int n, i = 0;node *h = new node;h->next = h->pre = h;while (~scanf("%d", &n) && n != -1){node *p = new node;p->data = n;if (i % 2 == 0) {puf(h, p);} else {pub(h, p);}i++;}shun(h);ni(h);return 0;
}
7-9 头插法创建单链表、遍历链表、删除链表
分数 20
作者 伍建全
单位 重庆科技大学
输入一系列自然数(0和正整数),输入-1时表示输入结束。按照输入的顺序,用头插法建立单链表,并遍历所建立的单链表,输出这些数据。注意 -1 不加入链表。
输入格式:
第一行是一个正整数k,表示以下会有k组测试数据。
每组测试数据是一系列以空格隔开的自然数(0和正整数)。数列末尾的 -1 表示本组测试数据结束。按照输入的顺序,用头插法建立单链表,并遍历所建立的单链表,输出这些数据。注意 -1 不加入链表。
输出格式:
对于每组测试数据,输出链表中各节点的数据域。每个数据后有一个空格。每组测试数据的输出占1行。
输入样例:
3
1 2 3 4 5 -1
30 20 10 -1
4 2 2 1 1 2 0 2 -1
输出样例:
在这里给出相应的输出。例如:
5 4 3 2 1
10 20 30
2 0 2 1 1 2 2 4
注意:对每组测试数据,创建链表,遍历链表输出之后,一定要删除链表,否则会出现“内存超限”。
#include<stdio.h>
#include<stdlib.h>
struct node
{int d;struct node *next;
};
void deleteList(struct node *h)
{struct node *t;while(h!=NULL){t=h;h=h->next;free(t);}
}
int main()
{int k;scanf("%d",&k);struct node *h,*p;for(int i=0;i<k;i++){h=(struct node*)malloc(sizeof(struct node));h->next=NULL;while(1){p=(struct node*)malloc(sizeof(struct node));scanf("%d",&p->d);if(p->d==-1){free(p);break;}p->next=h->next;h->next=p;}p=h->next;while(p!=NULL){printf("%d ",p->d);p=p->next;}printf("\n");deleteList(h);}return 0;
}