@coder-pig
2019-05-31T06:04:29.000000Z
字数 2804
阅读 1829
数据结构

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

循环链表的特点:
上面也说了,比单链表稍微高比格一点的地方就是:
链表最后一个结点的指针域指向了头结点而已,这样形成所谓的环,就是循环单链表了,呵呵!
特点的话有:
我们之前判断单链表是否为空: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;
单链表的结构图:

如上图,加入我们的链表很长的话,从表头找到表尾是很费时的,所以循环链表往往是设置尾指针!
而不是设置头指针!尾指针!尾指针!尾指针!
Status InitList(LinkList L){L = (LinkList)malloc(sizeof(LNode));if(!L)exit(ERROR);L ->next = L; //自己指自己~(头节点指针域指向头结点)return OK;}
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; //自己指自己}
有头节点的哦~
Status ListEmpty(LinkList L){return L!=L ->next?FALSE:TRUE;}
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 ->next; //指向第一个结点if(i <= 0||i >ListLength)return ERROR; //判断插入位置是否合法while(j < i){j++;p = p ->next;}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 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;}
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;}}}
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;
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;指向删除结点的前一个节点~
void ListTraverser(LinkList L,void(*visit)(ElemType)){LinkList p = L ->next ->next; //指向第一个结点while(p != L ->next){visit(p ->data);p = p ->next;}printf("\n");}
https://github.com/coder-pig/Data-structure-auxiliary-tutorial/blob/master/List/list4.c


