1问题链接:

https://www.patest.cn/contests/pat-b-practise/1025

2解题想法:

这题原来用数组打过,现在是想保留暂存数据的数组,然后按顺序提取出来到创建的链表里,再写一个传入反转首尾地址的链表反转函数来进行反转。

3GITHUB链接

具体的解决过程长而繁,这里直接给出代码链接,有稍加注释。然后主要想说一下反转的做法(其实是老师课件里的):举个例子来说,反转整个链表就是对数据首节点进行后删,把删掉的节点后插到head之后,也就是数据首节点之前,重复这个操作直到原来的数据首节点变成尾节点,这时反转也就完成了。

4具体过程:

这题这学期的c上机课上打过,当时自己是没有打出来,看着讲题同学的PPT也是折腾了好久才打出来。当初用的是数组,但是这次的主要目的是练习链表,所以我是打算在原来的代码基础上修改成存到链表里,然后在链表里反转后再把链表输出来。

然后毕竟原来没有打出来,所以还是大概说明一下模仿的ppt的想法:先把输入的数据存到一个结构体数组里,输入的时候用另外一个数组来标记题目里的地址(具体的可以看注释),根据标记数组把数据按照题目里的顺序存到另外一个结构体数组里,然后对这个排好序的数组进行反转的操作,最后再把这个数组输出来。

代码如下:

#include<stdio.h>
//定义了个结构体来存储数据
struct Node
{
int Address;
int Date;
int Next;
};
//记录题目里数据地址的标记数组
int mark[100000]={0};
//暂时的数据存储数组和实际操作输出的数组
struct Node node1[100000],node2[100000];
int main()
{
//用来进行反转操作的函数
void reverse(struct Node *p,int k);
int head,N,k;
//输入数据的首地址、数据个数、还有反转时用到的k
scanf("%d %d %d",&head,&N,&k);
int i,start;
//输入乱序的数据
for(i=0;i<N;i++)
{
scanf("%d %d %d",&node1[i].Address,&node1[i].Date,&node1[i].Next);
//标记数组的下标对应题目里的地址,元素则对应该地址数据在存储数组node1里的位置
mark[node1[i].Address]=i;
//记录第一个数据在存储数组中的位置
if(node1[i].Address==head) start=i;
}
//把数据按题目中要求的顺序存到node2数组里
i=0;
//先是start记录的第一个数据
node2[0]=node1[start];
//从第一个数据里获取第二个地址,也就是node[1].Next
//利用标记数组找到该地址的数据在node1数组中的位置,也就是mark[node[1].Next]
//把该数据整体赋值给node2[1],也就是按顺序的第二个数据
//接下来是第三个、第四个...直到出现题目中的结束地址-1
while(node2[i].Next!=-1)
{
i++;
node2[i]=node1[mark[node2[i-1].Next]];
}
int count=i;
i=0;
//对按题目要求排好序的node2数组进行反转操作
while(i+k<=count+1)
{
reverse(&node2[i],k);
i=i+k;
}
//按要求输出操作后的数据
for(i=0;i<count;i++)
{
//不足五位补零
//直接输出下一个数据的地址,反转的时候没有改变数据里的Next
printf("%05d %d %05d\n",node2[i].Address,node2[i].Date,node2[i+1].Address);
}
//结束的地址-1需要特判,它不能补零
printf("%05d %d -1",node2[count].Address,node2[count].Date);
return 0;
}
//用来进行反转的函数
//传入反转的起始地址和反转个数
void reverse(struct Node *p,int k)
{
int i;
struct Node t;
for(i=0;i<k/2;i++)
{
//改变的数组下标指向的数组元素,元素本身在内存里的储存位置没变
t=*(p+i);
*(p+i)=*(p+k-i-1);
*(p+k-i-1)=t;
}
}

然后这是原来的提交结果:

至少原来的是对的,所以修改的前提条件是满足了的,接下来就是怎么改成链表了。

想法是拆分成链表的创建,链表的输出,以及最主要的链表的反转等部分,其中链表的反转按照课件里的想法还用到了后插和后删操作(比如反转整个链表就是对第一个数据节点进行后删,然后把该节点后插到head后面,再对第一个后删再后插到head后面,直到原来的第一个数据节点变成最后一个,这时反转也就完成了),就是现在需要改成要传入反转的首尾地址。

主要的想法就是:保留原来暂时储存数据的结构体数组,然后用链表的创建函数从数组按顺序提取出数据到新建的节点里,并记录链表的长度,然后是把链表分成每K个一组,将每组的首尾地址传入链表反转的函数里,将其整个反转再接回去,最后输出。下面是一些链表的操作函数:

creat

在main函数里直接把head指向了题目要求的第一个节点,第一个节点不再单独申请空间。

(ps:后来发现没有头节点的后删和后插是会有问题的)

void creat(struct Node *head)
{
//定义个尾节点,用来连接新节点
//定义个新节点,用来申请新空间
struct Node *tail,*newp;
//大概是初始化
head->Next=NULL;
tail=head;
//创建链表
while(1)
{
//申请新节点
newp=(struct Node *)malloc(sizeof(struct Node));
//申请空间失败的报错
if(newp==NULL)
{
printf("overflow\n");
//整个程序暂停
exit(1);
}
//利用原来的方法将储存在数组里的数据存到新节点里
newp->Address=node[mark[tail->After]].Address;
newp->Date=node[mark[tail->After]].Date;
newp->After=node[mark[tail->After]].After;
newp->Next=NULL;
//把新节点接到链表里
tail->Next=newp;
//尾节点后移一位
tail=newp;
//输到尾地址-1结束
if(tail->After==-1)break;
}
}

deleteAfter

(ps:后来发现这个函数有错,执行的判断什么的,删除的是尾节点的话会出错)

struct Node *deleteAfter(struct Node *p)
{
//定义个结构体指针指向要删除的节点
struct Node *t=p->Next;
if(t->Next!=NULL)
{
//直接将p与后面的节点的后面的接起来
//达到后删的目的
//因为还要用到删除的节点,故并不释放空间
p->Next=t->Next;
p->After=t->Next->Address;
}
//返回被删除的节点
return t;
}

insertAfter

void insertAfter(struct Node *p,struct Node *newp)
{
//将新节点指向要接入的位置
newp->Next=p->Next;
newp->After=p->Next->Address;
//将接入位置的前一个节点指向新节点
//完成链表的插入
p->Next=newp;
p->After=newp->Address;
}

reverse

(ps:准确的说,首地址是要反转的节点的前一个节点的地址)

void reverse(struct Node *head,struct Node *tail)
{
//调用后删和后插操作
struct Node *deleteAfter(struct Node *p);
void insertAfter(struct Node *p,struct Node *newp);
//记录尾节点指向的节点地址
//用于判断循环的结束条件
int mark=tail->After;
//定义rear指向第一个数据节点
struct Node *rear=head,*del;
//大概是k为1的操作,也就是什么都不做
if(head==tail)
{
return;
}
//通过后删和后插来实现反转
while(rear->After!=mark)
{
del=deleteAfter(rear);
insertAfter(head,del);
}
}

链表的输出操作没有什么好说的,这里就不贴了。然后在尝试整个链表反转时发现了问题:创建链表的时候,head直接指向了数据首节点,然后反转的里面后插操作就会有问题,插到head之后就是插到数据首节点之后,然后就相当于删了首节点之后的一个节点之后又给接回去了,根本没变。。。所以现在是想创建的时候加个不存数据的头节点(大概头节点的存在意义就是这个吧),这样head的后插就相当于数据首节点的前插了(有想过前插,但好像那样链表要双向的,还要指向前一个,所以还是放弃了)。

(ps:后来发现课件有说头节点就是为了方便后删和后插,毕竟单向的链表(指向下一个元素)也是后插和后删好实现)

然后,还在改。。。2016.05.20

后来是把head指向了一个申请的结构体空间,这个结构体(也就是没有数据的头节点)的Next再指向储存第一个数据的结构体数组里的元素,然后又在creat,display,reverse等函数里修改了一下,下面是输出反转前后的链表的运行结果(先是尝试整个反转):

然后就是解决每k个节点反转的问题,主要想法就是创建链表的时候记录一下长度,看要反转几次,也就是调用几次reserve函数,每次改变一下要反转的首尾地址就好。大概代码如下:

(ps:也是有bug的代码,首尾地址的传递没有处理好)

int len,j;
struct Node *head,*ph,*pt,*p;
head=(struct Node *)malloc(sizeof(struct Node));
//头节点指向数据首节点
head->Next=&node[first];
//记录链表长度
len=creat(head);
//初始反转的首地址
ph=head;
//一共执行len/k次反转
for(i=0;i<len/k;i++)
{
p=ph;
for(j=0;j<k;j++)
{
p=p->Next;
}
//获取反转的尾地址
pt=p;
//调用函数进行反转
reverse(ph,pt);
//让下次反转的首地址为前一次的尾地址
ph=pt;
}

测试样例可以倒是可以过:

然后我就直接交了一发:

还是回去多试试几个样例好了。。。

折腾来折腾去(只会用输出来判断哪出错也是够了。。。),发现的bug是每组反转不能很好的衔接在一起,原来的首尾指向的数据反转后已经换了位置,然后下一轮的首尾地址按原来的赋值就会乱掉,首地址会是链表的第一个数据节点。举个例子来说:把样例中的k改为3,输出的结果会是3、4、1、2、5、6,第一遍的反转确实把链表变成了3、2、1、4、5、6,但是原来的尾节点3已经跑到了第一位,下一次反转的首地址就会变成3的地址,也会变成反转2、1、4,所以输出就会变成是3、4、1、2、5、6。

上面的bug改完之后,结果是停止运行。。。最后发现是后删操作有问题,就里面的执行判断有错,如果删的是尾节点,会崩溃的。

一番修改之后,这是提交结果:

心情真是复杂。。。我到底又漏掉了什么。看到群里说单节点什么的,随意改了一下都没调试就交上去:

额,起码知道哪里错了。然后又改了一下,终于对了(开心):

最终代码已上传GITHUB,这里就不贴了,这里再来一个传送门

2016.05.21

C++课堂作业二之反转链表的更多相关文章

  1. 课堂作业二 PAT1025 反转链表

    MyGitHub 终于~奔溃了无数次后,看到这个结果 ,感动得不要不要的::>_<:: 题目在这里 题目简述:该题可大致分为 输入链表 -> 链表节点反转 -> 两个步骤 输入 ...

  2. C++课堂作业_02_PAT1025.反转链表

    The 1st classwork of the C++ program 题目:PAT.1025.反转链表 github链接:Click Here mdzz,做完题目的第一感受= = 这道题的题意就是 ...

  3. 20155213 第十二周课堂作业MySort

    20155213 第十二周课堂作业MySort 作业要求 模拟实现Linux下Sort -t : -k 2的功能 参考 Sort的实现 提交码云链接和代码运行截图 初始代码 1 import java ...

  4. 2、java数据结构和算法:单链表: 反转,逆序打印, 合并二个有序链表,获取倒数第n个节点, 链表的有序插入

    什么也不说, 直接上代码: 功能点有: 1, 获取尾结点 2, 添加(添加节点到链表的最后面) 3, 添加(根据节点的no(排名)的大小, 有序添加) 4, 单向链表的 遍历 5, 链表的长度 6, ...

  5. 剑指Offer面试题:15.反转链表

    一.题目:反转链表 题目:定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点. 链表结点定义如下,这里使用的是C#描述: public class Node { public in ...

  6. python中使用递归实现反转链表

    反转链表一般有两种实现方式,一种是循环,另外一种是递归,前几天做了一个作业,用到这东西了. 这里就做个记录,方便以后温习. 递归的方法: class Node: def __init__(self,i ...

  7. 剑指offer 15:反转链表

    题目描述 输入一个链表,反转链表后,输出新链表的表头. 法一:迭代法 /* public class ListNode { int val; ListNode next = null; ListNod ...

  8. 【Java】 剑指offer(24) 反转链表

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头 ...

  9. 反转链表 Reverse Linked List

    2018-09-11 22:58:29 一.Reverse Linked List 问题描述: 问题求解: 解法一:Iteratively,不断执行插入操作. public ListNode reve ...

随机推荐

  1. [PY3]——内置数据结构(9)——线性结构与切片/命名切片slice()

    线性结构的总结 列表list  元组tuple  字符串str  bytes  bytearray的共同点: 都是顺序存储.顺序访问的: 都是可迭代对象: 都可以通过索引访问 线性结构的特征: 可迭代 ...

  2. IOS打开pdf文件

    下了一个打开pdf的第三方,就是打开之后不能缩放.今天上午修改了下试着可以让它能够缩放,在网上查了下,要实现代理方法,写了下,可调试的时候用两个手指不起作用,以为是写的有问题,最后问了下小伙伴.我也在 ...

  3. [转]RDL(C) Report Design Step by Step 3: Mail Label

    本文转自:http://www.cnblogs.com/waxdoll/archive/2006/09/02/493350.html Crystal Report在报表向导中提供了三种向导类型给用户进 ...

  4. 【C#高级】泛型(一)

    泛型,.net 2.0之后出现,基本只要代码中出现 ‘<>’ 尖括号就可以确定是泛型. 在2.0之前大多是使用Object来代替,因为所有类都是Object的派生类,根据继承的原理Obje ...

  5. EF Code-First数据迁移

    Code-First数据迁移  首先要通过NuGet将EF升级至最新版本. 新建MVC 4项目MvcMigrationDemo 添加数据模型 Person 和 Department,定义如下: usi ...

  6. Office 卸载问题(安装包的语言不受系统支持)

    本人系统Win7 这个问题搞了一下午.各种网站找解决办法.下载下来的都是一些垃圾软件. Win7以上调成兼容模式运行理论可行. 放上微软的解决方法: * 彻底卸载Office 2003: http:/ ...

  7. Spring_Spring与AOP_AspectJ基于XML的实现

    一.前置通知 import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.as ...

  8. Sprng IOC&AOP&事务梳理 (文章整理new)

    IOC <理解 IOC> <IOC 的理解与解释> 正向控制:传统通过new的方式.反向控制,通过容器注入对象. 作用:用于模块解耦. DI:Dependency Inject ...

  9. 记录LNMP环境彻底删除绑定域名及网站文件夹/文件的过程

    lnmp vhost del #删除绑定的域名 chattr -i /home/wwwroot/域名文件夹/.user.ini #解除文件安全限制 rm -rf /home/wwwroot/域名文件夹 ...

  10. js 数组转json,json转数组

    //数组转json串var arr = [1,2,3, { a : 1 } ];JSON.stringify( arr ); //json字符串转数组var jsonStr = '[1,2,3,{&q ...