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

来源:互联网 发布:羊毛毡淘宝哪家好 编辑:程序博客网 时间:2024/04/26 22:26

小猪的数据结构辅助教程——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;} 

逻辑解析

很简单,就是头结点自己指自己而已~


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 0typedef 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\t",e); }  //定义一个正序输出链表的方法 void ListTraverse(LinkList L) {    LinkList p = L->next;  //指向首元结点    while(p!=L)    {        PrintNode(p->data);        p = p ->next;    }     printf("\n"); }  //定义一个逆序输出链表的方法 void ListTraverseBack(LinkList L) {    LinkList p = L ->prior;  //指向最后一个结点     while(p!=L)    {        PrintNode(p->data);        p = p ->prior;    }     printf("\n"); }  int main() {    LinkList p;    int N = 0;    printf("请输入双向链表的结点个数:");    scanf("%d", &N);    p = ListCreate(N);    printf("正序打印链表中的结点:\n");    ListTraverse(p);    printf("逆序打印链表中的结点:\n");    ListTraverseBack(p);     return 0; }

很简单,就不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

1 0
原创粉丝点击