@coder-pig
2019-05-31T06:24:47.000000Z
字数 3823
阅读 2148
数据结构

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

双向循环链表的特点:
上面也说了,空间换时间,比起循环链表只是多了一个指向前驱的指针
特点的话:
判断空表:L ->next = L -> prior = L;
存储结构:
typedef struct LNode{ElemType data; //数据域struct LNode *prior; //前驱指针struct LNode *next; //后继指针}LNode;typedef struct LNode *LinkList;
双向循环链表的结构图:

Status InitList(LinkList L){L = (LinkList)malloc(sizeof(LNode));if(!L)exit(ERROR);else L ->next = L ->prior = L;return OK;}
逻辑解析:
很简单,就是头结点自己指自己而已~

void ClearList(LinkList L){LinkList p = L ->next; //指向第一个结点while(p != L){p = p ->next; //指向下一个结点free(p->prior); //释放该结点的前驱结点}L ->next = L ->prior = L; //自己指自己}
Status ListEmpty(LinkList L){return L->next == L && L ->prior == L?TRUE:FALSE;}
void DestoryList(LinkList L){ClearList(L);free(L);L = NULL;}
int ListLength(LinkList L){int i = 0;LinkList p = L ->next;while(p != L){i++;p = p ->next;}return 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;}
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}
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;}
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;}
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;}
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;}
实现逻辑图:

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;}
实现逻辑图:

嘿嘿,是不是觉得少了个遍历表中元素的基本操作呢,别急,我们下面写个例子,
按正序遍历链表,以及逆序来遍历表中的所有元素~
运行截图:

代码实现:
#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了~
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
