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


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

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


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

    学习要点

    1.了解引入双向循环链表的原因
    2.熟悉双向循环链表的特点以及存储结构
    3.掌握双向循环链表的一些基本操作的实现逻辑
    4.掌握逆序输出双向循环链表元素逻辑


    1.双向循环链表的引入


    2.双向循环链表的存储结构

    双向循环链表的特点

    上面也说了。空间换时间,比起循环链表仅仅是多了一个指向前驱的指针
    特点的话:
    推断空表:L ->next = L -> prior = L;

    存储结构

    typedef struct LNode
    {
        ElemType data;         //数据域
        struct LNode *prior;   //前驱指针 
        struct LNode *next;   //后继指针
    
    }LNode;  
    typedef struct LNode *LinkList; 

    双向循环链表的结构图


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

    1)构建空表

    Status InitList(LinkList L)
    {
        L = (LinkList)malloc(sizeof(LNode));
        if(!L)exit(ERROR);
        else L ->next = L ->prior = L;
        return OK;
    } 

    逻辑解析

    非常easy,就是头结点自己指自己而已~


    2)将表置空

    void ClearList(LinkList L)
    {
        LinkList p = L ->next;   //指向第一个结点 
        while(p != L)
        {
            p = p ->next;  //指向下一个结点 
            free(p->prior); //释放该结点的前驱结点 
        }
        L ->next = L ->prior = L; //自己指自己 
    }

    3)推断是否为空表

    Status ListEmpty(LinkList L)
    {
        return L->next == L && L ->prior == L?TRUE:FALSE;
    }

    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;  //指向第一个结点
        while(p != L && j < i)   //指针后移 
        {
            j++;
            p = p ->next;
        } 
        if(p == L || j > i)return ERROR;  //找不到该元素
        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 p = L ->next ->next;  //指向第二个结点
        while(p != L)   //未指向头结点
        {
            if(p ->data == choose)
            {
                before = p ->prior ->data; 
                return OK;
            }
            p = p ->next;
        } 
        return ERROR;
    }

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

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

    10)返回第i个元素的地址

    LinkList GetElemAdd(LinkList L,int i)
    {
        int j;
        LinkList p = L;
        if(i < 0 || i > ListLength(L))return NULL; //推断i值位置是否合法
        for(j = 1;j < = i;j++)
        {
            p = p ->next;
        } 
        return p;
    }   

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

    Status ListInsert(LinkList L,int i,ElemType e)
    {
        LinkList p,q;
        //推断i值是否合法
        if(i < 1 || i > ListLength(L) + 1)return ERROR;
        p =  GetElemAdd(L,i - 1);
        //NULL的话说明,第i个结点的前驱不存在。
        //这里如果头节点为第一个结点的前驱   
        if(!p)return ERROR; 
        q = (LinkList)malloc(sizeof(LNode));
        if(!q)return ERROR;
        q ->data = e;  //给新结点赋值
        q ->prior = p;  //新结点的前驱为第i - 1个结点
        q ->next = p ->next; //新结点的后记为第i个结点
        p ->next ->prior = q; //第i个结点前驱指向新结点 
        p ->next = q;  //第i-1个结点的后继指向新结点 
        return OK; 
    }

    实现逻辑图

    12)删除第i个位置的元素

    Status ListDelete(LinkList L,int i,ElemType *e)
    {
        LinkList p;
        if(i < 1)return ERROR; //推断删除位置是否合法
        p = GetElemAdd(L,i);
        if(!p)return ERROR;  //为NULL说明第i个元素不存在
        e = p ->data;
        p ->prior ->next = p ->next; //i-1个结点的后继指向滴i+1个结点
        p ->next ->prior = p ->prior; //第i+1个结点的前驱指向第i - 1个结点
        free(p); //释放第i个结点
        return OK; 
    }

    实现逻辑图

    嘿嘿,是不是认为少了个遍历表中元素的基本操作呢,别急,我们以下写个样例,
    按正序遍历链表,以及逆序来遍历表中的全部元素~


    4.简单样例:正序和逆序遍历表中元素

    执行截图

    代码实现

    #include <stdio.h>
    #include<stdlib.h>
    
    #define OK 1
    #define ERROR 0
    #define TRUE 1
    #define FALSE 0
    
    typedef int ElemType;  
    typedef int Status;
    typedef struct LNode
    {
        ElemType data;         //数据域
        struct LNode *prior;   //前驱指针 
        struct LNode *next;   //后继指针
    }LNode;  
    typedef struct LNode *LinkList;
    
    //定义一个创建N个结点的方法
     LinkList ListCreate(int N)
     {
        LinkList p,q,head;
        int i,data;
        q = head;
        head = (LinkList)malloc(sizeof(LNode)); 
        head ->prior = head;
        head ->next = head;
        p = head;
        for(i = 0;i < N;i++)
        {
            printf("请输入第%d个结点的值:",i + 1);
            scanf("%d",&data);
            q = (LinkList)malloc(sizeof(LNode));
            q ->data = data;
            p ->next = q;
            q ->prior = p;
            q ->next = head; 
            head ->prior = q; 
            p = q; 
        }
        return head;
     } 
    
     //定义一个打印结点数据的方法
     void PrintNode(ElemType e)
     {
        printf("%d	",e);
     } 
    
     //定义一个正序输出链表的方法
     void ListTraverse(LinkList L)
     {
        LinkList p = L->next;  //指向首元结点
        while(p!=L)
        {
            PrintNode(p->data);
            p = p ->next;
        } 
        printf("
    ");
     } 
    
     //定义一个逆序输出链表的方法
     void ListTraverseBack(LinkList L)
     {
        LinkList p = L ->prior;  //指向最后一个结点 
        while(p!=L)
        {
            PrintNode(p->data);
            p = p ->prior;
        } 
        printf("
    ");
     } 
    
     int main()
     {
        LinkList p;
        int N = 0;
        printf("请输入双向链表的结点个数:");
        scanf("%d", &N);
        p = ListCreate(N);
        printf("正序打印链表中的结点:
    ");
        ListTraverse(p);
        printf("逆序打印链表中的结点:
    ");
        ListTraverseBack(p); 
        return 0;
     }

    非常easy。就不BB了~


    5.本节演示样例代码下载:

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

  • 相关阅读:
    Java中使用JTS对空间几何计算(读取WKT、距离、点在面内、长度、面积、相交等)
    Java中Thread类的常用API以及使用示例
    Vue+Leaflet.PM+Turf.js实现绘制多线段并自动生成辐射区(缓冲区)
    SpringBoot中使用Redisson分布式锁的应用场景多线程、服务、节点秒杀/抢票处理
    Turf.js(地理空间GIS分析的js库),处理地图相关算法
    Java中使用CountDownLatch实现并发流程控制
    Java中数据同步synchronized关键字与Mointor的使用
    SpringBoot+Lombok+Builder实现任意个数属性的对象构造
    【20220930】连岳摘抄
    【20220928】按自己节奏走
  • 原文地址:https://www.cnblogs.com/yxysuanfa/p/7268174.html
Copyright © 2020-2023  润新知