ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

单链表原地逆置

2020-11-28 23:00:49  阅读:238  来源: 互联网

标签:结点 单链 后继 next 链表 原地 逆置 指针


给定一个带头结点的单链表,编写算法将其原地逆置。所谓“原地”是指空间复杂度为O(1)。有两种方法,头插法和冒泡法。这两种方法的时间复杂度均为O(n)。

头插法

思路

我们知道,用头插法建立链表,得到的链表中元素的顺序和出入的顺序相反,所以利用这一特点,可以将链表的逆置。

给定一个带头结点的单链表L,如下图所示。

2020-11-28-单链表原地逆置-1

首先用指针p存储链表第一个结点,然后将头结点从链表中剥离下来,如下图所示,此时链表L只有一个头结点。

2020-11-28-单链表原地逆置-2

另设一指针r保存p的后继,将p指向的结点N1用尾插法插入到链表L中,

2020-11-28-单链表原地逆置3

此时p指向N2,保存p的后继N3,再将N2尾插到链表L中,

2020-11-28-单链表原地逆置-4

以此类推,直至保存后继的指针r为空,退出循环。

头插法的实现代码

void Reverse_L1(Linklist L)
{
    /* p 为工作指针, r 为 p 的后继, 以防断链. */
    LNode *p, *r;

    /* 从第一个元素开始. */
    p = L->next;

    /* 先将头结点剥离出来. */
    L->next = NULL;

    /* 依次将元素摘下. */
    while (p != NULL)
    {
        /* 暂存 p 的后继. */
        r = p->next;
        /* 用头插法插入 p. */
        p->next = L->next;
        L->next = p;
        /* 更新 p, 指向下一个结点. */
        p = r;
    }

    return;
}

冒泡法

我把这种方法称为“冒泡法”,是因为算法流程和冒泡排序类似,只不过在冒泡排序中是相邻的元素出现逆序才交换,而链表逆置则要求每对结点之间都要交换顺序。

思路

冒泡法和头插法不同,头插法只有一个工作指针p指向一个操作对象——被摘下来的结点,以及存储其后继的指针r。而冒泡法有两个工作指针,即一对工作指针,以及存储其后继的指针r,共计3个指针。考虑如下一般情况,

2020-11-28-单链表原地逆置-5

指针pre和p分别指向两个结点,将其看作一对结点,它们是每次循环操作的对象。循环中,让N2的后继指向N1,即完成了(N1,N2)的逆置。之后三个指针进一,pre=p,p=r,r=r->next,重复上述逆置操作,链表变成了下图。

2020-11-28-单链表原地逆置-6

显而易见,如果指针r!=NULL,则循环还要继续下去,若r==NULL,循环结束,链表逆置完成,而指针p指向逆置后的一个元素。

上述步骤发生的前提是链表中除了头结点以外,至少有两个结点,而为了将判断是否继续循环和是否进入循环结合起来,应将指针r初始置为指向第一个结点的后继,p指向第一个结点,即p=L->next,r=p->next,指针pre无强制要求。

注意初始p指向的结点在完成逆置之后将成为最后一个结点,所以应该在逆置之前将p的后继置为空,否则逆置之后p的后继会指向倒数第二个结点,而倒数第二个结点的后继指向倒数第一个结点,即逆置前的p,两个结点之间形成环。

冒泡法的实现代码

void Reverse_L2(Linklist L)
{
    /**
     * 三个指针, p 为要反转的结点, pre 为 p 前面的结点, r 是保存 p 后继的指针.
     * 初始状态 p 指向第一个元素, r 指向第二个元素.
     */
    LNode *pre = L, *p = L->next, *r = p->next;

    /**
     * 将要第一个结点后继链接断开. 因为它将成为逆置后链表的最后一个结点, 否则
     * 将在逆置后的链表中的最后一个元素和倒数第二个元素之间形成环.
     */
    p->next = NULL;

    /**
     * 如果 r 不为空, 三个指针前进一个单位.
     * 否则链表只有一个结点, 直接执行 while 后的 L->next = p.
     * 循环中将 p 反转指向其前面的结点 pre, r 是 p 的后继.
     * 假设链表有 n 个元素, 则循环可将前 n - 1 个元素逆置,
     * 之后 p 指向第 n 个元素.
     */
    while (r != NULL)
    {
        /* 指针依次进一. */
        pre = p;
        p = r;
        r = r->next;
        /* 逆置. */
        p->next = pre;
    }

    /* 头结点指向逆置之后的第一个结点. */
    L->next = p;

    return;
}

代码仓库

  1. data-structure/linklist.c at master · tianshihao/data-structure (github.com)

标签:结点,单链,后继,next,链表,原地,逆置,指针
来源: https://www.cnblogs.com/tianshihao/p/14054753.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有