小白算法积累——单链表25#带头结点单链表+穿插+逆置

题目:设线性表L=(a1,a2,a3,…an-2,an-1,an)采用带头结点的单链表保存,链表中的结点定义如下:
typedef struct node
{ int data;
struct node*next
}Node;

请设计一个空间复杂度为O(1)且时间上尽可能高效的算法,重新排列L中的各结点,得到线性表L’=(a1,an,a2,an-1,a3,an-2…)

关键字:带头结点单链表+穿插+逆置

思路 :为了达到穿插+逆置的效果,我们选择分段+逆置+插入
先观察L=(a1,a2,a3,…an-2,an-1,an)和L’=(a1,an,a2,an-1,a3,an-2,…)
发现L’是由L摘取第一个元素,再摘取倒数第一个元素…依次合并而成的。
为了方便链表后半段取元素,需要先将L后半段原地逆置【题目要求空间复杂度为O1,不能借助栈】,否则每取最后一个结点都需要遍历一次链表。

1)先找出链表L的中间结点,利用速度差产生路程差法
ps:顺序表就是“直接算算地址”法了
为此设置两个指针p和q,指针p每次走一步,指针q每次走两步
当指针q到达链尾时,指针p自然就在链表的中间结点

2)将L的后半段结点原地逆置
需要变量:q的后继结点指针r,负责将“逆置指针”的工作

3)从单链表前后两段中依次各取一个结点,按要求重排。
需要变量:

void change_list(Node*L)
{     Node*p,*q,*r,*s;//
    p=q=L;
    while(q->next!=NULL)//寻找中间结点
    {  p=p->next;//p走一步
       q=q->next;//q走一步
       if(q->next!=NULL)
           q=q->next;//q再走一步
    }
    q=p->next;//此时p所指结点为中间结点,q为后半段链表的首结点
    p->next=NULL;//将前半段与后半段断开(分段)
    while(q!=NULL)//开始将链表后半段逆置
    {   r=q->next;//r为q的后继结点,在q反转指针的指向时需要防止断链
        q->next=p->next;//由于就近,利用刚好处于中间结点的p来帮助q反转指向
        p->next=q;//通过循环遍历让p(前半段最后一个元素,即中间结点)从指向后半段第一个结点逐步后移,最终指向后半段最后一个元素(表尾结点)
        q=r;//利用后驱指针r来防止q回不到后半链
     }
     s=L->next;//s为前半段链表的首结点(因为此时p作为中间结点指针需要为添加后半段指针做准备)
     q=p->next;//q为后半段指针
     p->next=NULL;//断开前半段链表
     while(q!=NULL)
     { r=q->next;//r为后半段链表的后驱指针
       q->next=s->next;//将后半段链表的结点链接到前半段链表中
       s->next=q;//
       s=q->next;//s遍历(一次前进两个结点)
       q=r;//q遍历
     }
    }
   
  
      
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值