给定一个常数 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 (≤10^5)、以及正整数 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
思路:
本题难度略高,有两个测试用例不好通过,一是大量的节点数据,查找超时的问题,二是不一定所有的节点都是有效节点,对于这种情况需要删除无效的节点
例如这种:
00100 6 2
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 77777
12309 2 33218
在这个测试用例中,节点5的下个地址是77777,但是并没有节点的地址是77777,因此在链表中5之后的节点就应该要删除,最后的结果应该是2-1-4-3-5
image
另外本题还需要注意的是:对于最后小于K的节点,是不需要逆序的。例如这个测试用例,最后剩两个节点5,6,个数是小于4的,因此5,6就不需要逆序,最后的结果应该是4-3-2-1-5-6
image00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
而解决查找速度的问题,本人使用了< algorithm >库中的find_if函数
关于find_if函数,以后如有机会可以详细介绍一下,在这里的关键代码如下:
先定义判断函数:
class judge//对find_if算法的第三个函数参数的定义
{
private:
int address;//judge函数是对节点node a进行判断,判断其地址是不是int address
public:
judge(int add) :address(add){}//必须对该类进行构造函数的定义,由于在find_if中,第三个参数需要传入一个变量add,需要判断该变量与node中的地址是否相同
bool operator()(node a)//由于find_if第三个参数无法传入自己的node值,因此需要对操作符()进行重新定义,使其可以使用操作符来间接调用自己的值
{
return a.address == address;//判断judge(add),如果a与node a的address相同返回true
}
};
可以直接调用函数查找下一个节点:
vector<node>::iterator it;//先定义指针
it = find_if(L.begin(), L.end(), judge(first));
//找到地址为first的节点
以上是该题的两个难点的解决方法。
最后总结一下整个程序的思路
image
代码:
//1025 反转链表
//本题有两个难点,一是对于大量的节点数据,查找超时的问题,二是不一定所有的节点都是有效的节点,无效的节点应该删除
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct node//首先需要定义节点结构体
{
int address;
int data;
int next;
};
void exchange(node &a, node &b)//定义交换两个节点的函数
{
node temp;
temp = a;
a = b;
b = temp;
return;
}
class judge//对find_if算法的第三个函数参数的定义
{
private:
int address;//judge函数是对节点node a进行判断,判断其地址是不是int address
public:
judge(int add) :address(add){}//必须对该类进行构造函数的定义,由于在find_if中,第三个参数需要传入一个变量add,需要判断该变量与node中的地址是否相同
bool operator()(node a)//由于find_if第三个参数无法传入自己的node值,因此需要对操作符()进行重新定义,使其可以使用操作符来间接调用自己的值
{
return a.address == address;//判断judge(add),如果a与node a的address相同返回true
}
};
int main()
{
int first, N, K;
cin >> first >> N >> K;
//存入第一行的首地址,节点个数,已经每隔K反转的值
vector<node> L;//定义node数组L
node temp;//临时存放node的值
int data, address, next;//存放每一行的地址,数据,下一个地址
//将所有数据读取,然后每一行一次存入L中
for (int i = 0; i < N; i++)
{
scanf("%d %d %d", &address, &data, &next);
temp.address = address;
temp.data = data;
temp.next = next;
L.push_back(temp);
}
//至此,将所有数组存储完毕
vector<node>::iterator it;//定义数组指针
it = find_if(L.begin(), L.end(), judge(first));//找到地址为first的节点使用find_if()函数,注意第三个变量是一个函数,函数的定义在上面,这个函数可以传入参数first
exchange(*it, L[0]);//将地址为首地址的节点与L数组的第一个交换
//从第二个元素开始查找,找到最后一个元素,将所有的节点按照节点的顺序进行排序
for (int i = 1; i < N; i++)
{
it = find_if(L.begin() + i, L.end(), judge(L[i - 1].next));//从i的元素开始查找,查找地址为L[i-1]的下一个地址
if (it != L.end())//如果找到将其与L[i]的后一个节点进行交换
exchange(*it, L[i]);
else//如果没有找到,证明i-1节点后面断了,后面的元素需要都删除
{
L.erase(L.begin() + i, L.end());
break;
}
}
//将有效的节点进行题目要求转序
int count = L.size() / K;//计算需要转序的次数,例如有6个节点,K=4,只需要转序一次,K=2则需要转序3次
it = L.begin();
for (int i = 0; i < count; i++)//转序count次,每一次将it与it+K之间的进行转序
{
reverse(it, it + K);
it += K;
}
//转序完后将每一个节点的next地址改为下一个节点的地址,并将最后一个节点的next地址设为-1
for (int i = 0; i < L.size() - 1; i++)
{
L[i].next = L[i + 1].address;
}
L[L.size() - 1].next = -1;
//按照要求输出结果
for (int i = 0; i < L.size() - 1; i++)
{
printf("%05d %d %05d\n", L[i].address, L[i].data, L[i].next);
}
printf("%05d %d %d\n", (L.end() - 1)->address, (L.end() - 1)->data, (L.end() - 1)->next);
return 0;
}
网友评论