首页 > 编程知识 正文

单向循环链表的特点,循环链表

时间:2023-05-04 03:41:06 阅读:228261 作者:3496

每日一句

It’s gonna be ok.

一切都会好的。

内容概要 单向循环链表

首先来看看图示:

图展示的是一个单向循环链表,他跟以上的单向链表对比只是多了一个指向头结点的 指针,因此,他们的算法几乎是一样的。
第一,设计节点。 单向循环链表的节点跟单向链表完全一致。

第二,初始化空链表。 跟单向链表类似,我们既可以初始化一个带有头结点的空循环链表,也可以不要头结点:

这边把两个步骤放在一起了

linklist init_list(void) // 带有头结点的单循环链表 { linklist head = malloc(sizeof(listnode)); head->next = head;return head; }

然后就是插入节点的操作,跟单链表一样,可以去看我前面的文章,链接https://blog.csdn.net/qq_41835250/article/details/107568684

第三,由于单循环链表的特点,删除一个指定的节点时我们可以不需要头指针,而仅仅通过该 待删除节点的指针就可以回溯到他的前一个节点,进而实现删除操作:

void remove_node(linklist delete) { linklist tmp = delete; while(tmp->next != delete) // 从 delete开始,绕一圈找到其前面的节点{ tmp = tmp->next;}tmp->next = delete->next; delete->next = NULL;}

第四,移动节点。 在单链表中,移动节点需要三个参数,因为移动的目的地可能在原始位置的前面,由于 没有头指针的情况下无法回溯回去,因此必须要链表的头指针来遍历。但是对于单循环链表
而言则不存在这个问题,链表中的任意一个节点,都可以从另一个任意节点出发找到,因此 移动节点不需要三个参数,请看代码:

void move_node(linklist p, linklist anchor) { if(p == NULL || anchor == NULL) return;remove_node(p); // 先将要移动的节点从链表中剔除 insert_node(anchor, p); // 再插入指定的地方}

第五,查找节点。 同理,单循环链表的查找跟单链表的查找基本是一样的,只是在判断是否遍历完链表的 条件上有所差别:

linklist find_node(linklist mylist, int data) { if(is_empty(mylist)) return NULL;linklist tmp;for(tmp=mylist->next; tmp!=mylist; tmp=tmp->next) {if(data == tmp->data) break; } return tmp==mylist ? NULL : tmp; // 如果遍历回到 mylist 则表示找不到} 双向循环链表

通过上面的内容,可以看出来单循环链表的缺点就是每一次遍历都需要从头开始,不能从任意位置来遍历,于是双向循环链表的出现就是来解决这个问题的。
第一,设计节点。 双向链表的节点当然需要两个指针,节点的设计如下:

typedef struct node{int data;struct node *prev;struct node *next;}listnode, *linklist;

这里需要注意一下,:节点中的 prev 和 next 指针均是 struct node 型的指针,他们都是指向本结 构体类型的相邻节点的。
第二,初始化空链表。
因为一般情况下我们所使用的大都是带头结点的双向循环链表,所以我就直接使用带头结点的。

linklist init_list(void){linklist mylist = malloc(sizeof(listnode));if(mylist != NULL){mylist->prev = mylist->next = mylist;}return mylist;}

第三,插入节点
由于双向循环链表的特征,所以可以有两种插入方式,头插法和尾插法,先来看看头插法的图示步骤:

第 1 步,将新节点的 prev 指针指向 anchor 节点:new->prev = anchor
第 2 步,将新将节点的 next指向 anchor 下一个节点:new->next = anchor->next
第 3 步,将节点 anchor 的 next 指针指向新节点 new:anchor->next = new
第 4 步,将 anchor 原来的下一个节点的 prev 指针指向 new 节点: new->next->prev = new
把它们链接起来就是

void insert_next(linklist new, linklist anchor) { if(new == NULL || anchor == NULL) return;new->prev = anchor; // 第①步 new->next = anchor->next; // 第②步anchor->next = new; // 第③步 new->next->prev = new; // 第④步}

尾插法也类似,不过是把节点插在头节点的后面
还是画个图示:

步骤:
第 1 步,将新节点的 prev 指向 anchor 的前节点:new->prev = anchor->prev
第 2 步,将新节点的 next 指向 anchor:new->next = anchor
第 3 步,将 anchor 的前节点的 next 指向新节点:anchor->prev->next = new
第 4 步,将 anchor 的前驱指针指向 new:anchor->prev = new;
代码:

void insert_prev(linklist new, linklist anchor){ if(new == NULL || anchor == NULL) return;new->prev = anchor->prev; new->next = anchor;new->prev->next = new;anchor->prev = new;}

第四,删除节点。
删除一个节点的步骤很简单,只需要将其前趋节点指向其后续节点,将其后续节点指向其前驱节点即可,另外要注意,需将被删除节点的前后向指针置空,使其彻底从链表中脱离 开来,防止错误的访问。

void remove_node(linklist delete) { if(delete == NULL) return;delete->prev->next = delete->next; delete->next->prev = delete->prev; delete->prev = NULL; delete->next = NULL; //这两行代码用来将要删除的节点彻底脱离}

第五,移动节点。
移动节点很好理解,就是一个删除一个移动,画个图就很好理解

代码

void move_next(linklist p, linklist anchor) { remove_node(p); // 将要移动的节点从链表中移除insert_next(p, anchor); // 将节点插入锚点 anchor 的后面 }

第六,查找节点。 节点的查找无非就是对链表进行遍历,从头开始找,找一圈找到为止。直接上代码:

linklist find_node(int data, linklist mylist){if(is_empty(mylist))return NULL;linklist tmp = mylist->next; // 让 tmp从第一个节点开始while(tmp != mylist){if(tmp->data == data)return tmp;tmp=tmp->next; // 不断地向后遍历,查找}return NULL;}

如果看到这里你都已经基本看明白了,那么你用双向循环链表写一个学生管理系统什么的已经没问题了,以前我也是学到这里也做过,如果需要欢迎私信。
写到这里突然想起来Linux里还有一个叫做内核链表的东西,下一篇更新一下内核链表。
每一个功能我都写成函数以便于调用,如有问题,欢迎指正。
感谢观看。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。