循环单链表

定义

概念

循环单链表是在单链表的基础上,将链表最后一个结点的 next 指针域指向了链表的第一个结点(如果单链表是带头结点的则最后一个结点的 next 指针域指向头结点;如果单链表是不带头结点的则最后一个结点的 next 指针域指向开始结点)。

结构体

循环单链表的链表结点同单链表的结点一样,都是由一个数据域和指针域组成。其中数据域存储当前节点的数据值,而指针域存储当前节点的后继节点的地址。如图所示:

循环单链表结点结构体代码如下:

/*** 循环单链表的节点*/
typedef struct CLNode {/*** 链表节点的数据域*/int data;/*** 链表节点的指针域,指向后继节点*/struct CLNode *next;
} CLNode;

特点

单链表有的特点,循环单链表也有:

  • 链式存储线性表时,不需要使用地址连续的存储元素,即不要求逻辑上相邻的元素在物理位置上也相邻。
  • 单链表由于是通过『链』建立起的数据元素之间的逻辑关系,插入和删除操作不需要移动元素,只需要修改链结点指针域的指向。
  • 因为单链表的元素是离散地分布在存储空间中,所以单链表不能随机存取,如果要找到某个数据元素,最坏情况下需要遍历整个单链表。
  • 单链表存储数据不需要大量连续存储空间,但单链表结点除了存储数据值之外,还附加有指针域,就存在浪费存储空间的缺点。

循环单链表独有的特点:

  • 最后一个结点的指针域指向链表的第一个结点(如果是带头结点的循环单链表则指向头结点;如果是不带头结点的循环单链表则指向开始结点)。
  • 循环单链表可以实现从任一结点出发访问链表中的任何结点,而单链表只能从任一结点出发后访问这个结点本身及其之后的所有结点。

基本操作

注:如无特殊说明,下面关于链表的所有操作都是基于带头结点的链表。完整代码请参考:

  • CircularLinkedList.c

  • CircularLinkedList.java

  • CircularLinkedListTest.java

概述

注:下面都是 C 语言代码,所以如果要对链表进行删除或新增操作,链表参数都是双指针。如果要使用 C++ 的引用则改成 *&。一般如果是考研建议使用 & 引用,避免双指针。

循环单链表的常见操作如下:

  • void init(CLNode **list):初始化带头结点的循环单链表。其中 list 是未初始化的循环单链表。
  • CLNode *createByHead(CLNode **list, int nums[], int n):通过头插法常见循环单链表。其中 list 是未初始化的循环单链表;nums 是待批量插入到循环单链表中的数组;n 是数组长度。返回创建成功的循环单链表。
  • CLNode *createByTail(CLNode **list, int nums[], int n):通过尾插法常见循环单链表。其中 list 是未初始化的循环单链表;nums 是待批量插入到循环单链表中的数组;n 是数组长度。返回创建成功的循环单链表。
  • int insert(CLNode **list, int i, int ele):在循环单链表第 i 个位置插入值为 ele 的新结点。其中 list 是循环单链表;i 是指定结点位置,从 1 开始;ele 是待插入元素值。如果插入成功则返回 1,否则返回 0。
  • void insertFirst(CLNode **list, int ele):在循环单链表的头部插入新元素。其中 list 是循环单链表;ele 是新元素值。
  • void insertLast(CLNode **list, int ele):在循环单链表的尾部插入新元素。其中 list 是循环单链表;ele 是新元素值。
  • int removeByNum(CLNode **list, int i, int *ele):删除循环单链表中第 i 个位置的结点,并将被删结点的值保存到 ele 中。其中 list 是循环单链表;i 是循环单链表中的结点序号,从 1 开始;ele 用来保存被删结点的数据值。如果删除成功则返回 1,否则返回 0 表示删除失败。
  • void removeByEle(CLNode **list, int ele):删除循环单链表中第一个等于指定值 ele 的结点。其中 list 是循环单链表;ele 是指定值。
  • void removeFirst(CLNode **list, int *ele):删除循环单链表第一个结点。其中 list 是循环单链表;ele 用来保存被删第一个结点的数据值。
  • void removeLast(CLNode **list, int *ele):删除循环单链表最后一个结点。其中 list 是循环单链表;ele 用来保存被删的最后一个结点的数据值。
  • CLNode *findByNum(CLNode *list, int i):查找循环单链表中第 i 个结点。其中 list 是循环单链表;i 是结点序号,从 1 开始。如果查找成功则返回该结点,否则返回 NULL 表示查找失败。
  • CLNode *findByEle(CLNode *list, int ele):查找循环单链表中第一个值等于 ele 的结点。其中 list 是循环单链表;ele 是指定值。如果查找成功则返回该结点,否则返回 NULL 表示查找失败。
  • int size(CLNode *list):获取循环单链表的长度,即结点个数。其中 list 是循环单链表。返回循环单链表的结点个数。
  • int isEmpty(CLNode *list):判断循环单链表是否为空。其中 list 是循环单链表。如果循环单链表为空则返回 1,否则返回 0。
  • void clear(CLNode **list):清空循环单链表。其中 list 是循环单链表。
  • void print(CLNode *list):打印循环单链表所有结点。其中 list 是循环单链表。

init

初始化循环单链表。注意带头结点的循环单链表和不带头结点的循环单链表初始化不一样。

实现步骤:

  • 如果是带头结点的循环单链表,则创建头结点,为其分配空间,并将头结点的 next 指针域指向头结点本身。
  • 如果是不带头结点的循环单链表,则将头指针 head 指向 NULL

实现代码如下:

/*** 对循环单链表进行初始化* @param list 循环单链表*/
void init(CLNode **list) {// 为链表的头节点分配空间*list = (CLNode *) malloc(sizeof(CLNode));// 指定头节点的指针域,将其指向自身,表示是循环单链表,而普通单链表是指向 null(*list)->next = *list;// 注意,指向头结点本身
}

createByHead

通过头插法批量插入元素然后创建一个非空循环单链表。所谓的头插法就是每次插入一个新元素都是插入在第一个结点的位置,无论循环单链表是否带有头节点。

nums=[1, 2, 3, 4, 5]; n=5 为例创建循环单链表:

实现步骤:

  • 对链表进行初始化。注意,是带头结点的循环单链表。
  • 循环遍历数组中的每个元素,然后根据数组元素创建单链表结点,创建新节点时将数组元素值赋给结点数据域,将新节点的指针域指向 NULL
  • 将创建的新节点的 next 指针域指向单链表的第一个节点,然后将单链表的头结点的 next 指针指向新节点。

实现代码如下:

/*** 通过头插法创建循环单链表* @param list 已经初始化后的循环单链表* @param nums 待插入到单链表中的数据数组* @param n 数组长度* @return 创建成功的循环单链表*/
CLNode *createByHead(CLNode **list, int nums[], int n) {// 1.初始化循环单链表,即创建循环单链表的头结点。也可以直接调用 init 函数来初始化// 1.1 为头结点分配空间*list = (CLNode *) malloc(sizeof(CLNode));// 1.2 修改头结点的 next 指针,将其指向自己,而普通单链表是指向 null(*list)->next = *list;// 注意,指向头结点本身// 2.遍历 nums 数组中所有元素,将其添加到链表中,完成链表的创建for (int i = 0; i < n; i++) {// 2.1 创建新节点// 2.1.1 给新节点分配空间CLNode *newNode = (CLNode *) malloc(sizeof(CLNode));// 2.1.2 指定新节点的数据域newNode->data = nums[i];// 2.1.3 将新节点的指针域置为 null,指向空newNode->next = NULL;// 2.2 将新节点插入到链表中,但是插入到头结点的后面// 2.2.1 将新节点的 next 指针指向链表第一个节点,此时完成了新节点和链表第一个节点链接newNode->next = (*list)->next;// 2.2.2 将链表头节点的 next 指针指向新节点,此时完成了链表头节点和新节点的链接(*list)->next = newNode;}return *list;
}

createByTail

通过尾插法创建循环单链表。所谓的尾插法就是每次将新节点插入到链表的尾部。

nums=[1, 2, 3, 4, 5]; n=5 为例使用尾插法创建循环单链表:

实现步骤:

  • 初始化单链表。
  • 设置一个变量来记录单链表的尾结点 tailNode,初始为单链表的头结点。
  • 循环遍历数组中的每个元素,然后根据数组元素创建单链表结点,创建新节点时将数组元素值赋给结点数据域,将新节点的指针域指向 NULL
  • 将尾结点 tailNodenext 指针指向新结点 newNode,然后将新结点 newNodenext 指针指向循环单链表的头结点 head,最后将新结点 newNode 记录为新的尾节点 tailNode

实现代码如下:

/*** 通过尾插法创建循环单链表* @param list  循环单链表* @param nums 待插入到单链表中的数据数组* @param n 数组长度* @return 创建成功的循环单链表*/
CLNode *createByTail(CLNode **list, int nums[], int n) {// 1.初始化循环单链表,即创建循环单链表的头结点。也可以直接调用 init 函数来初始化// 1.1 为头结点分配空间*list = (CLNode *) malloc(sizeof(CLNode));// 1.2 修改头结点的 next 指针,将其指向自己,而普通单链表是指向 null(*list)->next = *list;// 注意,指向头结点本身// 保存链表的尾节点,初始为链表的头节点,即空链表的尾节点就是链表的头节点CLNode *tailNode = *list;// 2.循环数组 nums 中所有数据,插入到单链表中for (int i = 0; i < n; i++) {// 2.1 创建新节点// 2.1.1 给新节点分配空间CLNode *newNode = (CLNode *) malloc(sizeof(CLNode));// 2.1.2 指定新节点的数据域newNode->data = nums[i];// 2.1.3 将新节点的指针域置为 null,指向空newNode->next = NULL;// 2.2 将新节点追加到链表的尾部// 2.2.1 将链表原尾节点的 next 指针指向新节点,即让新节点成为链表的尾节点tailNode->next = newNode;// 2.2.2 然后将新节点(即链表新的尾节点)指向链表的头节点newNode->next = *list;// 2.2.3 然后将 tailNode 指向新节点(即新的尾节点)tailNode = newNode;}// 3.返回创建成功的链表return *list;
}

insert

在循环单链表的第 i 个位置插入值为 ele 的新结点。以 list=[1, 2, 3, 4, 5]; i=3; ele=66 为例如图:

实现步骤:

  • 参数校验,位置 i 的范围必须在 [1, length]。否则超出范围就判定插入失败。
  • 声明一个变量 node 保存循环单链表中的结点,初始为链表的开始结点;声明一个变量 pre 保存结点 node 的前驱结点,为了插入。
  • 通过遍历循环单链表(注意循环结束条件),找到第 i 个结点 node 和 第 i-1 个结点 pre
  • 然后创建一个新结点 newNode,为其分配数据域和指针域。将新结点 newNode 插入到第 i-1 个结点 pre 和第 i 个结点 node 之间。
  • 最后完成插入,返回 1 表示插入成功。

实现代码如下:

/*** 向链表的第 i 个位置插入新节点* 注意,如果是空链表(已经初始化),插入第 1 个节点会失败* @param list 循环单链表* @param i 序号,从 1 开始* @param ele 新节点的数据值* @return 如果插入成功则返回 1,否则返回 0*/
int insert(CLNode **list, int i, int ele) {// 0.参数校验if (i < 0 || i > size(*list)) {return 0;}// 1.声明一些变量// 1.1 链表的第一个节点CLNode *node = (*list)->next;// 链表的第一个节点// 1.2 保存前驱节点,初始为链表的头节点CLNode *pre = *list;// 保存前驱节点// 1.3 计数器,记录当前节点是链表的第几个节点int count = 0;// 注意,使用下面的方式插入第 1 个节点时,如果链表为空则会失败// 找到第 i 个节点的前驱节点(即第 i-1 个节点),因为插入节点必须知道它的前驱节点// 2.遍历链表所有节点,找到第 i 个节点,然后插入新节点while (node != *list) {// 2.1 计数器加 1,表示已经迭代一个节点了count++;// 2.2 找到第 i 个节点,那么就可以插入新节点了if (count == i) {// 2.2.1 创建新节点// 2.2.1.1 给新节点分配空间CLNode *newNode = (CLNode *) malloc(sizeof(CLNode));// 2.2.1.2 给新节点的数据域赋值newNode->data = ele;// 2.2.1.3 给新节点的指针域赋为 nullnewNode->next = NULL;// 2.2.2 将新节点插入到链表中// 2.2.2.1 将第 i-1 个节点的 next 指针指向新节点pre->next = newNode;// 2.2.2.2 将新节点的 next 指针指向原第 i 个节点newNode->next = node;// 2.2.3 结束循环,跳出程序break;}// 2.3 保存当前节点为前驱节点pre = node;// 2.4 继续下一个节点node = node->next;}return 1;
}

insertFirst

在循环单链表的开始结点位置插入新结点。

实现步骤:

  • 创建新结点 newNode,为新结点分配存储空间,并指明数据域和指针域。
  • 然后将新结点插入到链表的头部。

实现代码如下:

/*** 在循环单链表的头部插入新节点* @param list 循环单链表* @param ele 待插入的元素*/
void insertFirst(CLNode **list, int ele) {// 1.创建新节点// 1.1 为新节点分配空间CLNode *newNode = (CLNode *) malloc(sizeof(CLNode));// 1.2 为新节点的数据域指定内容newNode->data = ele;// 1.3 将新节点的指针域指向 nullnewNode->next = NULL;// 2.将新节点插入到链表的头部,成为新的链表第一个节点// 2.1 将新节点的 next 指针指向原链表的第一个节点newNode->next = (*list)->next;// 2.2 将头节点的 next 指针指向新节点,即让新节点成为链表的第一个节点(*list)->next = newNode;
}

insertLast

在循环单链表的尾部插入新结点。

实现步骤:

  • 找到循环单链表的尾结点 tailNode
  • 然后创建新结点 newNode 为其分配存储空间,并指定数据域和指针域。最后将新结点 newNode 插入到尾结点 tailNode 的后面,注意新尾结点的 next 指针必须指向链表的头结点,才能构成一个循环。

实现代码如下:

/*** 向循环单链表末尾追加一个新节点* @param list 循环单链表* @param ele 新节点的数据值*/
void insertLast(CLNode **list, int ele) {// 1.先找到链表的最后一个节点,即尾节点// 1.1 链表的第一个节点CLNode *node = (*list)->next;// 1.2 通过循环找到链表的最后一个节点,注意这里循环结束的条件是 node.next!=list,在循环结束之后,node 就是链表的尾节点while (node->next != *list) {node = node->next;}// 2.创建新节点然后插入到链表的尾部// 2.1 创建新节点// 2.1.1 为新节点分配空间CLNode *newNode = (CLNode *) malloc(sizeof(CLNode));// 2.1.2 为新节点的数据域指定内容newNode->data = ele;// 2.1.3 将新节点的指针域指向 nullnewNode->next = NULL;// 2.2 将新节点追加到循环单链表的末尾// 2.2.1 将链表的原尾节点的 next 指针指向新节点,已经完成了新节点链接到链表中newNode->next = node->next;// 其实 node->next 就是链表的头结点,所以 newNode->next=*list 也是可以的// 2.2.2 将新节点的 next 指针指向链表的头节点,完成了尾节点与头节点的链接node->next = newNode;
}

removeByNum

删除循环单链表第 i 个结点,并将被删结点的值保存到 ele 中。以 list=[11, 22, 33, 44, 55]; i=3 为例如图所示:

实现步骤:

  • 参数校验,序号 i 必须在 [1, length] 范围内,否则参数不合法则返回 0 表示删除失败。
  • 声明两个变量 prenode,其中 node 记录单链表中的结点,从链表第一个结点开始(即头结点的后继结点);而 pre 记录结点 node 的前驱结点。
  • 通过循环遍历找到第 i 个结点 node 和第 i-1 个结点 pre
  • 最后删除第 i 个结点(即 pre->next=node->next),用 ele 保存被删结点的值,然后调用 free 函数释放结点空间。

实现代码如下:

/*** 删除循环单链表中第 i 个节点* @param list 循环单链表* @param i 指定序号,从 1 开始* @param ele 保存被删除节点的数据域* @return 如果删除成功则返回 1,否则返回 0*/
int removeByNum(CLNode **list, int i, int *ele) {// 0.参数校验if (i < 0 || i > size(*list)) {return 0;}// 1.声明一些变量// 1.1 链表的第一个节点CLNode *node = (*list)->next;// 1.2 保存前驱节点,初始为链表的头节点CLNode *pre = *list;// 1.3 计数器,记录当前是链表的第几个节点int count = 0;// 2.遍历链表,寻找第 i 个节点,通过循环计数的方式来找到while (node != *list) {// 2.1 计数器加 1count++;// 2.2 找到待删除节点,即比较计数器与参数值是否相等,如果相等则删除节点if (count == i) {// 2.2.1 删除 node 节点,即将第 i-1 个节点的 next 指针指向第 i+1 个节点,这样就删除了第 i 个节点pre->next = node->next;// 2.2.2 保存被删除节点的数据域值*ele = node->data;// 2.2.3 释放节点空间free(node);// 2.2.4 删除成功则返回 1return 1;}// 2.3 保存当前节点为前驱节点pre = node;// 2.4 继续下一个节点node = node->next;}return 0;
}

removeByEle

删除循环单链表中第一个值为 ele 的结点,并用 ele 来保存被删结点的数据值。以 list=[11, 22, 33, 44, 55]; ele=33 为例如图所示:

实现步骤:

  • 声明一个变量 node 来记录循环单链表中的结点,直到找到第 i 个结点,从链表的第一个结点(即头结点的后继结点)开始;再声明一个变量 pre 来记录 node 结点的前驱结点,便于删除结点。
  • 遍历循环,找到循环单链表中第一个值为 ele 的结点,如果找到则删除该结点。

实现代码如下:

/*** 根据值删除循环单链表元素* @param list 循环单链表* @param ele 被删除节点的数据域值*/
void removeByEle(CLNode **list, int ele) {// 0.声明一些变量// 0.1 链表的第一个节点,其实主要用于遍历链表CLNode *node = (*list)->next;// 0.1 保存值等于 ele 的节点的前驱节点CLNode *pre = *list;// 1.循环链表,查找节点值等于 ele 的节点,然后进行删除while (node != *list) {// 1.1 如果找到值等于 ele 的节点if (node->data == ele) {// 1.1.1 删除 node 节点,即将第 i-1 个节点的 next 指针指向第 i+1 个节点,这样就删除了第 i 个节点pre->next = node->next;// 1.1.2 释放节点空间free(node);// 1.1.3 跳出循环break;}// 1.2 保存当前节点为前驱节点pre = node;// 1.3 继续链表的下一个节点node = node->next;}
}

remvoeFirst

删除循环单链表中的开始结点,并用 ele 保存开始结点的数据值。以 list=[11, 22, 33, 44, 55] 为例如图所示:

实现步骤:

  • 即将头结点的 next 指针指向开始结点 node 的后继结点。

注:代码待完善,当循环单链表为空的时候虽然不会报错,但是由于会释放头结点空间,所以导致循环单链表结构出问题。

实现代码如下:

/*** 删除链表的第一个节点* @param list 循环单链表* @param ele 被删除的节点的数据域值*/
void removeFirst(CLNode **list, int *ele) {// 链表的第一个节点CLNode *node = (*list)->next;// 删除第一个节点,即将链表的头节点的 next 指针指向原链表的第二个节点(即让原链表第二个节点成为新的链表第一个节点)(*list)->next = node->next;// 保存被删除节点的数据域值*ele = node->data;// 释放节点空间free(node);
}

removeLast

删除循环单链表的尾结点,并用 ele 保存被删结点的数据值。以 list=[11, 22, 33, 44, 55] 为例如图所示:

实现步骤:

  • 声明两个变量 prenode,其中 node 记录循环单链表中的结点,初始为循环单链表的开始结点(即头结点的后继结点);而 pre 记录 node 结点的前驱结点。其实 node 就是为了最后记录循环单链表的尾结点,pre 记录尾结点的前驱结点,便于删除。
  • 通过遍历循环,找到循环单链表的尾结点 node 和尾结点的前驱结点 pre
  • 删除循环单链表的尾结点 node,用 ele 保存被删结点的数据值,然后释放存储空间。

实现代码如下:

/*** 删除循环单链表的尾节点* @param list 循环单链表* @param ele 被删除节点的数据域值*/
void removeLast(CLNode **list, int *ele) {// 0.声明一些变量// 0.1 链表的第一个节点CLNode *node = (*list)->next;// 0.2 记录尾节点的前驱节点CLNode *pre = *list;// 1.找到链表的尾节点和尾节点的前驱节点while (node->next != *list) {// 注意循环结束的条件是 node.next!=list,当循环结束之后 node 就是链表的尾节点// 1.1 保存当前节点为前驱节点pre = node;// 1.2 继续链表的下一个节点node = node->next;}// 2.删除链表的最后一个节点// 2.1 删除节点 nodepre->next = node->next;// 其实等价于 pre->next=*list// 2.2 保存被删除节点的数据域值*ele = node->data;// 2.3 释放节点空间free(node);
}

findByNum

查找循环单链表中第 i 个结点。以 list=[11, 22, 33, 44, 55]; i=3 为例如图所示:

实现步骤:

  • 参数校验,序号 i 的范围必须在 [1, length] 范围内。否则参数不合法,则返回 NULL
  • 声明一个变量 count 来记录已经遍历到第几个结点了,直到遇到 count==i,则表示找到第 i 个结点,然后返回。

实现代码如下:

/*** 通过序号在链表中检索节点* @param list 循环单链表* @param i 序号,从 1 开始* @return 如果找到第 i 个节点则返回,否则返回 NULL*/
CLNode *findByNum(CLNode *list, int i) {// 0.参数校验if (i < 1 || i > size(list)) {return NULL;}// 1.声明一些变量// 1.1 链表的第一个节点CLNode *node = list->next;// 1.2 计数器,记录当前正在遍历的节点是链表的第几个节点int count = 0;// 2.遍历链表所有节点,找到第 i 个节点while (node != list) {// 2.1 计数器加 1,表示已经迭代了一个节点count++;// 2.2 比较计数器的值与参数值是否相等,如果相等则表示找到了第 i 个节点则返回该节点if (count == i) {return node;}// 2.3 继续下一个节点node = node->next;}return NULL;
}

findByEle

查找循环单链表中第一个值为 ele 的结点。

实现步骤:

  • 从头到尾扫描单链表,比较结点 node 的数据值是否等于 ele,如果相等则返回该结点,否则继续判断链表的下一个结点。

实现代码如下:

/*** 通过数据值在链表中检索节点* @param list 循环单链表* @param ele 指定数据值* @return 如果找到则返回对应值的节点,如果没有找到则返回 NULL*/
CLNode *findByEle(CLNode *list, int ele) {// 链表的第一个节点CLNode *node = list->next;// 1.遍历链表所有节点,比较节点的值与输入的参数值是否相等,如果相等则返回该节点while (node != list) {// 1.1 比较当前节点的数据域是否与参数值相等,如果相等则表示找到指定值的节点,那么返回即可if (node->data == ele) {return node;}// 1.2 继续下一个节点node = node->next;}return NULL;
}

size

获得循环单链表的长度,即循环单链表中的结点个数,不包括头结点。以 list=[11, 22, 33, 44, 55] 为例如图所示:

实现步骤:

  • 从头到尾扫描循环单链表所有结点,用变量 count 来记录所有结点个数。

实现代码如下:

/*** 统计循环单链表的节点个数* @param list 循环单链表* @return 链表的长度*/
int size(CLNode *list) {// 计数器,记录链表的节点个数int count = 0;// 链表的第一个节点CLNode *node = list->next;// 遍历链表所有节点,统计个数,注意结束循环的条件while (node != list) {// 统计个数count++;// 继续下一个节点node = node->next;}// 返回节点个数return count;
}

isEmpty

判断循环单链表是否为空。

实现步骤:

  • 如果是带头结点的循环单链表,则直接判断 head->next==head;如果是不带头结点的循环单链表,则直接判断 head==NULL 即可。

实现代码如下:

/*** 判断链表是否为空* @param list 待判断的循环单链表* @return 如果为空则返回 1,否则返回 0*/
int isEmpty(CLNode *list) {// 判断链表的第一个节点是否又是头结点,如果是则表示链表为空return list->next == list;
}

clear

清空循环单链表。注意如果是带头结点的循环单链表,则需要将头结点的 next 指针指向它本身。

实现步骤:

  • 从头到尾扫描循环单链表所有结点,释放结点空间。
  • 最后将头结点的 next 指针指向它本身,空表仍然是循环单链表。

实现代码如下:

/*** 清空循环单链表* @param list 待清空的循环单链表*/
void clear(CLNode **list) {// 链表的第一个节点CLNode *node = (*list)->next;// 循环链表所有节点,删除节点,释放空间while (node != *list) {// 临时保存当前节点的后继节点CLNode *temp = node->next;// 释放空间free(node);// 继续下一个节点node = temp;}// 最后重新将链表的头结点的 next 指针指向自身,仍然是循环单链表(*list)->next = *list;
}

print

打印循环单链表所有结点值。

实现步骤:

  • 从头到尾扫描循环单链表所有结点,打印它们的数据值。

实现代码如下:

/*** 打印循环单链表* @param list 待打印的循环单链表*/
void print(CLNode *list) {printf("[");CLNode *node = list->next;while (node != list) {printf("%d", node->data);if (node->next != list) {printf(", ");}node = node->next;}printf("]\n");
}

注意事项

循环单链表判空操作

如果是带头结点的循环单链表,则判空条件是:head->next==head

如果是不带头结点的循环单链表,则判空条件是:head=NULL

练习题

  • Example038-编写一个函数将链表 h2 链接到链表 h1 之后,要求链接后的链表仍然保持循环链表形式
  • Example039-删除循环单链表中的所有最小值节点直至链表为空

线性表文档之循环单链表相关推荐

  1. 线性表文档之循环双链表

    循环双链表 定义 概念 循环双链表就是在双链表的基础上,见链表的尾结点和链表的第一个结点连接起来,形成一个循环. 如果是带头结点的循环双链表,则将链表的尾结点的 next 指针指向链表的头结点,将链表 ...

  2. 数据结构之线性表-链式存储之单链表(一)

    本人文笔较差,语文从来不及格,基础不好,写此类文章仅供自己学习,理解队列及其他知识,高手大神请略过.参考书籍 <数据结构与算法分析-Java语言描述> 1.1 单链表简介 线性表的最大的缺 ...

  3. 线性表的链式实现(单链表)

    单链表的定义 为了表示每个数据元素与其直接后续元素之间的逻辑关系,每个元素除了存储本身的信息外,还需要存储指示其直接后续的信息,即每个结点存放本身的数据元素和下一个元素的地址.n个结点连接成一个链式线 ...

  4. 线性表的链式存储-单链表

    单链表操作 [x] 单链表的创建(尾插法.头插法) [x] 单链表的查找操作 [x] 单链表的删除操作 [x] 单链表的逆置操作(使用头插法) [x] 单链表表长的计算 [x] 打印单链表 单链表的创 ...

  5. 线性表的链式表示——单链表

    单链表 定义 线性表的链式存储又称单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素.每个链表的结点,除存放元素自身的信息之外,还需要存放一个指向其后继结点的指针.即单链表的结构分为两部分, ...

  6. 【数据结构】线性表的链式存储-单链表

    单链表的定义 线性表的链式存储又称为单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素. 为了建立起数据元素之间的线性关系,对每个链表结点,除了存放元素自身的信息之外,还需要存放一个指向其后 ...

  7. (王道408考研数据结构)第二章线性表-第三节1:单链表的定义及其操作(插入和删除,建立之尾插和头插)

    文章目录 一:单链表相关 (1)单链表的定义 (2)头指针与头结点 二:单链表代码描述 三:单链表的初始化 四:单链表的插入 五:单链表的删除 六:单链表查找 (1)按位查找 (2)按值查找 七:单链 ...

  8. 数据结构与算法(2-2)线性表之链式存储(单链表、静态链表、循环链表、双向循环链表)

    目录 一.单链表 1.存储方式 2.插入 3.删除 总代码: 二.静态链表 1.存储方式 2.插入 3.删除 4.遍历 总代码: 三.循环链表 总代码: 四.双向循环链表 1.存储方式: 2.插入和删 ...

  9. python 单链表节点怎么快速定义_线性表链式存储结构之单链表

    线性表的链式存储结构的特点就是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以在内存中未被占用的任意位置.比起顺序存储结构每个元素只需要存储一个位置就可以了.现在链式存储结构中,除了要存储数 ...

最新文章

  1. spring boot部署到tomcat
  2. 优化gradle下载引用jar速度慢或者出错的问题
  3. VS2013找不到winres.h的解决办法
  4. shrio的rememberMe不起作用
  5. java8 groupingby_Java8 教程第五章之Streams Collectors groupingBy
  6. python中isort的使用
  7. C语言课程设计学生籍贯信息,C语言课程设计 学生籍贯信息记录簿设计.doc
  8. SQL Server 2017 AlwaysOn AG 自动初始化(七)
  9. 11.6 ConfigParser模块
  10. python 计算机程序设计-计算机程序设计(Python)
  11. 第六章_循环神经网络(RNN)
  12. 【MediaSoup】UDPSOCKET recv数据到rtcp包解析
  13. 实现一个监控 IP 的 windows 服务
  14. pwm波如何控制电机代码_如何通过PLC控制伺服电机?
  15. 【012】Excel宏编程相关封装模块(删除行/列、隐藏列、合并单元格)_005_#VBA
  16. 洛谷P4052 [JSOI2007]文本生成器(AC自动机)
  17. python学习-飞机大战
  18. 【STM32学习】(21)STM32实现步进电机
  19. 黄山职业技术学院计算机专业怎么样啊,黄山职业技术学院怎么样
  20. TinyXml 介绍

热门文章

  1. FH Admin fhadmin
  2. mysql revoke_MySQL REVOKE:删除用户权限
  3. react 状态驱动_React状态:做最少的事情
  4. 连接远程linux服务器
  5. idisplay4.2.5安卓最新版
  6. 【Unity-学习-003】导弹,跟踪弹怎么玩?
  7. 刚刚开始学习java,自己慢慢写了最基础的吃货联盟
  8. 支付宝双12生活节刷翻倍豆脚本
  9. python 高斯金字塔_python实现opencv学习十五:高斯金字塔和拉普拉斯金字塔
  10. ZED2代相机+nvidia jetson AGX xavier踩坑记录