前面留的一个问题,后文更跟新回答

单链表可以表示任意的线性关系,有些线性关系是循环的,既没有队尾元素。

将单链表中的终端结点指针端由空指针改为指向头结点,这时的单链表形成国恒一个环,改为循环链表。

插入与删除与单链表的原理甚至一模一样,工程CircleListPro,将单链表改成循环链表。

CircleList.h文件

ifndef _CIRCLELIST_H_#define _CIRCLELIST_H_typedef void CircleList;typedef struct _tag_CircleListNode CircleListNote;struct _tag_CircleListNode{  CircleListNode* next;  }CircleList* CircleList_Creat(int capacity);void CircleList_Destory(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);#endif

CiecleList.c

#include #include #include "CircleList.h"#define AVAILABLE -1//空闲位置的宏//静态链表结构体定义typedef struct _tag_CircleList{  CircleListNode header;//链表头  int length;}TCircleList;  CircleList* CircleList_Create()//o(1){  TCircleList* ret = (TCircleList*)malloc(sizeof(TCircleList));    if(ret != NULL)//指针不为0时可以继续赋值操作    {      ret->length = 0;      ret->header.next = NULL;    }  return ret;}void CircleList_Destory(CircleList* list){  free(list);}void CircleList_Clear(CircleList* list) //o(1){  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换    if(sList != NULL)//链表不为空是合法的,可以继续清空操作    {      sList->length = 0;      sList->header.next  = NULL;//第一个元素下标没有了    }}int CircleList_Length(CircleList* list)//o(1){  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换   int ret = -1;//定义一个返回值  if(sList !=NULL)//链表不为空是合法的,可以继续清空操作    {    ret = sList->length;    }      return ret;}// 插入时,如果表头是空的指向NULL,元素是空的,进行单链表元素插入时,现将插入元素// 尾结点与NULL相连,再把插入元素数据与前结点相连,再把该节点next与自己相连,去除原来NULL,构成循环链表int CircleList_Insert(CircleList* list,CircleListNode* node,int pos)//o(n)n是插入元素的位置·{  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换   int ret =(sList !=NULL)&& (pos >=0) && (node != NULL);//单链表方法完成判断  int i=0;   if(ret)//在数组中找空闲位置index  {  CircleListNode* current = (CircleListNode*)sList;    for(i = 0;(inext != NULL); i++)  {    current = current->next;  }    node->next = current->next;  current->next = node;    if(sList->length == 0)// 插入的元素是第一个,length的值为0  {    node->next =  node;// 新元素node的next指针指向自己  }    sList->length++ ;}return ret;}CircleListNode* CircleList_Get(CircleList* list,int pos)// o(n){TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换 CircleListNode* ret = NULL;//定义一个返回值int i = 0;if((sList != NULL) &&(0 <= pos)//链表不为空是合法的,长度正常,与单链表不同的是不需要pos  {    CircleListNode* current = (CircleListNode*)sList;      for(i=0;i  {    current = current->next;//第一个元素所在下标  }   ret = current->next;  }return ret;}//获取第pos个元素,将第pos个元素从链表里删除//特殊的删除第一个元素,除了将表头next移到第二个元素之外,还要将最后一个next移到第二个nextCircleListNode* CircleList_Delete(CircleList* list,int pos)//o(n){  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换   CircleListNode* ret = NULL;//定义一个返回值  int i = 0;  if( (sList !=NULL) && (0 <= pos) )//链表不为空是合法的,长度正常  {    CircleListNode* current = (CircleListNode*)sList;    CircleListNode* first = sList->header.next;// 标记第一个元素    CircleListNode* last = (CircleListNode*)CircleList_Get(sList,sList->length - 1);    // 由get函数得到最后一个元素    for(i=0;i      {        current = current->next;//第一个元素所在下标      }    ret = current->next;    current->next = ret->next;        sList->length--;        if(first == ret)// 判断删除元素是否是原来表头,first指针与原来ret指针是否是同一个      {        sList->header.next = ret->next;// 将表头指向ret        last->next = ret->next;// 指针移动到原来的第二个元素      }            if(sList->length == 0)// 如果链表空了则前面操作没有意义      {        sList->header.next = NULL;// 复原      }  }  return ret;}

main.c

#include #include #include  "CircleList.h"//自己创建的文件,而不是系统文件用双引号struct Value{  CircleListNode header;// 定义域  int v;// 真正保存数据的域}int main(int argc,char *argv[]){  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(    {      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);    }   CircleList_Destory(list);     return 0;}

为了体现循环链表的威力,引入游标:在循环链表中定义一个“当前”指针,这个指针通常称为游标,可以通过这个游标来遍历链表中所有元素。

加了游标新操作CircleList.h文件

#ifndef _CIRCLELIST_H_#define _CIRCLELIST_H_typedef void CircleList;typedef struct _tag_CircleListNode CircleListNote;struct _tag_CircleListNode{  CircleListNode* next;  }CircleList* CircleList_Creat(int capacity);void CircleList_Destory(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_Resert(CircleList* list);// 将游标移动到链表的下一个数据元素CircleListNode* CircleList_Current(CircleList* list);// 直接删除链表中某个数据元素CircleListNode* CircleList_Next(CircleList* list);#endif
#include #include #include "CircleList.h"#define AVAILABLE -1//空闲位置的宏//静态链表结构体定义typedef struct _tag_CircleList{  CircleListNode header;//链表头  CircleListNode* sLidrer;// 定义游标  int length;}TCircleList;  CircleList* CircleList_Create()//o(1){  TCircleList* ret = (TCircleList*)malloc(sizeof(TCircleList));    if(ret != NULL)//指针不为0时可以继续赋值操作    {      ret->length = 0;      ret->header.next = NULL;      ret->slider  = NULL;// 在循环链表创建的时候,没有元素,游标定义为空    }  return ret;}void CircleList_Destory(CircleList* list){  free(list);}void CircleList_Clear(CircleList* list) //o(1){  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换    if(sList != NULL)//链表不为空是合法的,可以继续清空操作    {      sList->length = 0;      sList->header.next  = NULL;//第一个元素下标没有了      sList->slider  = NULL;// 循环链表重置为复原状态。游标也重置为空    }}int CircleList_Length(CircleList* list)//o(1){  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换   int ret = -1;//定义一个返回值  if(sList !=NULL)//链表不为空是合法的,可以继续清空操作    {    ret = sList->length;    }      return ret;}// 插入时,如果表头是空的指向NULL,元素是空的,进行单链表元素插入时,现将插入元素// 尾结点与NULL相连,再把插入元素数据与前结点相连,再把该节点next与自己相连,去除原来NULL,构成循环链表int CircleList_Insert(CircleList* list,CircleListNode* node,int pos)//o(n)n是插入元素的位置·{  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换   int ret =(sList !=NULL)&& (pos >=0) && (node != NULL);//单链表方法完成判断  int i=0;   if(ret)//在数组中找空闲位置index  {    CircleListNode* current = (CircleListNode*)sList;    for(i = 0;(inext != NULL); i++)  {    current = current->next;  }    node->next = current->next;  current->next = node;    if(sList->length == 0)// 插入的元素是第一个,length的值为0  {    slider->slider = node;// 游标指向插入的第一个结点    node->next =  node;// 游标默认初始位置为0,新元素node的next指针指向自己      }    sList->length++ ;}return ret;}CircleListNode* CircleList_Get(CircleList* list,int pos)// o(n){  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换   CircleListNode* ret = NULL;//定义一个返回值  int i = 0;    if((sList != NULL) &&(0 <= pos)//链表不为空是合法的,长度正常,与单链表不同的是不需要pos    {      CircleListNode* current = (CircleListNode*)sList;          for(i=0;i    {      current = current->next;//第一个元素所在下标    }       ret = current->next;    }  return ret;}//获取第pos个元素,将第pos个元素从链表里删除//特殊的删除第一个元素,除了将表头next移到第二个元素之外,还要将最后一个next移到第二个nextCircleListNode* CircleList_Delete(CircleList* list,int pos)//o(n){  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换   CircleListNode* ret = NULL;//定义一个返回值  int i = 0;  if( (sList !=NULL) && (0 <= pos) )//链表不为空是合法的,长度正常  {    CircleListNode* current = (CircleListNode*)sList;    CircleListNode* first = sList->header.next;// 标记第一个元素    CircleListNode* last = (CircleListNode*)CircleList_Get(sList,sList->length - 1);    // 由get函数得到最后一个元素    for(i=0;i      {        current = current->next;//第一个元素所在下标      }    ret = current->next;    current->next = ret->next;        sList->length--;        if(first == ret)// 判断删除元素是否是原来表头,first指针与原来ret指针是否是同一个      {        sList->header.next = ret->next;// 将表头指向ret        last->next = ret->next;// 指针移动到原来的第二个元素      }      if(slider->slider == ret)// SLIDER指向的元素和要删除的元素指针一致      {        sList->slider = ret->next ;// slider指向ret的下一个元素      }      if(sList->length == 0)// 如果链表空了则前面操作没有意义      {        sList->header.next = NULL;// 复原        sList->slider = NULL;// 删除的元素刚好为链表最后一个元素,游标复原为空      }  }  return ret;}// 获取当前游标指向的数据元素,删除对应的CircleListNode* node这个元素o(n0CircleListNode* CircleList_DeleteNode(CircleList* list,CircleListNode* node){// 该做的检测正常做  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换   CircleListNode* ret = NULL;//定义一个返回值  int i = 0;     if (sList != NULL)   {    CircleListNode* current = (CircleListNode*)sList;// 做移动,查找node在循环链表的逻辑位置       for(i=0;ilength;i++)      {          if(current->next == node)          {              ret =current->next;              break;          }            current = current->next;          }           if(ret != NULL )// 找不到,非法元素           {               circleList_Delete(sList,i);// i就是所找到的删除位置,调用delete删除即可           }     }  return ret;}CircleListNode* CircleList_Resert(CircleList* list)// o(1)将游标重置指向链表中的第一个元素{// 该做的检测正常做  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换   CircleListNode* ret = NULL;//定义一个返回值    if(sList != NULL )  {     slist->slider = sList->header.next;// slider重置到第一个元素     ret = sList->slider ;// 返回判断重置是否成功   }   return ret;}CircleListNode* CircleList_Current(CircleList* list)//  o(1)将游标移动指向到链表中的下一个数据元素{// 该做的检测正常做  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换   CircleListNode* ret = NULL;//定义一个返回值      if(sList != NULL )  {     ret = sList->slider ;   }   return ret;  }CircleListNode* CircleList_Next(CircleList* list)//  o(1)直接删除链表中的某个数据元素 {// 该做的检测正常做  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换   CircleListNode* ret = NULL;//定义一个返回值  // 当前游标指向下一个元素    if((sList != NULL ) && (sList->slider != NULL ))  {     ret = sList->slider ;// 在移动之前把当前值保存作为返回值返回     sList->slider = ret->next;// 真正移动   }   return ret;}

循环链表的应用:约瑟夫问题

n个人围成一个圆圈,首先从第一个人从1开始报数,报到第m个人,令其出列;然后再从下一个人继续报数,报到第m个人,再另其出列……如此下去,求其出列顺序。

main.c

#include #include #include  "CircleList.h"//自己创建的文件,而不是系统文件用双引号struct Value{  CircleListNode header;// 定义域  int v;// 真正保存数据的域}int main(int argc,char *argv[]){  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(    {      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("\n");       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_Resert(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_Destory(list);     return 0;}

小结;

循环链表只是在单链表的基础上做了一个加强

循环链表完全可以代替单链表

循环链表的Next和Current操作可以高效的遍历链表中的每个元素

动态游标for循环_数据结构系列循环链表相关推荐

  1. 动态游标for循环_【【动图算法】(动态规划篇):最长回文子串

    本周继续做一道动态规划类型的题目,该题是阿里一面的一道算法题. [动图算法](动态规划篇):最长回文子串 leetcode 5 题:最长回文子串 https://leetcode-cn.com/pro ...

  2. 游标(概念、优点、分类、静态游标的使用(显示游标(显示游标的属性、遍历显示游标、显示游标的FOR循环)、隐式游标(隐式游标的属性))、动态游标的使用、静态游标和动态游标的区别、更新或删除当前游标数据

    文章目录 游标 游标概念 游标优点 游标分类 静态游标的使用 显示游标 显示游标的属性 遍历显示游标 显示游标的FOR循环 接收显式游标数据的数据类型(普通变量.记录变量.集合变量) 通过游标更新.删 ...

  3. db2存储过程 可以使用游标循环嵌套吗_DB2存储过程使用动态游标的例子

    本文将为您介绍一个 DB2 存储过程使用动态游标的例子,如果您对动态游标的使用感 兴趣的话,不妨一看,对您学习 DB2 的使用会有所帮助. CREATE PROCEDURE data_wtptest( ...

  4. 6-4 链表拼接 (20分)_数据结构之链表

    在面试过程中,数据结构和算法基本上算是研发类岗位必考的部分,而链表基本上又是数据结构中相对容易掌握.而且容易出题的部分,因此我们先整理一下链表部分的经典题目. (声明:以下所有程序都是用java编写) ...

  5. 数据结构视频教程 -《电子科技大学_罗吴蔓_数据结构》

    整个视频打包下载地址:史上最全的数据结构视频教程系列分享之<电子科技大学_罗吴蔓_数据结构>,转载请保留出处和链接! 更多优秀资源请访问:我是码农 数据结构是计算机程序设计的重要理论技术基 ...

  6. Oracle 动态游标 PL/SQL 动态SQL语句 open for [using] 语句

    PL/SQL:open for [using] 语句 2017年07月19日 09:52:55 学孩儿无牙哭做粥 阅读数:681 标签: oracleSQLPLSQL 更多 个人分类: ORACLES ...

  7. 【Oracle】PL/SQL 显式游标、隐式游标、动态游标

    在PL/SQL块中执行SELECT.INSERT.DELETE和UPDATE语句时,Oracle会在内存中为其分配上下文区(Context Area),即缓冲区.游标是指向该区的一个指针,或是命名一个 ...

  8. oracle 动态游标行数,oracle动态游标的简单实现方法

    下面就是例子程序 --明细表打印予处理  通用报表: procedure mx_print_common(pd_id in mx_pd_syn.pd_id%type, p_pd_mxb_id IN m ...

  9. MySql中游标的定义与使用方式_数据库系列:MySql中游标的定义与使用方式

    创建游标 首先在MySql中创建一张数据表: CREATE TABLE IF NOT EXISTS `store` ( `id` int(11) NOT NULL AUTO_INCREMENT, `n ...

最新文章

  1. svn 没有绿色小勾
  2. 项目中涉及到的BAPI
  3. $http中文乱码|param乱码|angular提交后台乱码|
  4. mysql冷热备_Mysql的冷备热备(数据备份)
  5. 无法开启计算机,Win7下鼠标右键无法开启计算机属性怎么办?
  6. ASP.NET MVC 1.0 学习笔记(随时更新)
  7. 云原生网络性能优化:service mesh 篇
  8. java注解拦截_轻松实现java拦截器+自定义注解
  9. CSharp中委托(一)委托、匿名函数、lambda表达式、多播委托、窗体传值、泛型委托
  10. Cesium中的Heading/Pitch/Roll
  11. 基于jsp+mysql+java+ssm实验室设备管理系统——计算机毕业设计
  12. 阿里巴巴开发规约之编程规约(1)
  13. 【UE4大数据可视化教程序章一】——生成城市与道路模型
  14. 对物联网的感悟_物联网心得体会总结
  15. matlab 分类学习工具箱 Classification Learner
  16. WordPress Avada主题评测:非常受欢迎但值得吗? (2022)
  17. Redis基础笔记(下)
  18. Mac用Visual Studio Code编写C/C++安装配置教程
  19. 你真的能解释工厂模式吗?不屑解释工厂模式的大牛不是真牛
  20. python2.7交通标志识别图_(四)深度学习初探:基于LeNet-5卷积神经网络的交通标志识别...

热门文章

  1. mybatis中sql语句传入多个参数方法
  2. UIViewController生命周期的理解
  3. pycharm快捷键_春节快结束了回单位途中总结下pycharm快捷键
  4. 项目验收文档合并技巧
  5. 为什么使用NativeJdbcExtractor
  6. android 释放bitmap fragment,,为什么重复Replace Fragment会内存泄漏
  7. arithmetic java_Java:Arithmetic
  8. haarcascades---各种分类器xml文件下载地址
  9. isinstance_Java类class isInstance()方法及示例
  10. c ++查找字符串_C ++异常处理| 查找输出程序| 套装1