循环链表
来源:互联网 发布:求最小公倍数的vb算法 编辑:程序博客网 时间:2024/06/07 03:09
循环链表
- 循环链表
- 基本概念
- 设计与实现
- 代码实现
- 优缺点
- 约瑟夫问题-循环链表典型应用
1.基本概念
循环链表的定义:
将单链表中最后一个数据元素(业务节点)的next指针指向第一个元素
循环链表拥有单链表的所有操作
创建链表
销毁链表
获取链表长度
清空链表
获取第pos个元素操作
插入元素到位置pos
删除位置pos处的元素
新增功能
:游标的定义
在循环链表中可以定义一个“当前”指针,这个指针通常称为游标,可以通过这个游标来遍历链表中的所有元素。
循环链表新操作
- 获取当前游标指向的数据元素
- 将游标重置指向链表中的第一个数据元素
- 将游标移动指向到链表中的下一个数据元素
- 直接指定删除链表中的某个数据元素
CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);CircleListNode* CircleList_Reset(CircleList* list);CircleListNode* CircleList_Current(CircleList* list);CircleListNode* CircleList_Next(CircleList* list);
2.设计与实现
插入元素的分析
1) 普通位置插入元素
2) 添加第一个元素(第一次插入元素)
3) 最后一个位置插入元素
4) 第一个位置插入元素
在第一个位置插入
删除节点
3.代码实现
- 头文件
#ifndef __CIRCLE_LIST_H__#define __CIRCLE_LIST_H__#include <stdio.h>#include <stdlib.h>#include <memory.h>typedef void CircleList;//数据类型的封装//业务数据节点typedef struct _tag_MyCicleListNode{ struct _tag_MyCicleListNode * next;//只有指针域}CircleListNode;CircleList *CircleList_Create();void CircleList_Destroy(CircleList * list);void CircleList_Clear(CircleList * list);int CircleList_Length(CircleList *list);int CircleList_Insert(CircleList * list, CircleListNode * node,int pos);CircleListNode * CircleList_Get(CircleList * list,int pos);CircleListNode * CircleList_Delete(CircleList * list,int pos);CircleListNode * CircleList_DeleteNode(CircleList * list,CircleListNode *node);CircleListNode * CircleList_Reset(CircleList * list);CircleListNode * CircleList_Current(CircleList * list);CircleListNode * CircleList_Next(CircleList * list);#endif // !__CIRCLE_LIST_H__
- 实现文件
#include "CircleList.h"//表头结点typedef struct _tagMyCircleList{ CircleListNode head;//表头结点里面的头结点, //里面存放的是指向链表第一个节点的指针 CircleListNode * slider;//游标 int length;//业务数据节点个数}TCircleList;/*创建链表*/CircleList *CircleList_Create(){ TCircleList * tmp = NULL; /*为表头结点分配内存*/ tmp = (TCircleList *)malloc(sizeof(TCircleList)); if (NULL == tmp)//合法性检测 { printf("malloc error!\n"); return NULL; } /*初始化表头节点*/ memset(tmp,0,sizeof(TCircleList)); /*返回分配好内存的循环链表表头节点首地址*/ return tmp;}/*销毁链表*/void CircleList_Destroy(CircleList * list){ if (list != NULL) { free(list); list = NULL; } else{ printf("argv error!\n"); } return ;}/*清空链表*/void CircleList_Clear(CircleList * list){ TCircleList * tmp = NULL; if (list == NULL) { printf("argv error!\n"); return; } tmp = (TCircleList *)list; /*全部恢复初始状态*/ tmp->head.next = NULL; tmp->slider = NULL; tmp->length = 0; return ;}/*获取业务节点个数*/int CircleList_Length(CircleList *list){ TCircleList * tmp = NULL; if (list == NULL) { printf("argv error!\n"); return -1; } tmp = (TCircleList *)list; return tmp->length;}/*插入节点*/int CircleList_Insert(CircleList * list, CircleListNode * node, int pos){ TCircleList * tmp = NULL;//表头结点辅助指针 CircleListNode * current = NULL;//当前节点指针 CircleListNode * last_node = NULL;//尾节点辅助指针 int i = 0;//循环变量 /*1. 合法性检测*/ if (list == NULL || node == NULL || pos < 0) { printf("argv error!\n"); return -1; } tmp = (TCircleList *)list; /*if (pos >= tmp->length)//循环链表不能进行容错纠正, //否则会卡死在第二次循环遍历的第一个节点 { pos = tmp->length; }*/ /*2.当前指针指向表头结点*/ current = &(tmp->head); /*3.向后跳pos个节点,使得当前指针指向pos-1个节点*/ for (i = 0; i < pos && current->next != NULL;i++) { current = current->next; } /*4.进行常规的插入操作*/ node->next = current->next; current->next = node; /*5.判断此次插入操作是否是第一次插入操作*/ if (tmp->length == 0) { tmp->slider = node;//如果是第一次操作,更新游标指向第一个插入的节点 } /*6.更新此时链表的长度*/ tmp->length++; /*7.判断此次插入是否是从表头插入(头插法)*/ if (current == &(tmp->head)) { /*7.1 获取此时链表中最后一个业务节点*/ last_node = CircleList_Get((CircleList *)tmp, tmp->length - 1); /*7.2 将尾节点指向新插入的头结点*/ last_node->next = node; } /*8. 插入成功返回0*/ return 0;}CircleListNode * CircleList_Get(CircleList * list, int pos){ /*相关辅助变量的声明*/ TCircleList * tmp = NULL; CircleListNode * current = NULL; int i = 0; /*合法性检测*/ if (list == NULL || pos < 0) { printf("argv error!\n"); return NULL; } tmp = (TCircleList *)list; /*if (pos >= tmp->length) { pos = tmp->length; }*/ /*当前节点指针的初始化--指向表头结点*/ current = &(tmp->head); /*向后跳pos个节点--当前指针指向第pos-1个节点*/ for (i = 0; i < pos && current->next != NULL; i++) { current = current->next; } /*返回目标节点*/ return current->next;}CircleListNode * CircleList_Delete(CircleList * list, int pos){ /*1. 相关辅助变量的声明*/ TCircleList * tmp = NULL;//表头结点指针 CircleListNode * current = NULL;//当前节点指针 CircleListNode * res_node = NULL;//要删除的节点指针 CircleListNode * last_node = NULL;//最后一个节点指针 int i = 0;//循环变量 /*2. 合法性检测*/ if (list == NULL || pos < 0 ) { printf("argv error!\n"); return NULL; } tmp = (TCircleList *)list; /*3. 防止对空链表进行删除操作*/ if (tmp->length <= 0) { printf("length error!\n"); return NULL; } /*if (pos >= tmp->length) { pos = tmp->length; }*/ /*4. 初始化当前节点指针*/ current = &(tmp->head); /*5. 向后跳pos个节点*/ for (i = 0; i < pos && current->next != NULL; i++) { current = current->next; } /*6. 判断是否是对头结点进行删除操作*/ if (current == &(tmp->head)) { //如果删除头结点,要先获取最后一个节点的指针在进行删除操作 last_node = CircleList_Get((CircleList *)tmp, tmp->length - 1); } /*7. 进行常规的删除操作*/ res_node = current->next;//获取要删除的节点 current->next = res_node->next; /*8. 修改此时链表长度*/ tmp->length--; /*9. 如果删除头结点则需要更新最后一个节点的指向, 否则最后一个节点指向的是一块非法内存空间*/ if (last_node != NULL) { last_node->next = current->next; tmp->head.next = current->next; } /*10 .如果删除的节点是游标所指的节点*/ if (tmp->slider == res_node) { //将游标指向下一个节点 tmp->slider = res_node->next; } /*11. 如果删除该节点以后是空链表--进行清空操作*/ if (tmp->length == 0) { tmp->head.next = NULL; tmp->slider = NULL; } /*12. 返回被删除的节点*/ return res_node;}/*从给定的链表中删除指定的节点*/CircleListNode * CircleList_DeleteNode(CircleList * list, CircleListNode *node){ /*相关辅助变量声明*/ TCircleList * tmp = NULL; CircleListNode * current = NULL; CircleListNode * res_node = NULL;//指向要删除的节点 int i = 0; /*参数合法性检测*/ if (list == NULL || node == NULL) { printf("argv error!\n"); return NULL; } tmp = (TCircleList *)list; /*当前节点辅助指针变量初始化--指向表头结点*/ current = &(tmp->head); /*遍历链表,寻找与给定节点相同的节点*/ for (i = 0; i < tmp->length && current->next != NULL;i++) { /*找到目标节点*/ if (current->next == node) { res_node = current->next;//记录目标节点的位置 break; } current = current->next;//更新当前节点辅助指针变量 } /*确实找到目标节点*/ if (res_node != NULL) { /*调用节点删除函数*/ CircleList_Delete((CircleList *)tmp, i);//此时的i就是目标节点在链表相应的pos } /*返回目标节点*/ return res_node;}/*使链表的游标复位--指向第一个业务节点*/CircleListNode * CircleList_Reset(CircleList * list){ TCircleList * tmp = NULL; /*合法性*/ if (list == NULL) { printf("argv error!\n"); return NULL; } tmp = (TCircleList *)list; /*更新游标指向第一个业务节点*/ tmp->slider = tmp->head.next; return tmp->slider;}/*返回当前游标指向的节点*/CircleListNode * CircleList_Current(CircleList * list){ TCircleList * tmp = NULL; /*合法性*/ if (list == NULL) { printf("argv error!\n"); return NULL; } tmp = (TCircleList *)list; return tmp->slider;//返回当前游标指向的节点}/*返回当前游标指向的节点并把游标指向下一个节点*/CircleListNode * CircleList_Next(CircleList * list){ TCircleList * tmp = NULL; CircleListNode * res_node = NULL;//保存当前游标指向的节点 /*合法性*/ if (list == NULL) { printf("argv error!\n"); return NULL; } tmp = (TCircleList *)list; /*防止是空链表--游标是空指针*/ if (tmp->slider == NULL) { printf("tmp->slider error!\n"); return NULL; } res_node = tmp->slider;//记录当前游标指向的节点 tmp->slider = res_node->next;//更新游标指向下一个节点 /*返回当前节点*/ return res_node;}
- 测试文件
#include "CircleList.h"struct Value{ CircleListNode header; int v;};void main(){ int i = 0; CircleList* list = CircleList_Create(); struct Value v1; struct Value v2; struct Value v3; struct Value v4; struct Value v5; struct Value v6; struct Value v7; struct Value v8; v1.v = 1; v2.v = 2; v3.v = 3; v4.v = 4; v5.v = 5; v6.v = 6; v7.v = 7; v8.v = 8; CircleList_Insert(list, (CircleListNode*)&v1, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v2, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v3, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v4, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v5, 5); CircleList_Delete(list, 0); for (i = 0; i<2 * CircleList_Length(list); i++) { struct Value* pv = (struct Value*)CircleList_Get(list, i); printf("%d\n", pv->v); } printf("\n"); while (CircleList_Length(list) > 0) { struct Value* pv = (struct Value*)CircleList_Delete(list, 0); printf("%d\n", pv->v); } printf("aloha\n"); CircleList_Destroy(list); system("pause"); return;}
4.优缺点
优点:功能强了。
- 循环链表只是在单链表的基础上做了一个加强,循环链表可以完全取代单链表的使用;
- 循环链表的Next和Current操作可以高效的遍历链表中的所有元素
缺点:
代码复杂度提高了
5.约瑟夫问题-循环链表典型应用
n 个人围成一个圆圈,首先第 1 个人从 1 开始一个人一个人顺时针报数,报到第 m 个人,令其出列。然后再从下一 个人开始从 1 顺时针报数,报到第 m 个人,再令其出列,…,如此下去,求出列顺序。
#include "CircleList.h"struct Value{ CircleListNode header; int v;};void main(){ int i = 0; CircleList* list = CircleList_Create(); struct Value v1, v2, v3, v4, v5, v6, v7, v8; v1.v = 1; v2.v = 2; v3.v = 3; v4.v = 4; v5.v = 5; v6.v = 6; v7.v = 7; v8.v = 8; CircleList_Insert(list, (CircleListNode*)&v1, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v2, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v3, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v4, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v5, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v6, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v7, CircleList_Length(list)); CircleList_Insert(list, (CircleListNode*)&v8, CircleList_Length(list)); for (i = 0; i < CircleList_Length(list); i++) { //获取游标所指元素,然后游标下移 struct Value* pv = (struct Value*)CircleList_Next(list); printf("%d\n", pv->v); } printf("\n"); //重置游标 CircleList_Reset(list); while (CircleList_Length(list) > 0) { struct Value* pv = NULL; for (i = 1; i < 3; i++) { CircleList_Next(list); } pv = (struct Value*)CircleList_Current(list); printf("%d\n", pv->v); CircleList_DeleteNode(list, (CircleListNode*)pv); } CircleList_Destroy(list); system("pause"); return;}
0 0
- 循环链表实现循环队列
- 循环链表与循环队列
- 循环链表
- 循环链表
- 双向循环链表
- 双向循环链表
- 循环链表实验
- 链表::循环链表
- 双向循环链表
- 双向循环链表
- 循环链表
- 循环链表
- 循环链表
- 循环链表建立
- 数据结构 循环链表
- 双向循环链表
- 循环链表详解
- 循环链表
- Shiro身份验证Realm
- 五步整理你的css文件
- 从一张图片截取JButton多种状态的Icon
- java基础10(Javaoo5)——多态与抽象
- java试题库1
- 循环链表
- java随机生成邀请码(数字+字母)
- zookeeper的简单安装以及基于intellij idea的可视化工具的使用
- BestCoder Round #89 1001 & HDU 5944 Fxx and string【字符串,等比数列】
- python文件操作
- 一阶微分算子提取边缘效果
- Cygwin:Windows下的Linux终端模拟器
- MIT牛人解说数学体系
- Linux文件系统只读Read-only file system的快速解决方法