目录
说明:
1110 区块反转
输入格式:
输出格式:
输入样例:
输出样例:
1105 链表合并
输入格式:
输出格式:
输入样例:
输出样例:
1025 反转链表
输入格式:
输出格式:
输入样例:
输出样例:
1075 链表元素分类
输入格式:
输出格式:
输入样例:
输出样例:
7-11 链表去重
输入格式:
输出格式:
输入样例:
输出样例:
L2-022 重排链表
输入格式:
输出格式:
输入样例:
输出样例:
总结:
此文章对pat上有关链表的一类题做了一个总结(主要来自乙级和天梯赛训练题集),以下题目解题思路均借鉴永远有多远博主的博客(可以通过下面链接去博主里面看各个题目)L2-002 链表去重 (25 分) C++_永远有多远.的博客-CSDN博客_链表去重 c++查了很多题解,个人认为这个思路是新颖易理解的,用结构体数组来模拟链表,在链表中比较难实现的操作利用数组就很容易实现,故总结于此,并加上自己对代码的理解(可见注释)。
给定一个单链表 L,我们将每 K 个结点看成一个区块(链表最后若不足 K 个结点,也看成一个区块),请编写程序将 L 中所有区块的链接反转。例如:给定 L 为 1→2→3→4→5→6→7→8,K 为 3,则输出应该为 7→8→4→5→6→1→2→3。
每个输入包含 1 个测试用例。每个测试用例第 1 行给出第 1 个结点的地址、结点总个数正整数 N (≤105)、以及正整数 K (≤N),即区块的大小。结点的地址是 5 位非负整数,NULL 地址用 −1 表示。
接下来有 N 行,每行格式为:
Address Data Next
其中 Address
是结点地址,Data
是该结点保存的整数数据,Next
是下一结点的地址。
对每个测试用例,顺序输出反转后的链表,其上每个结点占一行,格式与输入相同。
00100 8 3
71120 7 88666
00000 4 99999
00100 1 12309
68237 6 71120
33218 3 00000
99999 5 68237
88666 8 -1
12309 2 33218
71120 7 88666
88666 8 00000
00000 4 99999
99999 5 68237
68237 6 00100
00100 1 12309
12309 2 33218
33218 3 -1
AC:
#include
using namespace std;
const int N=1e5+10;
struct node
{
int address;
int data;
int next;
}s[N];
int main()
{//输入
int start,n,k;
cin>>start>>n>>k;
for(int i=0;i>addr;
cin>>s[addr].data>>s[addr].next;
}
vector v;//数组来存地址
for(int i=start;i!=-1;i=s[i].next)
v.push_back(i);//遍历链表,将地址存到数组中
for(int i=0;i
给定两个单链表 L1=a1→a2→⋯→an−1→an 和 L2=b1→b2→⋯→bm−1→bm。如果 n≥2m,你的任务是将比较短的那个链表逆序,然后将之并入比较长的那个链表,得到一个形如 a1→a2→bm→a3→a4→bm−1⋯ 的结果。例如给定两个链表分别为 6→7 和 1→2→3→4→5,你应该输出 1→2→7→3→4→6→5。
输入首先在第一行中给出两个链表 L1 和 L2 的头结点的地址,以及正整数
N (≤105),即给定的结点总数。一个结点的地址是一个 5 位数的非负整数,空地址 NULL 用 -1
表示。
随后 N 行,每行按以下格式给出一个结点的信息:
Address Data Next
其中 Address
是结点的地址,Data
是不超过 105 的正整数,Next
是下一个结点的地址。题目保证没有空链表,并且较长的链表至少是较短链表的两倍长。
按顺序输出结果链表,每个结点占一行,格式与输入相同。
00100 01000 7
02233 2 34891
00100 6 00001
34891 3 10086
01000 1 02233
00033 5 -1
10086 4 00033
00001 7 -1
01000 1 02233
02233 2 00001
00001 7 34891
34891 3 10086
10086 4 00100
00100 6 00033
00033 5 -1
AC:
#include
using namespace std;
const int N = 1e5+10;
struct node
{
int address;
int data;
int next;
}s[N];
int main()
{//输入
int start1, start2, n;
cin >> start1 >> start2 >> n;
for (int i = 0; i < n; i++) {
int addr;
cin >> addr;
cin >> s[addr].data >> s[addr].next;
}
//设两个数组来存两个链表的地址
vectorv1, v2;
for (int i = start1; i != -1; i = s[i].next) {
v1.push_back(i);
}
for (int i = start2; i != -1; i = s[i].next) {
v2.push_back(i);
}
if (v1.size() < v2.size()) swap(v1, v2);//将v1一直为较长的那条链表
if (v1.size() >= v2.size() * 2) reverse(v2.begin(), v2.end()); //如果 n≥2m(v1的长度大于v2的),你的任务是将比较短的那个链表逆序(逆序仍利用的reverse)
//长的链表两个元素之后插入一个短链表中的元素
int cnt = 0, tmp = 0;
for (int i = 0; i < v1.size(); i++) {//遍历长链表
cnt++;
if (cnt == 3 && tmp < v2.size()) {//在长链表的两个元素之后也就是第三个位置插入
cnt = 0;//赋值为0,重新开始计数
v1.insert(v1.begin() + i, v2[tmp++]);//insert函数,v2中的元素插入相应位置
}
}
//n=2m的情况,tmp不等于v2.size(),也就省最后一个了直接把那元素压入到v1数组最后
if (tmp != v2.size()) v1.push_back(v2[tmp++]);
//遍历合并后的链表进行输出
for (int i = 0; i < v1.size() - 1; i++) {
printf("%05d %d %05d\n", v1[i], s[v1[i]].data, v1[i + 1]);
}
printf("%05d %d -1\n", v1[v1.size() - 1], s[v1[v1.size() - 1]].data);
return 0;
}
给定一个常数 K 以及一个单链表 L,请编写程序将 L 中每 K 个结点反转。例如:给定 L 为 1→2→3→4→5→6,K 为 3,则输出应该为 3→2→1→6→5→4;如果 K 为 4,则输出应该为 4→3→2→1→5→6,即最后不到 K 个元素不反转。
每个输入包含 1 个测试用例。每个测试用例第 1 行给出第 1 个结点的地址、结点总个数正整数 N (≤105)、以及正整数 K (≤N),即要求反转的子链结点的个数。结点的地址是 5 位非负整数,NULL 地址用 −1 表示。
接下来有 N 行,每行格式为:
Address Data Next
其中 Address
是结点地址,Data
是该结点保存的整数数据,Next
是下一结点的地址。
对每个测试用例,顺序输出反转后的链表,其上每个结点占一行,格式与输入相同。
00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68237
68237 6 -1
AC:
#include
using namespace std;
const int N = 1e5+10;
struct node
{
int address;
int data;
int next;
}s[N];
int main()
{//输入
int start, n, k;
cin >> start >> n >> k;
for (int i = 0; i < n; i++) {
int addr;
cin >> addr;
cin >> s[addr].data >> s[addr].next;
s[addr].address = addr;
}
vector v;//遍历链表,将地址存到vector数组
for (int i = start; i != -1; i = s[i].next) {
v.push_back(i);
}
//v.size()-v.size()%k来控制有几个k来旋转,i+=k表示k个一旋转
for (int i = 0; i < (v.size() - v.size() % k); i += k) {
reverse(v.begin() + i, v.begin() + i + k);//i=0开始,左闭右开 所以是+i+k
}
//输出
for (int i = 0; i < v.size(); i++) {
if (i != v.size() - 1) {
printf("%05d %d %05d\n", v[i], s[v[i]].data, v[i + 1]);
}
else {
printf("%05d %d -1\n", v[i], s[v[i]].data);
}
}
return 0;
}
给定一个单链表,请编写程序将链表元素进行分类排列,使得所有负值元素都排在非负值元素的前面,而 [0, K] 区间内的元素都排在大于 K 的元素前面。但每一类内部元素的顺序是不能改变的。例如:给定链表为 18→7→-4→0→5→-6→10→11→-2,K 为 10,则输出应该为 -4→-6→-2→7→0→5→10→18→11。
每个输入包含一个测试用例。每个测试用例第 1 行给出:第 1 个结点的地址;结点总个数,即正整数N (≤105);以及正整数K (≤103)。结点的地址是 5 位非负整数,NULL 地址用 −1 表示。
接下来有 N 行,每行格式为:
Address Data Next
其中 Address
是结点地址;Data
是该结点保存的数据,为 [−105,105] 区间内的整数;Next
是下一结点的地址。题目保证给出的链表不为空。
对每个测试用例,按链表从头到尾的顺序输出重排后的结果链表,其上每个结点占一行,格式与输入相同。
00100 9 10
23333 10 27777
00000 0 99999
00100 18 12309
68237 -6 23333
33218 -4 00000
48652 -2 -1
99999 5 68237
27777 11 48652
12309 7 33218
33218 -4 68237
68237 -6 48652
48652 -2 12309
12309 7 00000
00000 0 99999
99999 5 23333
23333 10 00100
00100 18 27777
27777 11 -1
AC:
#include
using namespace std;
const int N=1e5+10;
struct node
{
int data;
int address;
int next;
}s[N];
int main()
{//输入
int start1,n,k;
cin>>start1>>n>>k;
for(int i=0;i>addr;
cin>>s[addr].data>>s[addr].next;
}
vector v1;//用数组来存链表中的地址
for(int i=start1;i!=-1;i=s[i].next)
{
if(s[i].data<0)//按照顺序进行遍历 如果是负数就先放入数组中(也就是链表里)
v1.push_back(i);
}
for(int i=start1;i!=-1;i=s[i].next)
{
if(s[i].data<=k&&s[i].data>=0)//按照顺序进行遍历,如果数值在[0,k]之间在后面依次放进数组里
v1.push_back(i);
}
for(int i=start1;i!=-1;i=s[i].next)
{
if(s[i].data>k)//按照顺序进行遍历,最后将大于k的数值放在k后面
v1.push_back(i);
}
//输出
for(int i=0;i
给定一个带整数键值的链表 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
AC:
#include
using namespace std;
const int MAXN = 100000;
struct node
{
int data, next;
}nodep[MAXN];
int flag[MAXN] = { 0 };
int main()
{//输入
int node1, n;
cin >> node1 >> n;
for (int i = 0; i < n; i++) {
int temp;
cin >> temp;
cin >> nodep[temp].data >> nodep[temp].next;
}
memset(flag, 0, sizeof(flag));//初始化为0
vectorv1, v2;//定义两个数组来存两个链表
for (int i = node1; i != -1; i = nodep[i].next) {
if (flag[abs(nodep[i].data)] == 0) {//flag数组来进行标记,flag=0,只有第一个绝对值等于 K 的结点被保留
flag[abs(nodep[i].data)] = 1;//标记为1,剩下的就存到新的链表
v1.push_back(i);//将不重复的点存到v1中
}
else {
v2.push_back(i);//将绝对值重复的结点地址存到另一条链表中
}
}
//输出不重复的链表
printf("%05d %d ", v1[0], nodep[v1[0]].data);
for (int i = 1; i < v1.size(); i++) {
printf("%05d\n%05d %d ", v1[i], v1[i], nodep[v1[i]].data);
}
printf("-1\n");
//如果有绝对值相同的,链表长度不为0,则输出另一条链表
if (v2.size()) {
printf("%05d %d ", v2[0], nodep[v2[0]].data);
for (int i = 1; i < v2.size(); i++) {
printf("%05d\n%05d %d ",v2[i], v2[i], nodep[v2[i]].data);
}
printf("-1\n");
}
}
给定一个单链表 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
AC:
#include
using namespace std;
const int MAXN = 100000;
struct node
{
int address, data, next;
}nodep[MAXN];
int main()
{//输入
int address1, n;
cin >> address1 >> n;
for (int i = 0; i < n; i++) {
int address;
cin >> address;
cin >> nodep[address].data >> nodep[address].next;
nodep[address].address = address;
}
vectors, ans;//将题目要求的结点的地址顺序存在ans中,后面按题目要求统一输出
for (int i = address1; i != -1; i = nodep[i].next) {
s.push_back(i);//遍历链表时按照题目要求进行遍历 并将每个结点的地址放入s中
}
int l = 0, r = s.size() - 1;//l, r表示将要输出的结点位置,
// 当(r + 1) - (l - 1) == 1 时 已经都遍历了一遍 此时退出循环
while (1) {//先输出最右边 输出之后r--往左移 再输出最左边,输出后 l++
ans.push_back(s[r]);//将按照题目顺序遍历的地址存到答案链表中
r--;
if ((r + 1) - (l - 1) == 1) break;
ans.push_back(s[l]);
l++;
if ((r + 1) - (l - 1) == 1) break;
}
//输出答案链表
for (int i = 0; i < ans.size(); i++) {
if (i != ans.size() - 1) {
printf("%05d %d %05d\n", ans[i], nodep[ans[i]].data, ans[i + 1]);
}
else {
printf("%05d %d -1\n", ans[i], nodep[ans[i]].data);
}
}
}
这一类问题开头存储变量方式以及输入都是如下
#include
using namespace std;
const int MAXN = 100000;
struct node
{
int address, data, next;//其实结构体里只有data和next就行 后面就用不到结构体里的address了,有新的变量去存储了
}nodep[MAXN];
int main()
{//根据题目进行相关输入
int address1, n;
cin >> address1 >> n;
for (int i = 0; i < n; i++) {
int address;
cin >> address;
cin >> nodep[address].data >> nodep[address].next;
nodep[address].address = address;
}
vectors, ans;//
for (int i = address1; i != -1; i = nodep[i].next) {//遍历链表
s.push_back(i)
输出为如下:
for (int i = 0; i < ans.size(); i++) {
if (i != ans.size() - 1) {
printf("%05d %d %05d\n", ans[i], nodep[ans[i]].data, ans[i + 1]);
}//%05d用来填充,ans[i]里面存储的是本元素的地址,nodep[ans[i]].data存储本元素数据值,ans[i+1]存下一个地址
else {
printf("%05d %d -1\n", ans[i], nodep[ans[i]].data);
}
}
然后重要的是中间的操作,要根据不同的题目要求做出相应的操作。