【c语言】循环链表(适合初学者)

循环链表简介:

循环链表是一种特殊的链表数据结构,与单向链表或双向链表相比,循环链表的最后一个节点的下一个节点指向第一个节点,从而形成一个环形结构。因此,循环链表可以在最后一个节点后继续添加节点,并且可以像单向链表或双向链表一样遍历、查找和删除节点。循环链表通常有一个头指针和一个尾指针,它们指向第一个节点和最后一个节点,以便在添加或删除节点时快速定位。

循环链表可以用于解决某些问题,例如模拟一个环形队列,或者用于实现循环播放的音乐列表等。循环链表的优点在于它具有链表的灵活性,同时可以避免在添加或删除节点时需要遍历整个链表的缺点。

以下是一个循环链表:

/*
This shows the coding basics of circular-linked-list in C programming.
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

struct node 
{
   int data;
   int key;
	
   struct node *next;
};

struct node *head = NULL;
struct node *tail = NULL;
struct node *current = NULL;

bool isEmpty() 
{
   return head == NULL;
}

int length() 
{
   int length = 0;

   //if list is empty
   if(head == NULL) 
   {
      return 0;
   }

   current = head->next;

   // here different to single linked list
   while(current != head) 
   {
      length++;
      current = current->next;   
   }
	
   return length;
}

//insert link at the first location
void insertFirst(int key, int data) 
{
   //create a link
   struct node *link = (struct node*) malloc(sizeof(struct node));
   link->key = key;
   link->data = data;
	
   // attention here, difference
   if (isEmpty()) 
   {
      head = link;
      head->next = head;
      tail = head;
   } 
   else 
   {
      //point it to old first node
      link->next = head;
		
      //point first to new first node
      head = link;

      //update tail node
      tail->next = head;
   }  
   //printf("%p,%d,%d,%p\n",link,link->data,link->key,link->next);
   //printf("current head: %p\n",head);
   //printf("current tail->next: %p\n",tail->next);  
}

//delete first item
struct node *deleteFirst() 
{

   //save reference to first link
   struct node *tempLink = head;
	
   if(head->next == head) 
   {  
      head = NULL;
      return tempLink;
   }     

   //mark next to first link as first 
   head = head->next;

   //update tail node
   tail->next = head;
	
   //return the deleted link
   return tempLink;
}

//display the list
void printList() 
{

   struct node *ptr = head;
   printf("\n[ ");
	
   //start from the beginning
   if(head != NULL) 
   {
      // difference here
      while(ptr->next != head) 
      {
         //printf("(%d,%d) ",ptr->key,ptr->data);
         printf("(%p,%d,%d,%p) ",ptr,ptr->key,ptr->data,ptr->next);
         ptr = ptr->next;
      }

      //print the last node
      //printf("(%d,%d) ",ptr->key,ptr->data);
      printf("(%p,%d,%d,%p) ",ptr,ptr->key,ptr->data,ptr->next);
   }
	
   printf(" ]");
}

struct node* find(int key) 
{

   //start from the first link
   struct node* current = head;

   //if list is empty
   if(head == NULL) 
   {
      return NULL;
   }

   //navigate through list
   while(current->key != key) 
   {
   
      //if it is last node
      if(current->next == head) {
         return NULL;
      } else {
         //go to next link
         current = current->next;
      }
   }      
   
   //if data found, return the current Link
   return current;
}
//在中间插入,在key1后加入新节点
void insertBetween(int key1, int key, int data) 
{
   //create a link
   struct node *link = (struct node*) malloc(sizeof(struct node));
   struct node *next = NULL;
   current = find(key1);
   link->key = key;
   link->data = data;
   next = current->next;
   current->next = link;
   link->next = next;
}
//在中间删除,并且返回被删除的节点的地址
struct node*deletebetween(int key)
{
   struct node* current = find(key);
   struct node* next = NULL;
   if(current == NULL || current == tail)
   {
      return NULL;
   }
   next = current->next;
   current->data = next->data;
   current->key = next->key;
   current->next = next->next;
   free(next);
   return current;
}
int main() 
{
   insertFirst(1,10);
   insertFirst(2,20);
   insertFirst(3,30);
   insertFirst(4,40);
   insertFirst(5,50);
   insertFirst(6,60); 
   insertBetween(2,1231,32423);
   insertBetween(5,324,1231);
   deletebetween(6);
   deletebetween(2);

   printf("Original List: "); 
	
   //print list
   printList();

   
   while(!isEmpty()) {            
      struct node *temp = deleteFirst();
      printf("\nDeleted value:");  
      printf("(%d,%d) ",temp->key,temp->data);
   }   
	
   printf("\nList after deleting all items: ");
   printList();
   
   return 0;
}

这是一个 C 语言编写的循环链表的基本代码示例。该代码定义了一个结构体 node 表示链表中的每个节点,节点包含两个整型数据 keydata,以及一个指向下一个节点的指针 next。整个链表通过三个指针 headtailcurrent 来维护。其中,head 指向链表的头部节点,tail 指向链表的尾部节点,current 用于遍历链表。

除此之外,该代码还实现了以下几个函数:

  • isEmpty():判断链表是否为空,如果链表为空则返回 true,否则返回 false。
  • length():计算链表的长度,返回链表中节点的数量。
  • insertFirst(int key, int data):在链表的头部插入一个新节点,该节点的键值为 key,数据为 data
  • deleteFirst():删除链表的头部节点,并返回被删除节点的地址。
  • printList():打印链表中所有节点的信息。
  • find(int key):查找链表中键值为 key 的节点,并返回该节点的地址。
  • insertBetween(int key1, int key, int data):在键值为 key1 的节点后面插入一个新节点,该节点的键值为 key,数据为 data
  • deletebetween(int key):删除键值为 key 的节点,并返回被删除节点的地址。

main() 函数中,该代码创建了一个循环链表,并在其中插入、删除节点,最后打印链表中所有节点的信息,然后将链表中所有节点删除,最终再次打印链表中的所有节点,可以看到链表已经为空。

各个函数的详细解释:

isEmpty()函数:

这段代码定义了一个函数 isEmpty(),用来判断链表是否为空。它通过比较头结点 head 是否为 NULL 来确定链表是否为空。如果 headNULL,则说明链表为空,函数返回 true,否则返回 false。由于返回值类型是 bool,因此函数的返回值只有两种情况:truefalse

length()函数:

这段代码实现了一个循环双链表(doubly linked list)的长度(number of elements)的计算。

首先初始化计数器 length 为 0。如果双链表为空(即头节点 headNULL),则直接返回长度为 0。否则,从双链表的第一个元素开始遍历,通过 current = head->nextcurrent 指针指向第一个元素(即头节点的下一个节点),然后开始循环。在循环中,每当遍历到一个元素时,将计数器 length 加一,并将 current 指针指向下一个元素,直到遍历到头节点 head 为止。最后返回计数器 length,即双链表的长度。

需要注意的是,双链表的长度的计算与单链表不同,因为双链表中每个节点都有一个前驱节点,因此在计算长度时需要遍历到头节点才能停止。

insertFirst()函数:

这段代码实现了在循环链表的头部插入一个新节点。首先,使用malloc函数动态分配一个新节点,并将key和data值设置为给定的参数。接下来,检查循环链表是否为空。如果为空,则将新节点设置为头节点,同时将头节点的下一个指针指向头节点本身,并将尾节点设置为头节点。如果循环链表不为空,则将新节点的下一个指针指向当前头节点,将新节点设置为新的头节点,最后更新尾节点的下一个指针为新的头节点,以确保循环链表的闭环性质不变。最后,这段代码输出了一些调试信息,包括新节点的地址、值以及链表的头节点和尾节点指针。

deleteFirst()函数:

这段代码定义了一个名为deleteFirst()的函数,它的返回类型是一个指向struct node类型的指针。该函数的作用是从循环链表的开头删除一个节点,并返回被删除节点的指针。函数首先将头指针保存在一个临时变量tempLink中。如果循环链表中只有一个节点,函数将把头指针设置为NULL,表示链表为空,然后返回tempLink。否则,函数将把头指针更新为原来的下一个节点,并更新尾节点的指针以使其指向新的头节点。最后,函数返回指向被删除节点的指针tempLink

printList()函数:

这段代码是定义了一个打印循环链表的函数。首先,它将头指针赋值给一个指针变量ptr。然后,它打印一个左中括号,表示链表的开始。接下来,如果头指针不为空,它进入一个while循环,该循环一直迭代到ptr指向的节点的下一个节点是头节点为止。在循环内,它使用printf()函数打印指向节点的指针、关键字、数据和下一个节点的指针。然后,它将指针ptr移动到链表中的下一个节点,以准备下一次迭代。最后,当ptr指向的下一个节点是头节点时,它打印出最后一个节点,然后打印右中括号,表示链表的结束。

find()函数:

这段代码实现了一个循环链表中寻找某个节点的功能。它首先从链表的头节点开始,判断链表是否为空,如果为空,则直接返回 NULL。如果链表不为空,则循环遍历链表,判断当前节点的关键字是否与所需寻找的关键字相等。如果相等,则返回当前节点。如果当前节点不是所需节点,则将指针移到下一个节点继续判断,如果到达链表末尾仍未找到,则返回 NULL。

insertBetween()函数:

这段代码定义了一个名为insertBetween的函数,它的参数包括要在哪个节点后面插入(即key1),要插入的新节点的键值和数据。

首先,它使用malloc函数动态地创建一个新的节点,并将其存储在指针变量link中。然后,使用find函数查找与给定键值key1匹配的节点,并将其存储在指针变量current中。

接下来,将要插入的新节点的键值和数据设置为link->keylink->data。然后,将指针变量next设置为当前节点current的下一个节点。

然后,将指针变量link插入到当前节点current和其下一个节点next之间。具体来说,将当前节点的下一个节点设置为指针变量link,将指针变量link的下一个节点设置为节点next。这样就完成了在给定节点后插入新节点的操作。

需要注意的是,这段代码假定链表中已经存在具有键值key1的节点。如果该节点不存在,则会出现问题。

deletebetween()函数:

这段代码实现了删除链表中某个节点的功能,通过find()函数找到具有指定关键字key的节点current;检查current是否为NULL或者是否为tail节点,若是则直接返回NULL;否则,取得current节点的下一个节点next;将next节点的数据和关键字赋值给current节点,同时将current节点的next指针指向next节点的next;最后,释放next节点的内存空间,返回current节点。这里的删除操作可以理解为将链表中某个节点的数据和关键字“移动”到当前节点,并将下一个节点删除的过程,从而达到删除的效果。

单链表和循环链表的区别:

我们在学习循环链表时,一定要认识到循环链表和单链表的区别。循环链表和单链表的主要区别在于最后一个节点的指针。在单链表中,最后一个节点的指针为 NULL,表示链表的结束。而在循环链表中,最后一个节点的指针指向第一个节点,形成一个循环。这意味着在循环链表中可以从任何一个节点开始遍历整个链表,而在单链表中只能从第一个节点开始遍历。由于循环链表中没有真正的“末尾”,因此对于一些特定的应用场景(如循环队列),循环链表可以更加方便和高效地实现。同时,循环链表也需要更多的注意,避免出现死循环。

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值