单链表的反转/逆序的三种方法

原文章地址:单链表反转、逆序

/*单链表的反转/逆序的三种方法*/

/*
*前面我们大约把单链表 ADT 的基本操作都过了一遍,但是这
*还不够。单链表在面试与笔试中出现的几率很高,接下来我们
*再花点时间把常见的单链表面试题尽可能过一遍,彻底掌握单
*链表~
*/

/*
*那开始我们的第一个面试题?不妨做做“单链表反转”,或者
*说“单链表逆序”吧?还是基于前面的例子。
*/

/*究竟要如何反转呢?我们不妨拿一个例子来说明一下算法。*/

/*
*我先画一个单链表,这个单链表有4个元素。我的思路就是,
*每次把第二个元素提到最前面来。比如下面是第一次交换,我
*们先让头结点的next域指向结点a2,再让结点a1的next域指向
*结点a3,最后将结点a2的next域指向结点a1,就完成了第一次交换。
*/

/*
*然后进行相同的交换将结点a3移动到结点a2的前面,然后再
*将结点a4移动到结点a3的前面就完成了反转。
*/

/*
*思路有了,那就可以写代码了。这里我们需要额外的两个工作
*指针来辅助交换。这个下面的步骤慢慢理解下,结合图片。
*注意结点之间的关系要先断再连。*/


#define OK 1
#define ERROR 0
typedef int ElemType;
typedef int Status;

typedef struct LNode{

    ElemType Data;
    struct LNode *Next;
}Node,*List;

/*算法一思路:
方法1,current始终是原链表的第一个数,然后把pnext不断移动到首位。
步骤:
*1.定义当前结点 current,初始值为首元结点,current = L->next;
*2.定义当前结点的后继结点 pnext, pnext = current->next; 
*3.只要 pnext 存在,就执行以下循环:
*   3.1 定义新节点 prev,它是 pnext的后继结点,prev = pnext->next;
*   3.2 把pnext的后继指向current, pnext->next = current;
*   3.3 此时,pnext 实际上已经到了 current 前一位成为新的current,
*       所以这个时候 current 结点实际上成为新的 pnext,current = pnext;
*   3.4 此时,新的 current 就是 pnext,current = pnext;
*   3.5 而新的 pnext 就是 prev,pnext = prev;
*4.最后将头结点与 current 重新连上即可,L->next = current;
**/

/* 单链表反转/逆序 */
LinkList ListReverse(LinkList L)
{
    LinkList current,pnext,prev;
    if(L == NULL || L->next == NULL)
        return L;
    current = L->next;  /* p1指向链表头节点的下一个节点 */
    pnext = current->next;
    current->next = NULL;
    while(pnext)
    {
        prev = pnext->next;
        pnext->next = current;
        current = pnext;
        pnext = prev;
        printf("交换后:current = %d,next = %d \n",current->data,current->next->data);
    }
    //printf("current = %d,next = %d \n",current->data,current->next->data);
    L->next = current;  /* 将链表头节点指向p1 */
    return L;
}


/*算法二思路:
方法2,current始终保持在第一位,pnext与prev遍历并完成交换。
步骤:
1.  p = current->next; p 就相当于前面的 pnext。(图1中a2即为p)
2.  current->next = p->next; p->next 就相当于 prev的角色,这句代码意思是 current 的后继指向 prev.
        (相当于图1中a1->next = a3(a2->next))
3.  p->next = L->next; 这句就是 p 的后继直接指向首元节点。(相当于图1中a2->next = a1)
4.  L->next = p; 然后再将头结点指向 p。(相当于图1中L->next = a2)
*/

LinkList ListReverse2(LinkList L)
{
    LinkList current, p;

    if (L == NULL)
    {
        return NULL;
    }
    current = L->next;
    while (current->next != NULL)
    {
        p = current->next;
        current->next = p->next;
        p->next = L->next;
        L->next = p;
    }
    return L;
}

/*算法三思路:
重新建立一个单链表newList,每次将list中的第一个结点放到newList后面。(头插法)
步骤:
    第一次循环:tmp保存了 newList->next,其实就是 NULL,
        然后把 L 的第一个节点拿过来放到新表的首元结点位置,
        然后删了 L 的那个结点。newList->next->next 这句就是补回 NULL。
        这时新表为 -> a1 -> NULL.
    第二次循环,这次是拿到a2了,这时新表变成 -> a2 -> a1 -> NULL,
        其实就是拿个新表来拷贝而已。
*/

LinkList ListReverse3(LinkList L)
{
    LinkList newList;    //新链表的头结点
    LinkList tmp;       //指向L的第一个结点,也就是要摘除的结点

    //参数为空或者内存分配失败则返回NULL
    if (L == NULL || (newList = (LinkList)malloc(sizeof(Node))) == NULL)
    {
        return NULL;
    }

    //初始化newList
    newList->data = L->data;
    newList->next = NULL;

    //依次将L的第一个结点放到newList的第一个结点位置
    while (L->next != NULL)
    {
        tmp = newList->next;         //保存newList中的后续结点
        newList->next = L->next;       //将L的第一个结点放到newList中
        L->next = L->next->next;     //从L中摘除这个结点
        newList->next->next = tmp;        //恢复newList中后续结点的指针
    }

    //原头结点应该释放掉,并返回新头结点的指针
    free(L);
    return newList;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值