PAT 1025反转链表的代码实现及错误分析(C语言)

题目

给定一个常数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

注意陷阱:输入可能存在非链表内数据,需要剔除,最后一定是NULL

实现思路1:

1.定义结构体,包含3个整数(5位地址最后可加0输出),定义两个指定大小结构体数组link1和link2;

2.将数据存入数组link1,利用下个地址变量next将链表按顺序存入link2,同时计算数量也剔除了无效数据;

3.判断总数和块大小的关系,将反转之后的链表存入link1;

4.输出

代码如下:

#include<stdio.h>

//定义结构体
typedef struct{
  int iAddr;
  int iData;
  int iNext;
} LINK;

int main()
{
  int next=0,iL=0,iK=0;
  scanf("%d%d%d",&next,&iL,&iK);
  LINK link1[iL+1],link2[iL+1];
  int iCnt=0;
  //存入数据到link1 
  for(int i=0;i<iL;i++)
  {
    scanf("%d%d%d",&link1[i].iAddr,&link1[i].iData,&link1[i].iNext);
  }
  //按地址衔接理顺链表存入link2,需要每次进行遍历,较费时间 
  do
  {
    for(int i=0;i<iL;i++)
    {
      if(next==link1[i].iAddr)
      {
        link2[iCnt]=link1[i];
        break;
      }
    }
    next=link2[iCnt].iNext;
    iCnt++;
  }while(next!=-1);
  //比较有效链表大小和反转块的大小关系,按需反转 
  int iNumofK=iCnt/iK;
  int iOther=iCnt%iK;
  if(iOther!=0)
  {
    //拷贝最后无法反转的链表数据 
	for(int i=iK*iNumofK;i<iL;i++)
    {
      link1[i]=link2[i];
    }
    //从后往前循环反转数据块 
    for(int i=iNumofK;i>0;i--)
    {
      for(int j=0;j<iK;j++)
      {
        link1[i*iK-1-j]=link2[i*iK-iK+j];
        link1[i*iK-1-j].iNext=link1[i*iK-j].iAddr;
      }
    }
  }
  else
  {
  	//若刚好所有数据尽皆反转,则设置一个尾数据地址,因此定义数组时多加一位 
    link1[iK*iNumofK].iAddr=-1;
    for(int i=iNumofK;i>0;i--)
    {
      for(int j=0;j<iK;j++)
      {
        link1[i*iK-1-j]=link2[i*iK-iK+j];
        link1[i*iK-1-j].iNext=link1[i*iK-j].iAddr;
      }
    }
  }
  //循环输出 
  for(int i=0;i<iCnt-1;i++)
  {
    printf("%05d %d %05d\n",link1[i].iAddr,link1[i].iData,link1[i].iNext);
  }
  printf("%05d %d -1",link1[iCnt-1].iAddr,link1[iCnt-1].iData);
  return 0;
}

实现思路2:考虑地址是五位数,可建立大小为100000的数组,在第一遍存数据的时候,就把地址作为下标存入这个数组中。一切输入都存储完毕后,根据初始地址,一个一个在地址数组中查找对应的值(数组按下标查找比数组循环遍历效率高太多),并将其存入结构体数组中(结构体有三个变量:address,data,next),最后使用思路1翻转,输出结构体数组。

代码如下:

#include<stdio.h>

struct node
{
    int add,data,next;
}a[100010],b[100010],c[100010];//a按地址下标存放输入数据,b依次存放顺序链表,c 
 
int main()
{
    int beginadd=0,n=0,k=0;
    int i=0,j=0,index=0;
    scanf("%d%d%d",&beginadd,&n,&k);
    //按地址下标存放输入数据 
    for(i = 0;i < n;i++)
    {
        scanf("%d",&index);
        scanf("%d %d",&a[index].data,&a[index].next);
    }
    //依次存放顺序链表 
    int num=0 
    for(i = beginadd;i != -1;i = a[i].next)
    {
        b[num].add = i;
        b[num].data = a[i].data;
        b[num].next = a[i].next;
        num++;
    }
    int s=num/k,y= num%k;//s为反转块数量,y为不反转链表节点数量 
    //按需反转,无需考虑next地址,因为顺序正确,next地址就是下一个节点的add 
	int pos = 0;
    for(i = 1;i <= s;i++)
    {
        for(j = i*k-1;j >= (i-1)*k;j--)
        {
            c[pos].add = b[j].add;
			c[pos].data = b[j].data;
			pos++;
        }
    }
    if(y != 0)
    {
        for(i = s*k;i < num;i++)
        {
            c[pos].add = b[i].add;
			c[pos].data = b[i].data;
			pos++;
        }
    }
 
	for(int k= 0; k < pos - 1; k++) {
		printf("%05d %d %05d\n", c[k].add, c[k].data, c[k+1].add);
	}
	printf("%05d %d -1\n", c[pos-1].add, c[pos-1].data);
    return 0;
}

错误分析:在一开始的思考中,如果把地址作为字符数组,则难度飙升,思路1的实现中,有测试点显示运行超时,思路2比思路1好的地方在于空间换时间,虽然开辟内存比较大, 但是省却第一次遍历排序链表,同时利用排好序的链表节点关系,直接忽略掉结构体中的next元素,将链表反转即可输出相关地址。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值