PTA的一道翻转链表题
没看题解前的思路与总结
- 根据题目样例,想到了模拟内存,但因为想节约空间(利用动态数组)模拟的不完全,把地址放进了结构体中(Ps:虽然代码还是没节约空间,有点懒)。由于设计的数据结构的缘故,算法时间复杂度为O(),数据量最大的例子超时了。
- 翻转的策略选择了:不断的交换两个节点的位置。采用了数组翻转的思路(忽略了单链表有一定规律,只需依次逆向节点的指向即可)
- 最后输出的时侯如何输出如:00010,这样的数字遇到了点困难,本考虑用字符数组存储字符串的形式,但在更改指向时由于字符数组为const的指针,逐放弃。
- 此外没有引入一个哑头节点,导致处理起来有点复杂。
#include <stdio.h>
#define N 100000
//"模拟"内存中的单链表节点
//逻辑上相连,物理地址上不相连
struct Node {
int address;
int data;
int next;
};
struct Node List[N];
int node_numbers = 0;//链表的节点数
void Read(int n);
int Reverse(int first_address, int length);
void Printf(int address);
int Find(int address);
int Find_last(int address);//找到节点的前驱(单链表的局限性)
int main() {
int first_address = 0;
int reverse_length = 0;
scanf("%d %d %d", &first_address, &node_numbers, &reverse_length);
int new_first_address =first_address;
Read(node_numbers);
if (reverse_length!=1)
{
new_first_address=Reverse(first_address,reverse_length);
}
Printf(new_first_address);
}
void Read(int n)
{
for (int i = 0; i < n; i++) {
scanf("%d %d %d", &List[i].address, &List[i].data, &List[i].next);
}
return;
}
int Reverse(int first_address, int length)
{
int count = node_numbers / length; //计算翻转的次数
int first=0;//头节点
int new_first_address = 0;//翻转后的链表头节点地址
//找到第一个节点
first = Find(first_address);
//翻转:通过不断的调换m,n的位置
//因为是单链表,所以需要m,n前一个节点的位置
int m = first;//m在前
int n = first;//n在后
int n_last = 0;
int m_last = 0;
int temp = 0;//临时记录
int read = 0;//记录下段翻转的m
//判断是否要翻转
if (count)
{
//第一段翻转的第一次交换(特殊性:头节点没有前节点)
//找到n
for (int i = 1; i < length; i++) {
n = Find(List[n].next);
}
n_last = Find_last(List[n].address);
// 记录翻转完后整个链表新的头节点
new_first_address = List[n].address;
//记录下段翻转的m
read = Find(List[n].next);
//length为2时的情况
if (m==n_last)
{
List[m].next = List[n].next;
List[n].next = List[m].address;
//一次交换完后就完成了该段的翻转,进入下一段翻转
m_last = m;
m = read;
n = Find(List[m].next);
n_last = m;
count--;
}
else
{
temp = List[n].next;
List[n].next = List[m].next;
List[n_last].next = List[m].address;
List[m].next = temp;
m_last = n;
m = Find(List[m_last].next);
n = n_last;
n_last = Find_last(List[n].address);
}
}
while (1)
{
//一个长度内的翻转
while (1)
{
//交换结束的条件
//1.m,n相临
if (m == n_last)
{
List[m].next = List[n].next;
List[n].next = List[m].address;
List[m_last].next = List[n].address;
break;
}
//2.m,n相等
if (m == n)
{
break;
}
//在链表逻辑中交换m,n节点
temp = List[n].next;
List[n].next = List[m].next;
List[m_last].next = List[n].address;
List[n_last].next = List[m].address;
List[m].next = temp;
//更新m,n以及它们的前一个节点
m_last = n;
m = Find(List[m_last].next);
n = n_last;
n_last = Find_last(List[n].address);
}
count--;
//进入下一段翻转,重新找到m,n
//如果read为-1或者count为0,则已完成整个链表的翻转
if (read==-1||count==0)
{
break;
}
m = read;
m_last = Find_last(List[m].address);
n = m;
//找到n
for (int i = 1; i < length; i++) {
//防止输入中有“多余”的节点
if(n==-1){
break;
}
n_last = n;
n = Find(List[n].next);
}
//防止输入中有“多余”的节点
if(n==-1){
break;
}
//记录下段翻转的m
read = Find(List[n].next);
}
return new_first_address;
}
void Printf(int address)
{
int i = 0;
while (1)
{
i = Find(address);
if(List[i].next!=-1){
printf("%05d %d %05d\n", List[i].address, List[i].data, List[i].next);
}else{
printf("%05d %d -1\n", List[i].address, List[i].data);
break;
}
address = List[i].next;
}
return;
}
int Find(int address)
{
//如果是最后一个节点
if (address == -1)
{
return -1;
}
//遍历查找
int result = 0;
for (int i = 0; i < node_numbers; i++) {
if (List[i].address == address) {
result = i;
break;
}
}
return result;
}
int Find_last(int address)
{
//遍历查找
int result = 0;
for (int i = 0; i < node_numbers; i++) {
if (List[i].next == address) {
result = i;
break;
}
}
return result;
}
看完题解后的笔记与总结
题解:数据结构_中国大学MOOC(慕课) (icourse163.org)
- 设计数据结构时应模拟一个真实的链表在内存里面存在的状态,开一个充分大的结构数组来代表内存空间,数组下标表示地址。
- 只在完全没规律的时侯通过交换结点来更改链表的结构如:排序。
#include<stdio.h>
#define N 100000
struct Node
{
int data;
int next;
};
struct Node List[N];
void Read(int numbers);
void Printf(int first);
int Reverse(int first, int length);
int main()
{
int first = 0;
int numbers = 0;
int length = 0;
scanf("%d %d %d", &first, &numbers, &length);
Read(numbers);
int new_first = first;
if (length!=1)
{
new_first=Reverse(first,length);
}
Printf(new_first);
return 0;
}
void Read(int numbers)
{
int address = 0;
for (int i = 0; i < numbers; i++) {
scanf("%d ", &address);
scanf("%d %d", &List[address].data, &List[address].next);
}
return;
}
void Printf(int first)
{
int address = first;
while (List[address].next!=-1)
{
printf("%05d %d %05d\n", address, List[address].data, List[address].next);
address = List[address].next;
}
printf("%05d %d -1", address, List[address].data);
return;
}
int Reverse(int first, int length)
{
int node_numbers = 1;//防止有多余的节点不在链表上
int address = first;
while (List[address].next!=-1)
{
node_numbers++;
address = List[address].next;
}
int count = node_numbers / length;//需要逆序的段数
struct Node head;//哑头节点
struct Node* point_head =&head;
point_head->next = first;
if (count)
{
//找到新的头节点
for (int i = 1; i < length; i++)
{
first = List[first].next;
}
}
int pre = 0;
int old = 0;
int temp = 0;
//翻转
while (count>0)
{
pre = point_head->next;
old = List[pre].next;
temp = List[old].next;
//一段内的逆向
for (int i = 1; i < length; i++)
{
List[old].next = pre;
pre = old;
old = temp;
if (temp!=-1)
{
temp = List[temp].next;
}
}
List[point_head->next].next = old;
temp = point_head->next;
point_head->next = pre;
point_head = &List[temp];
count--;
}
return first;
}