【区块反转】【链表合并】【反转链表】【链表元素分类】【链表去重】【重排链表】

目录

说明:

1110 区块反转

输入格式:

输出格式:

输入样例:

输出样例:

1105 链表合并

输入格式:

输出格式:

输入样例:

输出样例:

1025 反转链表

输入格式:

输出格式:

输入样例:

输出样例:

1075 链表元素分类

输入格式:

输出格式:

输入样例:

输出样例:

7-11 链表去重

输入格式:

输出格式:

输入样例:

输出样例:

L2-022 重排链表

输入格式:

输出格式:

输入样例:

输出样例:

总结:


说明:

此文章对pat上有关链表的一类题做了一个总结(主要来自乙级和天梯赛训练题集),以下题目解题思路均借鉴永远有多远博主的博客(可以通过下面链接去博主里面看各个题目)L2-002 链表去重 (25 分) C++_永远有多远.的博客-CSDN博客_链表去重 c++查了很多题解,个人认为这个思路是新颖易理解的,用结构体数组来模拟链表,在链表中比较难实现的操作利用数组就很容易实现,故总结于此,并加上自己对代码的理解(可见注释)。

1110 区块反转

给定一个单链表 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<bits/stdc++.h>
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;
    }
    vector<int> v;//数组来存地址
    for(int i=start;i!=-1;i=s[i].next)
        v.push_back(i);//遍历链表,将地址存到数组中
    for(int i=0;i<v.size();i+=k) //先部分旋转,i+=k表示k个一旋转
        reverse(v.begin()+i,min(v.begin()+i+k,v.end()));//用reverse来选转,k个一旋转,如果最后不满足k个,则到结尾有几个旋转几个
    reverse(v.begin(),v.end());//再整体一旋转 就到达了效果
//输出
    for(int i=0;i<v.size()-1;i++)
        printf("%05d %d %05d\n",v[i],s[v[i]].data,v[i+1]);//%05d用来填充,v[i]里面存储的是本元素的地址,s[v[i]].data存储本元素数据值,v[i+1]存下一个地址
    printf("%05d %d -1\n",v[v.size()-1],s[v[v.size()-1]].data);
    
}

1105 链表合并

给定两个单链表 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<bits/stdc++.h>
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;
	}
//设两个数组来存两个链表的地址
	vector<int>v1, 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;
}

1025 反转链表

给定一个常数 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<bits/stdc++.h>
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<int> 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;
}

1075 链表元素分类

给定一个单链表,请编写程序将链表元素进行分类排列,使得所有负值元素都排在非负值元素的前面,而 [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<bits/stdc++.h>
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<n;i++)
    {
        int addr;
        cin>>addr;
        cin>>s[addr].data>>s[addr].next;
    }
    vector<int> 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<v1.size()-1;i++)
        printf("%05d %d %05d\n",v1[i],s[v1[i]].data,v1[i+1]);
    printf("%05d %d -1",v1[v1.size()-1],s[v1[v1.size()-1]].data);

}

7-11 链表去重

给定一个带整数键值的链表 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 <bits/stdc++.h>
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
	vector<int>v1, 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");
	}
}

L2-022 重排链表

给定一个单链表 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<bits/stdc++.h>
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;
	}
	vector<int>s, 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 <bits/stdc++.h>
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;
	}
	vector<int>s, 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);
		}
	}

然后重要的是中间的操作,要根据不同的题目要求做出相应的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值