当前位置:   article > 正文

单链表分离(头插法和尾插法的结合,理解指针变换)_3.对任务1或者2中创建的某一个单链表{a1,b1,a2,b2,...,an,bn},编写一个算法将

3.对任务1或者2中创建的某一个单链表{a1,b1,a2,b2,...,an,bn},编写一个算法将它拆

问题:

●有一个带头节点的单链表 L={ a1,b1 , a2, b2......,an,bn }

●设计一个算法将其拆分成两个带头结点的单链表 L1 和 L2;

●L1 = {a1, a2 , a3 , a4....,an} , L2 = {bn,bn-1,...,b1}

●要求L1使用L的头结点

分析:

我们分开链表的实质就是,让 a 和 b 分开, 然后 a用尾插法, b 用头插法
至于头插法和尾插法, 我的上一个文章已经详细讲解了,

现在开始我们的操作:

首先,读题,第四点,题上要求,L1直接使用L 的头结点,那我们就不用为L1头结点分配空间了,
并且 L 的节点也不用分配空间了
那我们就可以,直接利用 L这个链表,在这个链表上构建 L1 , 然后 L2的话,把b提出来,
这时候 a 的结点会因为 b 的断开而 断开,此时,我们需要链接 ,a 的各个结点 ,我们
先从L1的头结点开始链接

我们首先就去创建链表的头结点

L1 = L;

L2=(LinkList*)malloc(sizeof(LinkList));

L2->next =NULL



这是刚开始的时候,我们把 L1 和 L2 的头结点定义了, L2 另外起家,L1 使用L

接下来,就是进行L1 和 L2 的构建了

a1 在前,所以我们就先构建 L1 , L1 肯定包括头结点吧
所以, 链接 a1 和 头结点,就是让 L1头结点的尾指针指向 a1 

所以这个时候,我们需要定义一个指针,来充当L1的新节点,然后把新节点送到L1尾节点的尾指针就行了

LinkList *r1 = L1;  //来定位L1的尾结点的位置,初始的时候只有头结点L1,所以r1指向L1

LinkList *p ;    //定义 p指针来充当L1 的新节点, 
p=L->next;    //刚开始新节点就是a1 ,也就是L头结点的后继节点


这里是把节点a1 传递给了 p ,包括a1的尾指针和数据,为了方便我们后续的操作

现在,我们的 a1 充当新节点,我们已经传给了 P

先进行尾插法

直接把 p 送到L1尾结点 r1  的尾指针 r1->next ,

r1->next=p;

 接下来我们 L1 的第一个节点就新建完成了,然后我们需要 对 b1 进行定位,然后用头插法把 b1送到

L2

我们需要定位 b1 ,所以 p 作为对新节点操作的指针, p要指向 b1, 所以 p 向后移动

p = p->next;

此时我们已经拿到了,b1 这个节点, 现在是 p 来进行管理,用到头插法 把 p 插入到L 2头结点的后继节点

我们需要做的就是, 让L2 的后继指针指向 b1 ,然后b1 指向 b1 插入前的 L2->next ,从而形成闭环

根据头插法,我们要让

p->next = L2->next;    //先把L2头结点的尾指针赋值给 新节点b的尾指针,

L2->next = p;                //然后把 新节点  b 1 插入到 L2头结点的后面,就是把 p 的指针 传入到头结点的尾指针


强调,这两步不能颠倒,我们建立链表肯定会进行这两步操作是无疑的, 但是当我们覆盖一个指针的时候,要考虑这个指针里面是否存着我们需要的信息,  插入前,L2->next 里面存放的是第一个结点 的位置, 我们还要用到, 所以要先进行这一步, 然后再把新节点的位置给 头结点的尾指针. 

注意: 覆盖指针的时候,考虑我们要实现什么操作, 覆盖的指针存放的有没有我们需要的信息, 并且要修改哪些指针,这个要搞清楚

 此时 , 我们已经 新建了L1 链表 ,插入了新元素 a1 

                     新建了L2 链表, 插入了元素  b1

但是, 我们接下来要对下一个新节点 a2  ,进行操作, 那 a2 的位置信息我们怎么寻找呢, 要知道, 我们把 b1 分离的时候, 已经

b1->next 变成了 L2->next . 所以 a2 的信息自然就丢失了

那怎么办呢?

最好的办法就是 , 在修改 b1->next 之前, 把 a2 的位置信息 (b1->next )存储起来,  我们用q来存储

LinkList *q;

q = p->next;        // a 1 和 b1 和  a2  分别对应  r1    p   q 


通过b->next 来指引存储 q 的位置

所以我们在进行 对 a1  , b1  , a2  进行一个循环的操作  , 

我们现在理清一下思路, 

第一步: 我们创建了 L1 和 L2 的头结点 , 

L1=L;

L2 = (LinkList *)malloc(sizeof(LinkList));

L2->next = NULL;

第二步: 对 L 从头结点开始, 为了对节点进行遍历分割

第三步: 先定义 r1 是L1的尾节点 ,  p是新插入的节点 ,然后因为 b结点分隔后, a 2失去联系 , 要用 q 来存储 b 的尾指针 

LinkList *p , *q , *r1;

r1=L1;                //刚开始, L1 是空节点, 所以指向 头结点 

第四步: 我们通过尾插法, 先构建 L1 ,让 p 指向 L1的尾指针 r1 , 

p=L->next;                //初始的时候, a1是L的新节点

r1 ->next = p;          // 然后把新节点的指针赋值给L1 尾结点 r1 的尾指针

r1 = p;                // 此时L1的尾节点变成 p , 所以 r1 指向 p

第五步: 接着处理下一个节点 b1 (当然指向新节点的指针 p 需要往后移动), 把 b1 揪出来 . 此时, 为了避免对 b1 的指针进行覆盖后, 会失去 a2 的联系 ,然后用 q 来存储

p = p->next;

LinkList *q;   //这是这一步才得出的操作, 上面的说过了,再陈述一遍

q = p->next;  //存储 a2位置, 此时 p 是 b1 , 其后继指针当然是 a2

第六步: 可以对 b1进行操作,头插法, 变换 b1 的指针指向L2 的头结点的尾指针 ,  L2 的尾指针再指向 b1

p->next = L2->next;

L2 ->next = p;

第七步:  接下来, 我们接着让 指向 新节点指针 p 向后移动 , 那怎么移动到 a2 呢 ? 我们刚才存储了 a2到 q ,所以直接赋值一下就可以

p = q 

         因为我们存储了 a2 ,所以我们就可以把 a2 链接到 a1 后边.

         此时, L1的尾结点是r1(a1),新节点就是 p (a2),我们就可以链接了

r1 ->next = p;

第八步:链接之后,L1 的尾结点,又更新了, r1 指向新节点 p , 就是 a2 ,那就需要 变换r1 指向 a2 ,然后 p指向下一个新节点, b2 ,如此循环往复

r1 = p; 

p = p->next;

第九步:我们会发现 , p 又指向了 b2 , r1指向了L2的新节点 a2 ,  我们还是需要对 b2 进行头插法 , 第八步我们已经 进行了对 L1的更新

,就是对新节点 a2 进行的尾插法操作 , 接下来 就又是 对 b 进行头插法, 如此循环 , 

那我们是不是就可以用 一个 w

,,,,,

那怎么看最后一步呢,由题可知, a1 ,b1,,,,,an ,bn

所以,我们就把第一种情况当作是最后一步来讨论

 所以,当什么时候,结束呢?

当判断bn 后面没有结点的时候, 我们就可以结束了, 刚才我们用 q 来存放 b 断开联系后的下一个结点 ,那当bn的尾指针是NULL的时候 , 我们就可以结束了, 意思就是不用连接了, 因为 bn 断开后, 没有后继节点了.

所以,判断q 是空就可以 跳出操作了,

跳出操作后, 我们还要注意,虽然我们不用链接了, 但是 r1 是L1 的尾结点, 尾结点没有置空,它的尾指针还存着 bn的位置指针,

说白了 , 就是他还需要链接  bn 后继空指针, 尾结点的尾指针反正是空, 所以跳出的时候 , 我们再进行置空就可以了.

那现在思路我们已经理清了 , 那什么时候开始循环呢 ? 哪些 步骤我们可以开始循环  ,

在创建L1 和 L2 头结点 后 ,  我们就开始往 L1 和 L2 链接新节点了

此时是 把  新节点 p 送到 r1->next , 链接 a 的新节点 ,并且 r1 指向 L1的尾结点 p

r1->next = p;

r1 = p;

然后 接着让 指向新节点的 p 指针往后移动 ,  需要把 b1 链接 到 L2  , 需要把 b1 ->next覆盖, 所以我们用 q 来存储会丢失的指针 

p = p->next;

q = p->next;

接着 , p->next = L2->next;

           L2->next = p;

接下来又需要链接 新节点 a 了 ,我们还需要让 p 来指向 新节点 a , 但 新节点  a  的位置刚才我们覆盖了, 存储在了 q 中, 所以需要赋值给 p

p= q;

现在 , 我们又一次需要对 a , 进行操作了 ,然后进行的操作还是 r1->next = p; 链接 b , 存储下一个结点 a , 

我们此时又回来了 , 所以我们就知道了 ,循环哪一部分了 , 

while( )
{
    r1->next = p;
    r1=p;
    p = p->next;
    q = p->next;
    p->next = L2->next;
    L2->next = p;
    p = q;
}

其实我们循环的是这一部分, 那循环判出的条件是 , q为空的时候 , q存放的是链接接 an 和 an+1 

的数据 , 当 an+1 是空的时候 , 那我们就可以跳出了 , 那直接用 

q!=NULL

可以吗 ? 我认为不妥 , 因为, 进入循环之前  q 我们还没有赋值 ,那就是 NULL , 

所以, 这又引出了 ,怎样才能进入循环, 

当仍然有新节点可以插入的时候, 我们就可以进入循环, 

所以 用  p!=NULL , 就很妙 !(p指向的就是待操作的新结点)

因为, 我们在循环结束的最后一步, 又把 b 断开后的指针存放起来了 ,并传递给了

p , 用来指向新节点, 那当操作 bn 时候 , p->next 就是空, 此时 q里存放的是空

传递给p 的也是空 ,

综上所述 , 我们判出的条件是 , p!=NULL 即可

L2的尾指针不用处理 , 因为是头插法, ,而L1 的尾结点是 r1 , 然后循环结束 ,也没有修改,链接 新指针 p(NULL)

所以 , 我们需要亲自链接   r1 ->next =NULL;

源代码如下:

  1. void split(LinkList *&L,LinkList *&L1,LinkList*&L2)
  2. {
  3. LinkList *p = L->next, *q, *r1;
  4. L1 = L;
  5. r1 = L1;
  6. L2 =(LinkList*)malloc(sizeof(LinkList));
  7. L2->next = NULL;
  8. while(p!=NULL)
  9. {
  10. r1->next = p;
  11. r1=p;
  12. p = p->next;
  13. q = p->next;
  14. p->next = L2->next;
  15. L2->next = p;
  16. p = q;
  17. }
  18. r1->next = NULL;
  19. }

测试代码如下:

  1. #include <stdio.h>
  2. #include <malloc.h>
  3. typedef int ElemType;
  4. typedef struct LNode //定义单链表结点类型
  5. {
  6. ElemType data;
  7. struct LNode *next; //指向后继结点
  8. } LinkList;
  9. void DispList(LinkList *L); //输出单链表
  10. void CreateListR(LinkList *&L,ElemType a[],int n); //尾插法建立单链表
  11. void split(LinkList *&L,LinkList *&L1,LinkList*&L2);//分离链表
  12. int main()
  13. {
  14. LinkList *L1, *L2,*L;
  15. ElemType a[10]= {1,2,3,4,5,6,7,8,9,10};
  16. CreateListR(L, a, 10);
  17. printf("输出L表结果:");
  18. DispList(L);
  19. split(L,L1,L2);
  20. printf("L1表结果:");
  21. DispList(L1);
  22. printf("L2结果:");
  23. DispList(L2);
  24. return 0;
  25. }
  26. void CreateListR(LinkList *&L,ElemType a[],int n)//尾插法建立单链表
  27. {
  28. LinkList *s,*r;
  29. int i;
  30. L=(LinkList *)malloc(sizeof(LinkList)); //创建头结点
  31. L->next=NULL;
  32. r=L; //r始终指向终端结点,开始时指向头结点
  33. for (i=0; i<n; i++)
  34. {
  35. s=(LinkList *)malloc(sizeof(LinkList));//创建新结点
  36. s->data=a[i];
  37. r->next=s; //将*s插入*r之后
  38. r=s;
  39. }
  40. r->next=NULL; //终端结点next域置为NULL
  41. }
  42. void DispList(LinkList *L) //输出单链表
  43. {
  44. LinkList *p=L->next;
  45. while (p!=NULL)
  46. {
  47. printf("%d ",p->data);
  48. p=p->next;
  49. }
  50. printf("\n");
  51. }
  52. void split(LinkList *&L,LinkList *&L1,LinkList*&L2)
  53. {
  54. LinkList *p = L->next, *q, *r1;
  55. L1 = L;
  56. r1 = L1;
  57. L2 =(LinkList*)malloc(sizeof(LinkList));
  58. L2->next = NULL;
  59. while(p!=NULL)
  60. {
  61. r1->next = p;
  62. r1=p;
  63. p = p->next;
  64. q = p->next;
  65. p->next = L2->next;
  66. L2->next = p;
  67. p = q;
  68. }
  69. r1->next = NULL;
  70. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/466986
推荐阅读
相关标签
  

闽ICP备14008679号