• 小猪的数据结构辅助教程——2.4 线性表中的循环链表


    小猪的数据结构辅助教程——2.4 线性表中的循环链表

    标签(空格分隔): 数据结构


    本节学习路线图与学习要点

    学习要点

    • 1.了解单链表存在如何的缺点。暴露出来的问题
    • 2.知道什么是循环单链表,掌握单链表的特点以及存储结构
    • 3.掌握循环链表的一些基本操作的实现逻辑。最好能手撕代码

    1.循环单链表的引入


    2.循环链表的特点以及存储结构

    循环链表的特点:

    上面也说了。比单链表略微高比格一点的地方就是:
    链表最后一个结点的指针域指向了头结点而已,这样形成所谓的环。就是循环单链表了。呵呵!
    特点的话有:
    我们之前推断单链表是否为空:head -> next 是否为NULL就可以
    而循环单链表仅仅需:head -> next 是否为head(自身)就可以
    相关的操作和单链表还是比較类似的。

    存储结构:(和单链表一样~)

    typedef int ElemType;
    typedef int Status;  
    typedef struct LNode
    {
        ElemType data;         //数据域
        struct LNode *next;   //指针域 
    }LNode;  
    typedef struct LNode *LinkList;

    单链表的结构图

    如上图,增加我们的链表非常长的话。从表头找到表尾是非常费时的,所以循环链表往往是设置尾指针


    而不是设置头指针。尾指针尾指针尾指针


    3.相关基本操作的代码实现

    1)构造空表

    Status InitList(LinkList L)
    {
        L = (LinkList)malloc(sizeof(LNode));
        if(!L)exit(ERROR);
        L ->next = L;   //自己指自己~(头节点指针域指向头结点) 
        return OK;
    }

    2)将表置空

    void ClearList(LinkList L)
    {
        LinkList p,q;
        L = L ->next;     //指向头结点 
        p = L ->next;    //指向第一个结点 
        while(p!=L)
        {
            q = p ->next;
            free(p);
            p = q;
        }
        L ->next = L; //自己指自己 
    } 

    3)推断是否为空表

    有头节点的哦~

    Status ListEmpty(LinkList L)
    {
        return L!=L ->next?

    FALSE:TRUE; }

    4)销毁表

    void DestoryList(LinkList L) 
    {
        ClearList(L);  //将表置空
        free(L);    //释放头节点 
        L = NULL; 
    }

    5)获得表长度

    int ListLength(LinkList L)
    {
        int i = 0;
        LinkList p = L ->next;  //指向头结点 
        while(p != L)
        {
            i++;
            p = p ->next;
        } 
        return i;
    } 

    6)获得表中第i个元素的值

    Status GetElem(LinkList L,int i,ElemType *e)
    {
        int j = 1;
        LinkList p = L ->next ->next;   //指向第一个结点
        if(i <= 0||i >ListLength)return ERROR;  //推断插入位置是否合法 
        while(j < i)
        {
            j++;
            p = p ->next;
        } 
        e = p ->data;
        return OK;
    } 

    7)查找表中是否存在满足条件的元素

    int LocateElem(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType)) 
    {
        int i = 0;
        LinkList p = L ->next ->next;  //指向第一个结点
        while(p != L ->next)
        {
            i++;
            if(compare(p->data,e))return i;
            p = p ->next; 
        } 
        return 0;   //找不到,返回0 
    }

    8)获得某个节点的直接前驱

    Status BeforeElem(LinkList L,ElemType choose,ElemType *before)
    {
        LinkList q,p = L ->next ->next;  //指向第一个结点
        q = p ->next;  
        while(q != L ->next)
        {       
            if(q ->data == choose)
            {
                before = p ->data;
                return OK;
            }
            p = q; //继续后移
            q = q ->next; 
        }
        return ERROR;   
    }

    9)获得某个节点的直接后继

    Status NextElem(LinkList L,ElemType choose,ElemType *behind)
    {
        LinkList p = L ->next ->next;  //指向第一个结点
        while(p != L)
        {
            if(p ->data == choose)
            {
                behind = p ->next ->data;
                return OK;
            }
        }
    }

    10)往第i个位置插入元素

    Status ListInsert(LinkList L,int i,ElemType e)
    {
        LinkList s,p = L ->next;
        int j = 0;
        if(i <= 0 || i > ListLength(L) + 1)return ERROR;  //推断插入位置是否合法
        //寻找插入结点的前一个结点 
        while(j < i - 1) 
        {
            j++;
            p = p ->next;   
        }
        //生成新结点
        s = (LinkList)malloc(sizeof(LNode)); 
        s ->data = e; //将e赋值给新结点
        s ->next = p ->next; //新结点指向原来的第i个结点 
        p ->next = s;   //原i - 1个结点指向新结点 
        //假如插入的位置是表尾,把新的表尾地址给尾指针 
        if(p == L)
        {
            L = s;
        }
        return OK;
    } 

    步骤解析

    普通的插入和单链表都是大同小异,普通结点插入的流程:

    而特殊情况就是,假如插入位置是尾结点的话。那么还须要让尾指针指向这个新插入的尾结点
    就是上面的:L = s;

    11)删除表中第i个元素

    Status ListDelete(LinkList L,int i,ElemType *e)
    {
        LinkList s,p = L ->next;
        int j = 0;
        if(i <= 0||i > ListLength(L))return ERROR;//推断删除位置是否合法 
        //寻找插入结点的前一个结点 
        while(j < i - 1) 
        {
            j++;
            p = p ->next;
        }
        s = p ->next;
        p ->next = s ->next; 
        e = s ->data;
        if(s == L)
        L = p;
        free(q);  //释放结点
        return OK; 
    }

    步骤解析

    和插入一样,删除完后。还要考虑尾指针指向的位置

    假如删除的是尾结点的话,那么还要让L = p;指向删除结点的前一个节点~

    12)遍历循环链表里的全部元素

    void ListTraverser(LinkList L,void(*visit)(ElemType))
    {
        LinkList p = L ->next ->next;   //指向第一个结点 
        while(p != L ->next)
        {
            visit(p ->data);
            p = p ->next;
        }
        printf("
    ");
    }

    4.本节代码下载:

    https://github.com/coder-pig/Data-structure-auxiliary-tutorial/blob/master/List/list4.c

  • 相关阅读:
    js单体模式
    react实现递归搜索下拉查询目录树功能
    浏览器跨域问题分析
    css中清除浮动
    ts中的函数
    ts中类型
    RX.js6变化
    js对象模型3
    React数组变化之后,视图没有更新
    Mac安装yarn并配置环境变量PATH,运行报错问题解决
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/7052554.html
Copyright © 2020-2023  润新知