PTA basic 1075 链表元素分类 (25 分) c++语言实现(g++)

给定一个单链表,请编写程序将链表元素进行分类排列,使得所有负值元素都排在非负值元素的前面,而 [0, K] 区间内的元素都排在大于 K 的元素前面。但每一类内部元素的顺序是不能改变的。例如:给定链表为 18→7→-4→0→5→-6→10→11→-2,K 为 10,则输出应该为 -4→-6→-2→7→0→5→10→18→11。

输入格式:

每个输入包含一个测试用例。每个测试用例第 1 行给出:第 1 个结点的地址;结点总个数,即正整数N (105​​);以及正整数K (103​​)。结点的地址是 5 位非负整数,NULL 地址用 −1 表示。

接下来有 N 行,每行格式为:

Address Data Next
 

其中 Address 是结点地址;Data 是该结点保存的数据,为 [−105​​,105​​] 区间内的整数;Next 是下一结点的地址。题目保证给出的链表不为空。

输出格式:

对每个测试用例,按链表从头到尾的顺序输出重排后的结果链表,其上每个结点占一行,格式与输入相同。

输入样例:

00100 9 10
23333 10 27777
00000 0 99999
00100 18 12309
68237 -6 23333
33218 -4 00000
48652 -2 -1
99999 5 68237
27777 11 48652
12309 7 33218
 

输出样例:

33218 -4 68237
68237 -6 48652
48652 -2 12309
12309 7 00000
00000 0 99999
99999 5 23333
23333 10 00100
00100 18 27777
27777 11 -1



 
测试用例
00100 9 10
23333 10 27777
00000 0 99999
00100 18 12309
68237 6 23333
33218 4 00000
48652 2 -1
99999 5 68237
27777 11 48652
12309 7 33218
测试用例
00100 19 10
23333 10 27777
00000 11 99999
00100 18 12309
68237 -6 23333
33218 -4 00000
48652 -2 -1
99999 15 68237
27777 11 48652
12309 17 33218
测试用例
00100 9 10
23333 10 27777
00000 0 99999
00100 9 12309
68237 -6 23333
33218 -4 00000
48652 -2 -1
99999 5 68237
27777 1 48652
12309 7 33218

 

可以参考 柳婼的这篇文章https://blog.csdn.net/liuchuo/article/details/78037319

  主要思路是建立三个数组, 把符合第i个数组特征的元素推入第i个数组中, 

  然后输出的时候, 首先输出第一个元素, 以 Addr data的形式

  然后循环输出数组除最后一个元素外剩下的所有元素,以addr换行addr data的形式

  最后一个元素以addr换行addr data -1;的形式输出

  这样利用了题目给的规则, 因为所有子链元素在原链表中相对位置不变, 所以可以按序取得元素放入对应子链,然后按序输出就可以,注意输出格式

  跳过了链表的地址修改过程,合理利用了题目条件  可以快速ac

 

 

我的方法是按部就班的修改链表地址解题  

解题思路

用8个指针来完成 三个分段头指针,三个分段next指针 ,一个遍历指针 一个暂存指针分别为

  fh  指向第一分链头部, fc 指向第一分链尾部元素

  mh 指向第二分链头部, mc 指向第二分链尾部元素

  lh  指向第三分链头部,  lc 指向第三分链尾部元素

  遍历指针 cp 从头遍历到尾

  暂存指针 tn 存着cp指向元素的下一个元素的地址

  初值全部为 -1 既空指针

  其中第一分链 是 小于0的元素的子链

  第二分链 是 [0,k] 范围的元素子链

  第三分链 是 大于k 的元素的子链

 

由于题目要求子链中各元素的相对位置 和 原链表中的相对位置相同 所以一趟遍历就可以完成链表重排

难点并不在如何筛选3种类型的元素 比较困难的点在于 如何链接三个分链

  

  这里使用两种办法可以解决这个链接问题

  方法一

  先检测要链接的元素地址是否合法, 从第一个子链的头指针开始判断

  如果地址不为-1 合法 说明子链元素存在,将链表头指针赋值为当前子链头指针

    当前子链的尾部 指向 下一个子链的头部(下一个子链如果存在则是真实地址, 不存在则为-1)

  使用这种方法AC

 

  

 

  方法二

  如果有更多分链如何链接?

  建立一个头尾指针的pointer(m*2,-1)数组,必须赋初值-1,和数组大小m*2

    和一个有效头尾指针 activePoiner数值;

  遍历链表元素时如果是第一次遇到第 m (本题m为 0 1 2)子链的元素

  说明这个元素是第m子链的头部元素,将这个元素的地址作为子链头部和尾部, 存入数组 的 下标 m*2 和 m*2+1 位置中  分别代表第m子链的头尾指针

  这个时候, pointer数组中存储的 每两个元素  都是一个子链的头尾指针  

  

  当遍历到新的第 m 个子链的元素时, 将这个元素地址 存入数组 (m*2+1)位置里  这个(m*2+1)位置中存的是第m个子链的尾部元素地址

 

  pointer数组中的元素总是成对存在的, 每一对都是一个子链的头尾指针 

  在重新链接子链的时候,  将pointer数组从头开始遍历 把所有不为-1的指针, 推入activePointer数组的尾部

  以步长为2 初值为i=2 遍历activePointer数组,  把 arr[activePointer[i-1] ]位置的元素next指向activePointer[i]; 既 有效子链的尾部指向下一个有效子链的头部

  结束后将最后一个子链的尾部next指向-1;

  将有效指针的第一个元素作为新的表头

  

    for(int i=2;i<activePointer.size();i+=2){
        arr[activePointer[i-1]].next=activePointer[i];
    }
    arr[activePointer[activePointer.size()-1]].next=-1;
    cp=activePointer[0];

  使用这种方法AC

      

   

 

 

方法一 AC代码  注释部分为方法一优化之前, 更容易理解的版本

#include <iostream>
#include <vector>
using namespace std;
class node{
public:
    int data;
    int next;//next指针 存放下一个元素的位置
    node():next{-1}{};
};
int main(){
    int phead,n,k,tempAddr,tempData,tempNext,data;
    int cp,fh,mh,lh,fc,mc,lc,tn;
    bool findFh{false},findMh{false},findLh{false};
    vector<node> arr(100000);//序号最大10w 且从序号1开始 初始化为next= -1; data=0;
    cin >> phead >> n >> k;
    cp=phead;
    fh=mh=lh=fc=mc=lc=tn=-1;//子链头尾指针 初始化为空指针
    for(int i=0;i<n;i++){//录入信息
        scanf("%d %d %d",&tempAddr,&tempData,&tempNext);
        arr[tempAddr].data=tempData;
        arr[tempAddr].next=tempNext;
    }
    cp=phead;//从头开始排序
    while(cp!=-1){//当下一个元素存在时
        tn=arr[cp].next;//下一个元素的地址
        data=arr[cp].data;//当前元素的数据
        if(data<0){//属于第一子链元素
            if(!findFh){fh=cp;findFh=true;}//还没找到第一子链的头元素时
            arr[fc].next=cp;//first子链当前指向的元素的next指针 指向cp指向的元素
            fc=cp;// fnext更新到当前cp指向的元素
        }else if(data<=k&&data>=0){//属于第二子链元素
            if(!findMh){mh=cp;findMh=true;}//还没找到第二子链的头元素时
            arr[mc].next=cp;//mid子链当前指向的元素的next指针 指向cp指向的元素
            mc=cp;//mnext指针指向cp位置的元素
        }else {//属于第三子链元素
            if(!findLh){lh=cp;findLh=true;}//还没找到第三子链的头元素时
            arr[lc].next=cp;//last子链当前指向的元素的next指针 指向cp指向的元素
            lc=cp;// lnext更新到当前cp指向的元素
        }
        cp=tn;//指针后移指向原链表下一个元素
    }
    //三种子链处理后 需要链接各子链 并 处理尾部
  //方法一
if(fc!=-1){//第一子链存在 cp=fh;// if(mc!=-1){//第二子链存在 arr[fc].next=mh;//第一子链尾部元素next指向 第二子链头结点 arr[mc].next=lh;//第二子链尾部元素next指向 第三子链头结点 }else{ arr[fc].next=lh;//第一子链尾部元素next指向 第三子链头结点 } }else if(mc!=-1){//第一子链不存在 第二子链存在 cp=mh;//链表头指向第二子链头部 arr[mc].next=lh;//第二子链尾部元素next指向 第三子链头结点 }else{//只有第三子链存在 cp=lh;//链表头指向第三子链头部 } if(lc!=-1)arr[lc].next=-1;//如果第三子链存在 尾部next置为-1 // if(findFh){//找到第一子链头结点 // cp=fh;//链表头指向第一子链头部 // if(findMh){//有第二子链 // arr[fc].next=mh;//尾部元素.next指向第二子链头结点 // arr[mc].next=lh;//第二子链尾部指向 第三子链头结点 // }else{//无第二子链 // arr[fc].next=lh;//直接指向第三子链头结点 // } // } // else if(findMh){//无第一子链 但找到第二子链头结点 // cp=mh;//链表头指向第二子链头部 // arr[mc].next=lh;//第二连接子链尾部 指向第三子链头部 // } // else if(findLh){//无第二子链但是找到第三子链头结点 // cp=lh;//链表头指向第三子链头部 // } // if(findLh)arr[lc].next=-1;//如果第三子链存在 尾部next置为-1 while(cp!=-1){//逐个输出重新链接后的链表 printf("%05d %d ",cp,arr[cp].data); if(arr[cp].next!=-1){ printf("%05d\n",arr[cp].next); }else{ printf("%d\n",arr[cp].next);//尾部元素输出-1时不需要%05d } cp=arr[cp].next;//指针后移到下一个元素 } return 0; }

 

方法二

 

#include <iostream>
#include <vector>
using namespace std;
class node{
public:
    int data;
    int next;//next指针 存放下一个元素的位置
    node():next{-1}{};
};
int main(){
    int phead,n,k,tempAddr,tempData,tempNext,data;
    int cp,fh,mh,lh,fc,mc,lc,tn;
    bool findFh{false},findMh{false},findLh{false};
    vector<node> arr(100000);//序号最大10w 且从序号1开始 初始化为next= -1; data=0;
    vector<int> pointer(3*2,-1);//6个指针 初始均为-1
    vector<int> activePointer;//存在(不为-1)的头尾指针列表
    cin >> phead >> n >> k;
    cp=phead;
    fh=mh=lh=fc=mc=lc=tn=-1;//子链头尾指针 初始化为空指针
    for(int i=0;i<n;i++){//录入信息
        scanf("%d %d %d",&tempAddr,&tempData,&tempNext);
        arr[tempAddr].data=tempData;
        arr[tempAddr].next=tempNext;
    }
    cp=phead;//从头开始排序
    while(cp!=-1){//当下一个元素存在时
        tn=arr[cp].next;//下一个元素的地址
        data=arr[cp].data;//当前元素的数据
        if(data<0){//属于第一子链元素
            if(!findFh){
                findFh=true;
                pointer[0]=fh;
                pointer[1]=fh;
            }//还没找到第一子链的头元素时
            arr[fc].next=cp;//first子链当前指向的元素的next指针 指向cp指向的元素
            fc=cp;// fnext更新到当前cp指向的元素
            pointer[1]=fc;
        }else if(data<=k&&data>=0){//属于第二子链元素
            if(!findMh){
                findMh=true;
                pointer[2]=mh;
                pointer[3]=mh;
            }//还没找到第二子链的头元素时
            arr[mc].next=cp;//mid子链当前指向的元素的next指针 指向cp指向的元素
            mc=cp;//mnext指针指向cp位置的元素
            pointer[3]=mc;
        }else {//属于第三子链元素
            if(!findLh){
                findLh=true;
                pointer[4]=lh;
                pointer[5]=lh;
            }//还没找到第三子链的头元素时
            arr[lc].next=cp;//last子链当前指向的元素的next指针 指向cp指向的元素
            lc=cp;// lnext更新到当前cp指向的元素
            pointer[5]=lc;
        }
        cp=tn;//指针后移指向原链表下一个元素
    }
    //三种子链处理后 需要链接各子链 并 处理尾部
    //方法二
    for(int i=0;i<pointer.size(); i++){
        if(pointer[i]!=-1)activePointer.push_back(pointer[i]);
    }
    for(int i=2;i<activePointer.size();i+=2){
        arr[activePointer[i-1]].next=activePointer[i];
    }
    arr[activePointer[activePointer.size()-1]].next=-1;
    cp=activePointer[0];

    
    while(cp!=-1){//逐个输出重新链接后的链表
        printf("%05d %d ",cp,arr[cp].data);
        if(arr[cp].next!=-1){
            printf("%05d\n",arr[cp].next);
        }else{
            printf("%d\n",arr[cp].next);//尾部元素输出-1时不需要%05d
        }
        cp=arr[cp].next;//指针后移到下一个元素
    }
    return 0;
}

 

posted @ 2021-05-12 15:28  keiiha  阅读(107)  评论(0编辑  收藏  举报