【数据结构与算法】-(3)循环链表(单向)

1、定义

将单链表中终点结点的指针端由空指针改成指向头结点,就使整个单链表造成一个环,这种头尾相连的单链表称为单循环链表,简称循环链表(circular linked list)。工具

咱们先讨论的是单向循环链表,示意图以下所示:ui

单向循环链表结构图

2、操做

2.1 建立单向循环链表

建立逻辑主要有下面的步骤:spa

先判断是否第一次建立?指针

  1. 是(空链表):建立一个结点,使新结点next 指向自身
  2. 否:使尾节点的next=新节点。新节点的next指向头节点

下面用代码解释一下:code

  1. 先定义一个结点(结构体),定一个别名:
//定义结点
typedef struct Node{
    ElemType data;
    struct Node *next;
}Node;

typedef struct Node * LinkList;
复制代码
  1. 建立一些变量,以供环境使用:
Status CreateList(LinkList *L){
		int item;
    LinkList temp = NULL;
    LinkList target = NULL;
复制代码
  1. 接下来判断这个链表*L 是否为空:
if(*L==NULL)
复制代码
  1. 若是输入的链表是空的——则建立一个结点,使它的next指向本身:
Status CreateList(LinkList *L){
    printf("输入节点的值,输入0结束\n");
    while(1)
    {
        scanf("%d",&item);
        if(item==0) break;
        
          //若是输入的链表是空。则建立一个新的节点,使其next指针指向本身 (*head)->next=*head;
        if(*L==NULL)
        {
            *L = (LinkList)malloc(sizeof(Node));
            if(!L)exit(0);
            (*L)->data=item;
            (*L)->next=*L;
        }
复制代码
  1. 若是链表不为空——则去寻找链表的尾结点

这里寻找尾结点能够有两种实现方式:cdn

  1. 遍历尾结点,根据尾结点指针会指向首元结点来定位到尾结点。blog

    • 一、使得尾结点的next 指向新结点。
    • 二、新结点的next 指向头结点。
    else{
    		for (target = *L; target->next != *L; target = target->next);
    		// 为新结点开辟内存空间 
    		temp=(LinkList)malloc(sizeof(Node));
    		// 如开辟失败,返回错误 
    		if(!temp) return ERROR;
    		// 新结点写入数据 
    		temp->data=item;
    		temp->next=*L;  //新节点指向头节点
    		target->next=temp;//尾节点指向新节点
    }
    复制代码
  2. 建立一个工具结点r,用它来灵活处理之后一个结点(后插法内存

  3. 新建一个 rci

    LinkList r = NULL;
    复制代码
  4. 在该链表建立时,将惟一的结点赋值给rget

    //第一次建立
    if(*L == NULL){       
       *L = (LinkList)malloc(sizeof(Node));
       if(!*L) return ERROR;
       (*L)->data = item;
       (*L)->next = *L;
       r = *L;
    复制代码
  5. 建立新的结点,进行赋值,next指向原链表首结点

    temp = (LinkList)malloc(sizeof(Node));
    if(!temp) return  ERROR;
    temp->data = item;
    temp->next = *L;
    复制代码
  6. 把原最后一个结点的尾结点指向新结点,以及新结点赋值给工具结点r

    r->next = temp;
    复制代码
这样,闭环完成,整个过程示意图能够用下面的图实现:
复制代码

2.2 单向循环链表插入数据

分两种状况,插入点是否为首元结点

2.2.1 插入点位首元结点
  1. 建立新结点并进行赋值
  2. 找到链表最后的结点——尾结点
  3. 让新结点的next 指向头结点
  4. 让尾结点的 next 指向新的头结点
  5. 让头结点指向temp ——临时的新结点

具体代码实现以下:

temp = (LinkList)malloc(sizeof(Node));
if (temp == NULL) {
    return ERROR;
}
temp->data = num;
for (target = *L; target->next != *L; target = target->next);
temp->next = *L;
target->next = temp;
*L = temp;
复制代码

如图所示:

2.2.2 插入点非首元结点
  1. 建立新结点 temp,并判断成功与否
  2. 找到插入的位置,若是超过链表长度,则自动插入队尾
  3. 经过工具target 找到要插入位置的前一个结点,让 target->next = temp
  4. 插入结点的前一个结点next 指向新结点,新结点next指向target原来的next 位

具体代码实现以下:

temp = (LinkList)malloc(sizeof(Node));
if (temp == NULL) {
   return ERROR;
}
temp->data = num;
for ( i = 1,target = *L; target->next != *L && i != place - 1; target = target->next,i++) ;
temp->next = target->next;
target->next = temp;
复制代码

2.3 单向循环链表的删除

单向循环链表的删除,与顺序表的删除很相似,步骤都是先肯定须要删除的位置,经过判断是否首元结点,作不一样的操做。具体操做步骤以下:

2.3.1 删除点为首元结点
  • 若是本链表只剩首元结点,则直接将*L 置为空;

    实施代码以下:

    if((*L)->next == (*L)){
        (*L) = NULL;
        return OK;
    }
    复制代码
  • 若是本链表还剩其余结点

    1. 找到尾结点 target
    2. 尾结点 next 指向原来首元结点的下一个结点,即 target->next = (*L)->next
    3. temp 临时接收首元结点
    4. 新结点为首元结点
    5. 释放以前的接收的首元结点 temp

    实施代码以下:

    // 步骤 1⃣️
    for (target = *L; target->next != *L; target = target->next);
    // 步骤 2⃣️
    temp = *L;
    // 步骤 3⃣️
    *L = (*L)->next;
    // 步骤 4⃣️
    target->next = *L;
    // 步骤 5⃣️
    free(temp);
    复制代码

2.3.2 删除点为非首元结点
  1. 找到须要删除结点的上一个结点,用target表示
  2. temp临时接受须要删除的结点
  3. targetnext 指以前指向的下一个结点
  4. 释放temp结点

实施代码以下:

// 步骤 1⃣️
for(i=1,target = *L;target->next != *L && i != place -1;target = target->next,i++);
// 步骤 2⃣️
temp = target->next;
// 步骤 3⃣️
target->next = temp->next;
// 步骤 4⃣️
free(temp);
复制代码

示意图以下:

2.4 单向链表的查询

这里仅仅讨论下简单的链表查询,步骤以下:

  1. 循环查找链表中给定值的结点
  2. 若查询的结点指向首元结点,而且此时尾结点值也不为所需,跳出

实施代码以下:

int i = 1;
    LinkList p;
    p = L;
    
    // 1⃣️ 寻找链表中的结点 data == value
    while (p->data != value && p->next != L) {
        i++;
        p = p->next;
    }
    
    // 2⃣️ 当尾结点指向头结点就会直接跳出循环,因此要额外增长一次判断尾结点的data == value;
    if (p->next == L && p->data != value) {
        return  -1;
    }
复制代码

3、小结

单向循环链表与顺序表有些许类似,可是不一样点在于,它并不是是按照序号排列,而是经过指针的指向进行链接,并且有首尾相连的特色。

相关文章
相关标签/搜索