PTA Reversing Linked List 思路分析及代码解析
一、前导
1. 需要掌握的知识
链表
2. 题目信息
- 题目来源:PTA / 拼题A
- 题目地址: Reversing Linked List
二、解题思路分析
1. 题意理解
- 输入数据
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
- 题意
(1)本题再次强调了,指针本质就是数据的存储地址
(2)根据输入数据创建一个链表,然后按要求反转,最后打印新链表
2. 思路分析
2.0 本题的解题思路和陈越老师讲的一致,使用模拟链表的方式求解
2.1 根据题意,可以用数组下标 模拟 内存地址 ,由于要存储下一个结点的地址,因此需要使用结构体数组
2.2 反转函数是AC的关键,如果没想清楚,建议先画图模拟一下
2.3 注意:输入数据中可能存在无效结点,例如下面的 00000 3 99999
00100 3 2
00000 3 99999
00100 1 12309
12309 2 -1
三、具体实现
1. 弯路和bug
对于反转操作,建议动手在纸上画一画,画出来后还是比较清晰的
2. 代码框架
2.1 采用的数据结构
结构体数组
#define MAX 1000000
struct Node
{
ElementType Data;
Address Next;
}a[MAX];
2.2 程序主体框架
程序伪码描述
int main()
{
1.根据录入数据创建一个链表
2.执行反转
3.打印输出
}
2.3 各分支函数
2.3.1 Reverse( ) 核心函数,整个流程用文字还是挺难描述的,还是上图啦(以6结点的链表,每次反转3个结点为例),大体原则就是:
(1)将结点分为Now,Next,Bak三类,Now表示已反转的结点,Next表示Now之后的结点,Bak表示Next之后的结点(用于保存位置,因为一旦反转,Next之后的结点就会失联)
(2)第一次反转比较特殊,需要记录链表的头地址
(3)如果想不清楚,一定要动手画图
typedef int Address;
Address Reverse(Address Position, int ReversedNumber)
{
Address Now, Next, Bak,PtrHead; //Bak=Next's Next
if (Flag) //Flag用于标记是否属于首次翻转,初始值为True
Now = Position;
else
{
Now = a[Position].Next;
PtrHead = a[Position].Next;
}
Next = a[Now].Next;
Bak = a[Next].Next;
while (--ReversedNumber) //执行翻转
{
a[Next].Next = Now;
Now = Next;
Next = Bak;
Bak = a[Next].Next;
}
if (Flag) //首次翻转
{
a[Position].Next = Next;
First = Now;
Flag = false;
return Position;
}
else //非首次
{
a[PtrHead].Next = Next; //本段链表首地址元素改变指向 //链表绕来绕去好烦啊
a[Position].Next = Now;
return PtrHead;
}
}
3. 完整编码
大家如有疑问或建议,欢迎留言 😃
#include <iostream>
using namespace std;
#define MAX 1000000
#define END -1
typedef int ElementType;
typedef int Address;
struct Node
{
ElementType Data;
Address Next;
}a[MAX];
Address Reverse(Address Position, int ReversedNumber);
void Print(int n);
int First, Number, ReversedNumber; //链表首地址、待输入的记录条数、翻转数
bool Flag = true; //记录是否首次翻转
int main()
{
cin >> First >> Number >> ReversedNumber;
Address Current,Next; ElementType Data;
Address Position; int Count=0;//Count 意味着 链表中的有效节点数
for (int i = 0; i < Number; i++) //存储输入数据
{
cin >> Current >> Data >> Next;
a[Current].Data = Data;
a[Current].Next = Next;
}
Position = First;
while (Position != END) //Number中可能存在无效结点,需要甄别出来,这是一个测试点
{
Count++;
Position = a[Position].Next;
}
int Mark = Count/ReversedNumber; //Mark是翻转链表的次数
Position = First;
while (Mark--) //翻转
Position = Reverse(Position, ReversedNumber);
Position = First;
while(Position !=END)//打印输出数据
{
Print(Position);
cout << " " << a[Position].Data << " ";
Print(a[Position].Next);
cout<< endl;
Position = a[Position].Next;
}
return 0;
}
Address Reverse(Address Position, int ReversedNumber)
{
Address Now, Next, Bak,PtrHead; //Bak=Next's Next
if (Flag)
Now = Position;
else
{
Now = a[Position].Next;
PtrHead = a[Position].Next;
}
Next = a[Now].Next;
Bak = a[Next].Next;
while (--ReversedNumber) //执行翻转
{
a[Next].Next = Now;
Now = Next;
Next = Bak;
Bak = a[Next].Next;
}
if (Flag) //首次翻转
{
a[Position].Next = Next;
First = Now;
Flag = false;
return Position;
}
else //非首次
{
a[PtrHead].Next = Next; //本段链表首地址元素改变指向 //链表绕来绕去好烦啊
a[Position].Next = Now;
return PtrHead;
}
}
void Print(int n)
{
int Scale = 10000;
if (n > 1)
{
while (n < Scale)
{
cout << "0";
Scale /= 10;
}
cout << n;
}
else if (n == 1)
cout << "00001";
else if (n == 0)
cout << "00000";
else if (n == END)
cout << "-1";
return;
}
210928 AC代码:如果使用链表的方法求解,需要通过画图理清楚翻转的步骤,首次翻转 和 非首次翻转代码存在差异(首次翻转需要记录头地址)
进步点:打印时使用了printf() 省时省力;进行了代码规划
待改进:耗费时间太长,大概用了90分钟;Reverse()函数代码准确性低、进行了较长时间的Debug;忘记计算有效结点
#include <iostream>
using namespace std;
#define MAX 100000
typedef int address;
struct Node
{
int Value;
address Next;
}a[MAX];
int Head, N, ReverseNum;
bool Flag = true;
address Reverse(int ReverseNum, int Start);
int main()
{
cin >> Head >> N >> ReverseNum;
address Addr,Next; int Value;
for (int i = 0; i < N; i++)
{
cin >> Addr >> Value >> Next;
a[Addr].Value = Value; a[Addr].Next = Next;
}
int Address = Head,ValidN=0 ;
while (Address != -1)
{
ValidN++;
Address = a[Address].Next;
}
int Many = ValidN / ReverseNum, Start = Head;
while (Many--)
Start = Reverse(ReverseNum, Start);
Address = Head;
while (a[Address].Next != -1)
{
printf("%05d %d %05d\n", Address, a[Address].Value, a[Address].Next);
Address = a[Address].Next;
}
printf("%05d %d -1", Address, a[Address].Value);
return 0;
}
address Reverse(int ReverseNum,int Start)
{
int R = ReverseNum - 1;
int Now, Next, Bak;
if (Flag)
Now = Start;
else
Now = a[Start].Next;
Next = a[Now].Next;
Bak = a[Next].Next;
while (R--)
{
a[Next].Next = Now;
Now = Next;
Next = Bak;
Bak = a[Next].Next;
}
if (Flag)
{
a[Start].Next = Next;
Head = Now;
Flag = false;
}
else
{
int tmp = a[Start].Next;
a[tmp].Next = Next;
a[Start].Next = Now;
Start = tmp;
}
return Start;
}