1025 反转链表

给定一个常数 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 (≤105)、以及正整数 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

第一次的代码:迭代,但是出现最后两个测试点段错误和答案错误;

/*
这里结点格式,选用两个字符串一个整型,因为这里地址5位整数,有可能不足5位,用
int输入不方便,直接用字符串输入,比较;然后就是反转,题目输入格式为首地址
结点个数,然后就是翻转结点数K;链表翻转时就是第一个不动,随后K-1个翻转,
然后变成了前K个结点逆序的链表,加后N-K个结点顺序的两个链表,如果K<N就需要将
后面的链表链接在前一个链表的后面;

例:结点及第一个结点地址:
00100
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218

链表:
00100 1 12309
12309 2 33218
33218 3 00000
00000 4 99999
99999 5 68327
68327 6 -1    //这里末尾结点next指针指向空-1;

k=4,k<6;
翻转:
00100 1 12309 找下一个结点,12039
12309 2 33218 然后翻转,
这里第一个结点next变为空,00100 1 -1;
然后第二个结点指向上一个结点; 12309 2 00100
这里发现33218 没了,所以我们用一个next变量存放翻转结点的下一个结点地址
即next=33218;
然后就变成;
00100 1 -1
12309 2 00100    //这里翻转一次,已经是两个结点了;
然后根据next=33218找到下一个结点
00100 1 -1
12309 2 00100
33218 3 00000
这里进行翻转,先存放33218的下一个结点 next=00000;
然后00000变为12309,但是链表之间已经断了,3这个结点指向4而非指向2
所以应该用一个来存放之前的地址,pre=12309,00000=pre,于是变成:
00100 1 -1
12309 2 00100
33218 3 12309
然后到这里pre变成33218,移到下一个结点 根据next==00000找到00000 4 99999
00100 1 -1
12309 2 00100
33218 3 12309
00000 4 99999
根据pre==33218,更新next为99999,然后99999赋值为33218:
00100 1 -1
12309 2 00100
33218 3 12309
00000 4 33218
到这里翻转了4个结点,只需要迭代3次;剩下链表:
99999 5 68327
68327 6 -1

此时pre=00000,next=99999;

将翻转后链表按头结点排序,方便看:
00000 4 33218
33218 3 12309
12309 2 00100
00100 1 -1

------------

99999 5 68327
68327 6 -1
这里将我们一开始应该输入一个头结点地址,head,翻转之后,head的位置应该是末尾
然后将链表链接,head->Next=next,这样就变成00100 1 99999
于是新链表:
00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68327
68327 6 -1


这样就是K<N的一种翻转过程;如果K==N;就不需要进行链表拼接这一步;直接全部翻转即可
补充:
这里注意是每K个翻转一次,每K个翻转一次;读题一半开写,以为是前K个翻转;这里就需要计数
了,这里;

看下标;
0123456789
0123456789
这里K=3时,2105438769
因为翻转只看链表后K-1个,就是从1到2,在2进行翻转之后就结束
0<-1<-2 3->4->5->6->7->8->9
这里输出210
然后从3开始,3布翻转,就是从4到4,在5进行翻转之后就结束
2105438769
所以这里需要一个变量标志后部分链表是否需要翻转;
每次局部翻转的边界是3 6 9;就是K*n,n=1,2,3,4;

0->1->2->3->4->5->6->7->8->9,k=3;
第一次翻转:
0<-1-<-2   3->4->5->6->7->8->9
这里翻转之后,0应该拼接到下一个翻转链表的头结点;
第二次翻转:
2->1->0    3<-4<-5     6->7->8->9
这里0应该指向第二个头结点5  于是:
2->1->0->5->4->3      6->7->8->9
第三次翻转:
2->1->0->5->4->3      6<-7<-8    9
这里3指向下一个链表的头结点;
2->1->0->5->4->3->8->7->6  9
然后这里9不足3个不用翻转;所以尾结点6指向9;
结果:
2->1->0->5->4->3->8->7->6->9;
变成这种形态需要移动数组,但是可以通过局部倒序输出实现,不改变数组;
但是移动数组也有一个优点,就是拼接的时候很方便;

然后看翻转过程;

1 2 3 4 5 6 7 8
0 1 2 3 4 5 6 7   k=3;
index从0开始,数3个,然后翻转后面2(k-1)个;
翻转之后index指向下标2;,下一次index++,让index指向下一个头结点下标3,
然后index+K-1就是满足可以翻转的下标,K-1就是头结点后面个;
如果index+K-1<=7,也就是在下标之内,就可以进行翻转;
index+K-1<=N-1;-->index+K<=N;

这里每次都时从index到index+K-1这样一组来的,当index==0时
就是0-2这k个;index==3,3-5这k个,边界为<index+K;

到这里为止都是用的迭代的方式实现反转,但是不知道为什么,最后两个测试点段错误和答案错误;

然后学习了别人的代码,局部反转,通过这个下标来实现next的功能,然后逆序通过下标对应
交换实现反转,很有创意的想法,在数组中确实时很方便的操作;

边界:
奇数时下标:012;中间不交换,于是是中间的前一个,一共K个,K/2==3/2==1,
01234 K=5,K/2==5/2==2;
这里就是下标小于K/2;
偶数时下标:01,无中间;K=2;K/2==2/2==1;也是小于K/2;

比如:
1 2 3 4 5 6 7
0 1 2 3 4 5 6
这里k=3;要反转的段数是N/K==7/3=2;反转前两段;

第一段;
这里用的是交换实现逆序,用一个变量存放第一次交换位置的数据;
temp=data(0);
data(0)=data(2);
data(2)=temp;        //这里就实现了交换逆序;
然后这里前半段与后半段交换实现逆序,

第二段:
第二段起始位置是3;
temp=data(3);
data(3)=data(5);
data(5)=tmep;

起始位置下标就是,这里考虑到用两层循环,一层描述反转的段数;
一层描述段内的反转;反转段数循环变量暂定为i,内层循环为j;
那么反转开始的下标就应该就是 i*K+j;j每次从0开始;
两段:
第一段:i=0,j=0;
下标:0==i*K+j==0*3+0==0
第二段:i=1,j=0;
下标:3==i*K+j==1*3+0==3
这里i*K是外层段开始的位置,j是内部的循环;这是前半段的下标
然后看后半段的下标;这里i=0,j=0时,前段下标为0,后段下标为2
i=1,j=0时,前端下标为K==3,后段下标为5
段下标为K*(i+1)-1,然后j往后移动,那么后段往前,应该是-j;
后端下标:K*(i+1)-1-j;
K*i+K-1-j;

最后反转完成通过下标找到下一个,然后将地址赋值给next域,就实现了结点的链接;
这里升序的下标就是链表的暗链;因为这样翻转后的下一个结点一定是当前结点的后继
结点;


*/
#include<iostream>
#include<string>
using namespace std;
struct LNode{
    string Address;
    int Data;
    string Next;
};
typedef struct LNode Node;
int main(){
    //输入头结点地址,结点数N,以及翻转结点数K;
    string head;    //注意这一结点地址是字符类型;
    int N,K;
    cin>>head>>N>>K;
    //输入结点信息;
    struct LNode data[N];
    for(int i=0;i<N;i++){
        cin>>data[i].Address>>data[i].Data>>data[i].Next;
    }
    struct LNode List[N];    //存放链接后链表;
    //构建链表;
    string pre=head;
    string next=head;
    int index=0;    //index指示下标;
    while(next!="-1"){    //找到最后一个结点结束;最后一个结点的标志是Next域为“-1”;
        for(int i=0;i<N;i++){    //查找与next地址相等的结点,
            if(next==data[i].Address){
                List[index++]=data[i];    //index为0时next指向head;
                next=data[i].Next;        //next指向当前结点的下一个;
                break;
            }
        }
    }
    //翻转;
    index=0;    //index走到N-1;即走完链表结束;
    int count=0;    //翻转计数器,用计数器方便index操作;作为翻转的边界;
    int thisList;    //当前尾节点;下标;
    while(index+K<=N){    //这里注意有一个等于,当index+K等于N时,下标为N-1;可翻转;
        count=index+K;    //翻转边界;
        //局部翻转;翻转时index指向第一个;
        pre=List[index].Address;    //这里pre指向第一个结点;
        next=List[index].Next;        //然后从第二个开始翻转;
        for(int i=index;i<count;i++){
            if(next==List[i].Address){    //这里next一开始就指向index的下一个;
                next=List[i].Next;
                List[i].Next=pre;
                pre=List[i].Address;
            }
        }
        //完成本次翻转,我们尾结点指向应该指向下一个翻转的部分链表的头结点;
        //或者指向不反转的末尾的头结点,或者是全翻转时改为-1;我们优先改为-1随后赋值;
        //那么这里就需要一个变量存放本次翻转的尾结点地址,以及下次翻转后的头结点地址;
        index+=K;    //翻转结束后指向下一个链表头结点;
        thisList=index-K;    //存放上一个已翻转链表尾结点;
        List[thisList].Next="-1";    //默认设置-1;
        if(index+K<=N){    //如果后面还有需要翻转的部分;则将上一个链表与这个链表进行拼接;
            List[thisList].Next=List[index+K-1].Address;
        }
        else if(index+K>N&&index<N){    //最后一部分不需要翻转;
            List[thisList].Next=List[index].Address;
        }
    }
    //输出;
    //找到头结点向前输出,这是K<N的情况;
    if(K<N){
        //输出翻转的部分链表;
        count=K;    //输出的边界;
        while(count<=N){
            for(int i=count-1;i>=count-K;i--){
                cout<<List[i].Address<<" "<<List[i].Data<<" "<<List[i].Next<<endl;
            }
            count+=K;
        }
        //输出剩下的链表;
        for(int i=count-K;i<N;i++){
            //这里i=count-K,因为上面while循环倒序在这里跳出;
            //虽然没有进行输出,但是还是加了K,这里是顺序输出;
            cout<<List[i].Address<<" "<<List[i].Data<<" "<<List[i].Next<<endl;
        }
    }
    else if(N==K){    //完全翻转;
        //全部倒序输出;
        for(int i=N-1;i>=0;i--){
            cout<<List[i].Address<<" "<<List[i].Data<<" "<<List[i].Next<<endl;
        }
    }
        return 0;
}

第二次AC的代码:

/*
这里结点格式,选用两个字符串一个整型,因为这里地址5位整数,有可能不足5位,用
int输入不方便,直接用字符串输入,比较;然后就是反转,题目输入格式为首地址
结点个数,然后就是翻转结点数K;链表翻转时就是第一个不动,随后K-1个翻转,
然后变成了前K个结点逆序的链表,加后N-K个结点顺序的两个链表,如果K<N就需要将
后面的链表链接在前一个链表的后面;

例:结点及第一个结点地址:
00100
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218

链表:
00100 1 12309
12309 2 33218
33218 3 00000
00000 4 99999
99999 5 68327
68327 6 -1    //这里末尾结点next指针指向空-1;

k=4,k<6;
翻转:
00100 1 12309 找下一个结点,12039
12309 2 33218 然后翻转,
这里第一个结点next变为空,00100 1 -1;
然后第二个结点指向上一个结点; 12309 2 00100
这里发现33218 没了,所以我们用一个next变量存放翻转结点的下一个结点地址
即next=33218;
然后就变成;
00100 1 -1
12309 2 00100    //这里翻转一次,已经是两个结点了;
然后根据next=33218找到下一个结点
00100 1 -1
12309 2 00100
33218 3 00000
这里进行翻转,先存放33218的下一个结点 next=00000;
然后00000变为12309,但是链表之间已经断了,3这个结点指向4而非指向2
所以应该用一个来存放之前的地址,pre=12309,00000=pre,于是变成:
00100 1 -1
12309 2 00100
33218 3 12309
然后到这里pre变成33218,移到下一个结点 根据next==00000找到00000 4 99999
00100 1 -1
12309 2 00100
33218 3 12309
00000 4 99999
根据pre==33218,更新next为99999,然后99999赋值为33218:
00100 1 -1
12309 2 00100
33218 3 12309
00000 4 33218
到这里翻转了4个结点,只需要迭代3次;剩下链表:
99999 5 68327
68327 6 -1

此时pre=00000,next=99999;

将翻转后链表按头结点排序,方便看:
00000 4 33218
33218 3 12309
12309 2 00100
00100 1 -1

------------

99999 5 68327
68327 6 -1
这里将我们一开始应该输入一个头结点地址,head,翻转之后,head的位置应该是末尾
然后将链表链接,head->Next=next,这样就变成00100 1 99999
于是新链表:
00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68327
68327 6 -1


这样就是K<N的一种翻转过程;如果K==N;就不需要进行链表拼接这一步;直接全部翻转即可
补充:
这里注意是每K个翻转一次,每K个翻转一次;读题一半开写,以为是前K个翻转;这里就需要计数
了,这里;

看下标;
0123456789
0123456789
这里K=3时,2105438769
因为翻转只看链表后K-1个,就是从1到2,在2进行翻转之后就结束
0<-1<-2 3->4->5->6->7->8->9
这里输出210
然后从3开始,3布翻转,就是从4到4,在5进行翻转之后就结束
2105438769
所以这里需要一个变量标志后部分链表是否需要翻转;
每次局部翻转的边界是3 6 9;就是K*n,n=1,2,3,4;

0->1->2->3->4->5->6->7->8->9,k=3;
第一次翻转:
0<-1-<-2   3->4->5->6->7->8->9
这里翻转之后,0应该拼接到下一个翻转链表的头结点;
第二次翻转:
2->1->0    3<-4<-5     6->7->8->9
这里0应该指向第二个头结点5  于是:
2->1->0->5->4->3      6->7->8->9
第三次翻转:
2->1->0->5->4->3      6<-7<-8    9
这里3指向下一个链表的头结点;
2->1->0->5->4->3->8->7->6  9
然后这里9不足3个不用翻转;所以尾结点6指向9;
结果:
2->1->0->5->4->3->8->7->6->9;
变成这种形态需要移动数组,但是可以通过局部倒序输出实现,不改变数组;
但是移动数组也有一个优点,就是拼接的时候很方便;

然后看翻转过程;

1 2 3 4 5 6 7 8
0 1 2 3 4 5 6 7   k=3;
index从0开始,数3个,然后翻转后面2(k-1)个;
翻转之后index指向下标2;,下一次index++,让index指向下一个头结点下标3,
然后index+K-1就是满足可以翻转的下标,K-1就是头结点后面个;
如果index+K-1<=7,也就是在下标之内,就可以进行翻转;
index+K-1<=N-1;-->index+K<=N;

这里每次都时从index到index+K-1这样一组来的,当index==0时
就是0-2这k个;index==3,3-5这k个,边界为<index+K;

到这里为止都是用的迭代的方式实现反转,但是不知道为什么,最后两个测试点段错误和答案错误;

然后学习了别人的代码,局部反转,通过这个下标来实现next的功能,然后逆序通过下标对应
交换实现反转,很有创意的想法,在数组中确实时很方便的操作;

边界:
奇数时下标:012;中间不交换,于是是中间的前一个,一共K个,K/2==3/2==1,
01234 K=5,K/2==5/2==2;
这里就是下标小于K/2;
偶数时下标:01,无中间;K=2;K/2==2/2==1;也是小于K/2;

比如:
1 2 3 4 5 6 7
0 1 2 3 4 5 6
这里k=3;要反转的段数是N/K==7/3=2;反转前两段;

第一段;
这里用的是交换实现逆序,用一个变量存放第一次交换位置的数据;
temp=data(0);
data(0)=data(2);
data(2)=temp;        //这里就实现了交换逆序;
然后这里前半段与后半段交换实现逆序,

第二段:
第二段起始位置是3;
temp=data(3);
data(3)=data(5);
data(5)=tmep;

起始位置下标就是,这里考虑到用两层循环,一层描述反转的段数;
一层描述段内的反转;反转段数循环变量暂定为i,内层循环为j;
那么反转开始的下标就应该就是 i*K+j;j每次从0开始;
两段:
第一段:i=0,j=0;
下标:0==i*K+j==0*3+0==0
第二段:i=1,j=0;
下标:3==i*K+j==1*3+0==3
这里i*K是外层段开始的位置,j是内部的循环;这是前半段的下标
然后看后半段的下标;这里i=0,j=0时,前段下标为0,后段下标为2
i=1,j=0时,前端下标为K==3,后段下标为5
段下标为K*(i+1)-1,然后j往后移动,那么后段往前,应该是-j;
后端下标:K*(i+1)-1-j;
K*i+K-1-j;

最后反转完成通过下标找到下一个,然后将地址赋值给next域,就实现了结点的链接;
这里升序的下标就是链表的暗链;因为这样翻转后的下一个结点一定是当前结点的后继
结点;注意最后一个结点没有后继,即下标为N-1的结点next域为-1;

补充:这里采用的反转方式没有问题,但是仍然段错误,那么应该是我这里的输入有
问题;继续判断输入的问题;输入应该没有问题,找构建链表这一部分;

暂时屈服了,实在是看不出来哪里出了问题;把字符串指针域改为整型指针域了;
这里改了int型之后就不是段错误了,这里应该是使用string类出了点问题,要去
学习一下string类细节;


然后这里大数超时了,10的5次方两层循环就是10的10次方
超过for的最大承受10的8次方;

*/
#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
struct LNode{
    int Address;
    int Data;
    int Next;
};
typedef struct LNode Node;
int main(){
    //输入头结点地址,结点数N,以及翻转结点数K;
    int head;    //注意这一结点地址是字符串类型;改为int型了;
    int N,K;
    cin>>head>>N>>K;
    //这里改一下输入,采用随机存储的形式,利用下标作为地址;这里N不超过10的5次方;
    Node data[100001];    //这里数组和地址是一样的,从0开始编码;
    int index=0;
    for(int i=0;i<N;i++){
        cin>>index;    //输入地址;
        cin>>data[index].Data>>data[index].Next;
        data[index].Address=index;
    }
    /*
    //输入结点信息;
    struct LNode data[N];
    for(int i=0;i<N;i++){
        cin>>data[i].Address>>data[i].Data>>data[i].Next;
    }
    */



    
    struct LNode List[N];    //存放链接后链表;
    //构建链表;
    int next=head;
    index=0;    //index指示下标;从0开始;
    for(int i=0;i<N;i++){
        List[i]=data[next];        //
        next=List[i].Next;
        /*这里的地址数查看应该是N+1个,一共N个结点,当i==N-1时,next指向最后一个结点的地址
        而非-1;这里循环就结束了;但是数据是放进了List的;
        */
        if(next==-1){
            N=i+1;        //这一条实在是不合理,没有理解到,求解;这里是根据别人的与我自己差异的找到的缺失;
            /*毕竟这里所有数据已经放进List了,不知道这里这里next等于-1后的操作有什么意义*/
            break;
        }
    }
    //反转;这个反转是数组随机存取的特色;
    struct LNode exchange;
    for(int i=0;i<N/K;i++){        //反转的段数;
        for(int j=0;j<K/2;j++){    //下标只到每段的前半段,因为交换到中间就交换完成;
            exchange=List[i*K+j];
            List[i*K+j]=List[K*(i+1)-1-j];    //末尾与首位交换;
            List[K*(i+1)-1-j]=exchange;
        }
    }
    //反转后通过下标暗链进行链接;
    for(int i=0;i<N;i++){
        if(i<N-1){    //有后继结点的结点next域指向下一个;
            List[i].Next=List[i+1].Address;
        }
        else if(i==N-1){
            //最后一个结点无后继,next指针域为-1;
            List[i].Next=-1;
        }
    }
    //逐个输出;
    for(int i=0;i<N;i++){
        //前N-1个都是5位整数格式;
        if(i<N-1){
            printf("%05d %d %05d\n",List[i].Address,List[i].Data,List[i].Next);
        }
        else if(i==N-1){
            printf("%05d %d %d\n",List[i].Address,List[i].Data,List[i].Next);
        }
        
    }
    return 0;
}
/*
这里第一次错误是读题不仔细,以为只是前K个反转,后面才发现是每k个就反转一次;
然后就是字符串类不够精细,还不够精准,然后就是数组的灵活运用与这个逆序的操作
很巧妙,多读别人的代码,很有帮助;
*/

  • 28
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1025 链表的Python代码如下所示: ```python # 开始的地址,链表节点数量,K start_address, count, k = list(map(int, input().split(' '))) # 创建一个长度为10万的空列表 single_lists = [None * 100000 # 读取输入,并存放至空列表中 for i in range(count): single_list = input().split(' ') single_lists[int(single_list = reverse_result_lists[::-1] # 按要求输出单链表 for j in range(count - 1): result_lists[j][2 = result_lists[j + 1][0] print(' '.join(result_lists[j])) result_lists[-1][2 = '-1' print(' '.join(result_lists[-1])) ``` 以上代码实现了链表的功能,通过读取输入的链表节点,并按照题目要求进行和输出。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [(python 3)1025 链表 (25分)](https://blog.csdn.net/qq_40728667/article/details/107581997)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [1025 链表 (python)](https://blog.csdn.net/qq_55322766/article/details/122285032)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值