C语言单循环链表的表示与实现实例详解

 更新时间:2014年07月07日 15:55:58   投稿:shichen2014  
这篇文章主要介绍了C语言单循环链表的表示与实现,对于学习数据结构与算法的朋友来说很有参考借鉴价值,需要的朋友可以参考下

1.概述:

对于一个循环链表来说,其首节点和末节点被连接在一起。这种方式在单向和双向链表中皆可实现。要转换一个循环链表,可以选择开始于任意一个节点然后沿着列表的任一方向直到返回开始的节点。再来看另一种方法,循环链表可以被视为“无头无尾”。这种列表很利于节约数据存储缓存, 假定你在一个列表中有一个对象并且希望所有其他对象迭代在一个非特殊的排列下。

指向整个列表的指针可以被称作访问指针。


用单向链表构建的循环链表

循环链表中第一个节点之前就是最后一个节点,反之亦然循环链表的无边界使得在这样的链表上设计算法会比普通链表更加容易。对于新加入的节点应该是在第一个节点之前还是最后一个节点之后可以根据实际要求灵活处理,区别不大(详见下面实例代码)。当然,如果只会在最后插入数据(或者只会在之前),处理也是很容易的。
另外有一种模拟的循环链表,就是在访问到最后一个节点之后的时候,手工的跳转到第一个节点。访问到第一个节点之前的时候也一样。这样也可以实现循环链表的功能,在直接用循环链表比较麻烦或者可能会出现问题的时候可以用。

2.程序实现:

/* c2-2.h 线性表的单链表存储结构 */
 struct LNode
 {
  ElemType data;
  struct LNode *next;
 };
 typedef struct LNode *LinkList; /* 另一种定义LinkList的方法 */

/* bo2-4.c 设立尾指针的单循环链表(存储结构由c2-2.h定义)的12个基本操作 */
 Status InitList_CL(LinkList *L)
 { /* 操作结果:构造一个空的线性表L */
  *L=(LinkList)malloc(sizeof(struct LNode)); /* 产生头结点,并使L指向此头结点 */
  if(!*L) /* 存储分配失败 */
   exit(OVERFLOW);
  (*L)->next=*L; /* 指针域指向头结点 */
  return OK;
 }
 Status DestroyList_CL(LinkList *L)
 { /* 操作结果:销毁线性表L */
  LinkList q,p=(*L)->next; /* p指向头结点 */
  while(p!=*L) /* 没到表尾 */
  {
   q=p->next;
   free(p);
   p=q;
  }
  free(*L);
  *L=NULL;
  return OK;
 }
 Status ClearList_CL(LinkList *L) /* 改变L */
 { /* 初始条件:线性表L已存在。操作结果:将L重置为空表 */
  LinkList p,q;
  *L=(*L)->next; /* L指向头结点 */
  p=(*L)->next; /* p指向第一个结点 */
  while(p!=*L) /* 没到表尾 */
  {
   q=p->next;
   free(p);
   p=q;
  }
  (*L)->next=*L; /* 头结点指针域指向自身 */
  return OK;
 }
 Status ListEmpty_CL(LinkList L)
 { /* 初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */
  if(L->next==L) /* 空 */
   return TRUE;
  else
   return FALSE;
 }
 int ListLength_CL(LinkList L)
 { /* 初始条件:L已存在。操作结果:返回L中数据元素个数 */
  int i=0;
  LinkList p=L->next; /* p指向头结点 */
  while(p!=L) /* 没到表尾 */
  {
   i++;
   p=p->next;
  }
  return i;
 }
 Status GetElem_CL(LinkList L,int i,ElemType *e)
 { /* 当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR */
  int j=1; /* 初始化,j为计数器 */
  LinkList p=L->next->next; /* p指向第一个结点 */
  if(i<=0||i>ListLength_CL(L)) /* 第i个元素不存在 */
   return ERROR;
  while(j<i)
  { /* 顺指针向后查找,直到p指向第i个元素 */
   p=p->next;
   j++;
  }
  *e=p->data; /* 取第i个元素 */
  return OK;
 }
 int LocateElem_CL(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType))
 { /* 初始条件:线性表L已存在,compare()是数据元素判定函数 */
  /* 操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。 */
  /*      若这样的数据元素不存在,则返回值为0 */
  int i=0;
  LinkList p=L->next->next; /* p指向第一个结点 */
  while(p!=L->next)
  {
   i++;
   if(compare(p->data,e)) /* 满足关系 */
    return i;
   p=p->next;
  }
  return 0;
 }
 Status PriorElem_CL(LinkList L,ElemType cur_e,ElemType *pre_e)
 { /* 初始条件:线性表L已存在 */
  /* 操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱, */
  /*      否则操作失败,pre_e无定义 */
  LinkList q,p=L->next->next; /* p指向第一个结点 */
  q=p->next;
  while(q!=L->next) /* p没到表尾 */
  {
   if(q->data==cur_e)
   {
    *pre_e=p->data;
    return TRUE;
   }
   p=q;
   q=q->next;
  }
  return FALSE;
 }
 Status NextElem_CL(LinkList L,ElemType cur_e,ElemType *next_e)
 { /* 初始条件:线性表L已存在 */
  /* 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继, */
  /*      否则操作失败,next_e无定义 */
  LinkList p=L->next->next; /* p指向第一个结点 */
  while(p!=L) /* p没到表尾 */
  {
   if(p->data==cur_e)
   {
    *next_e=p->next->data;
    return TRUE;
   }
   p=p->next;
  }
  return FALSE;
 }
 Status ListInsert_CL(LinkList *L,int i,ElemType e) /* 改变L */
 { /* 在L的第i个位置之前插入元素e */
  LinkList p=(*L)->next,s; /* p指向头结点 */
  int j=0;
  if(i<=0||i>ListLength_CL(*L)+1) /* 无法在第i个元素之前插入 */
   return ERROR;
  while(j<i-1) /* 寻找第i-1个结点 */
  {
   p=p->next;
   j++;
  }
  s=(LinkList)malloc(sizeof(struct LNode)); /* 生成新结点 */
  s->data=e; /* 插入L中 */
  s->next=p->next;
  p->next=s;
  if(p==*L) /* 改变尾结点 */
   *L=s;
  return OK;
 }
 Status ListDelete_CL(LinkList *L,int i,ElemType *e) /* 改变L */
 { /* 删除L的第i个元素,并由e返回其值 */
  LinkList p=(*L)->next,q; /* p指向头结点 */
  int j=0;
  if(i<=0||i>ListLength_CL(*L)) /* 第i个元素不存在 */
   return ERROR;
  while(j<i-1) /* 寻找第i-1个结点 */
  {
   p=p->next;
   j++;
  }
  q=p->next; /* q指向待删除结点 */
  p->next=q->next;
  *e=q->data;
  if(*L==q) /* 删除的是表尾元素 */
   *L=p;
  free(q); /* 释放待删除结点 */
  return OK;
 }
 Status ListTraverse_CL(LinkList L,void(*vi)(ElemType))
 { /* 初始条件:L已存在。操作结果:依次对L的每个数据元素调用函数vi()。一旦vi()失败,则操作失败 */
  LinkList p=L->next->next;
  while(p!=L->next)
  {
   vi(p->data);
   p=p->next;
  }
  printf("\n");
  return OK;
 }

/* main2-4.c 单循环链表,检验bo2-4.c的主程序 */
 #include"c1.h"
 typedef int ElemType;
 #include"c2-2.h"
 #include"bo2-4.c"
 Status compare(ElemType c1,ElemType c2)
 {
  if(c1==c2)
   return TRUE;
  else
   return FALSE;
 }
 void visit(ElemType c)
 {
  printf("%d ",c);
 }
 void main()
 {
  LinkList L;
  ElemType e;
  int j;
  Status i;
  i=InitList_CL(&L); /* 初始化单循环链表L */
  printf("初始化单循环链表L i=%d (1:初始化成功)\n",i);
  i=ListEmpty_CL(L);
  printf("L是否空 i=%d(1:空 0:否)\n",i);
  ListInsert_CL(&L,1,3); /* 在L中依次插入3,5 */
  ListInsert_CL(&L,2,5);
  i=GetElem_CL(L,1,&e);
  j=ListLength_CL(L);
  printf("L中数据元素个数=%d,第1个元素的值为%d。\n",j,e);
  printf("L中的数据元素依次为:");
  ListTraverse_CL(L,visit);
  PriorElem_CL(L,5,&e); /* 求元素5的前驱 */
  printf("5前面的元素的值为%d。\n",e);
  NextElem_CL(L,3,&e); /* 求元素3的后继 */
  printf("3后面的元素的值为%d。\n",e);
  printf("L是否空 %d(1:空 0:否)\n",ListEmpty_CL(L));
  j=LocateElem_CL(L,5,compare);
  if(j)
   printf("L的第%d个元素为5。\n",j);
  else
   printf("不存在值为5的元素\n");
  i=ListDelete_CL(&L,2,&e);
  printf("删除L的第2个元素:\n");
  if(i)
  {
   printf("删除的元素值为%d,现在L中的数据元素依次为:",e);
   ListTraverse_CL(L,visit);
  }
  else
   printf("删除不成功!\n");
  printf("清空L:%d(1: 成功)\n",ClearList_CL(&L));
  printf("清空L后,L是否空:%d(1:空 0:否)\n",ListEmpty_CL(L));
  printf("销毁L:%d(1: 成功)\n",DestroyList_CL(&L));
 }

相关文章

  • C语言素数(质数)判断的3种方法举例

    C语言素数(质数)判断的3种方法举例

    这篇文章主要给大家介绍了关于C语言素数(质数)判断的3种方法,质数是只能被1或者自身整除的自然数(不包括1),称为质数,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-11-11
  • 一文详解C++中的类型转化

    一文详解C++中的类型转化

    说起类型转化,我们在C语言之前的学习中可以了解到,类型转换可以分为两种情况:隐式类型转化;显示类型转化。但是为什么在c++中还要继续对类型转化做文章呢?我们一起来看
    2023-04-04
  • C++ 基于BFS算法的走迷宫自动寻路的实现

    C++ 基于BFS算法的走迷宫自动寻路的实现

    这篇文章主要为大家介绍了C++ 基于BFS算法实现走迷宫自动寻路,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • C语言中getchar和putchar的使用方法详解

    C语言中getchar和putchar的使用方法详解

    我们知道scanf函数可以从键盘输入信息,而printf则可以输出信息,同样地,getchar和putchar也有同样的功能,下面我来给大家介绍putchar和getchar的使用方法,需要的朋友可以参考下
    2023-08-08
  • C及C++ 基础循环示例详解

    C及C++ 基础循环示例详解

    这篇文章主要介绍了C及C++ 中的循环示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • 浅谈C++ 设计模式的基本原则

    浅谈C++ 设计模式的基本原则

    这篇文章主要介绍了++ 设计模式的基本原则,主要的目标是实现最终目的,高内聚,低耦合,开放封闭原则类的改动是通过增加代码进行的,感兴趣的小伙伴可参考下面文章的具体内容
    2021-09-09
  • visual studio 2013中配置opencv图文教程 Opencv2.4.9安装配置教程

    visual studio 2013中配置opencv图文教程 Opencv2.4.9安装配置教程

    这篇文章主要为大家详细介绍了Opencv2.4.9安装教程,以及在visualstudio 2013中opencv的配置步骤,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • C++右值引用与移动构造函数基础与应用详解

    C++右值引用与移动构造函数基础与应用详解

    左值和右值都是针对表达式,左值是指表达式结束后依然存在的持久对象,右值是指表达式结束时就不再存在的临时对象,下面这篇文章主要给大家介绍了关于C++11右值引用和移动语义的相关资料,需要的朋友可以参考下
    2023-02-02
  • C/C++实现动态数组的示例详解

    C/C++实现动态数组的示例详解

    动态数组相比于静态数组具有更大的灵活性,因为其大小可以在运行时根据程序的需要动态地进行分配和调整,本文为大家介绍了C++实现动态数组的方法,需要的可以参考下
    2023-08-08
  • C语言可变参数列表的用法与深度剖析

    C语言可变参数列表的用法与深度剖析

    这篇文章主要给大家介绍了关于C语言可变参数列表的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2022-02-02

最新评论