单向循环链表

一,循环链表的概念

1.什么是循环链表

  所谓的循环链表就是让单向链表的首尾相连,组成一个环状。

2.循环链表的典型应用

  约瑟夫环问题。

3.实现循环链表的重点

  1,循环链表在插入第一个元素的时候,需要我们将第一元素的指针域指向其自身,也就构成了循环链表。

  2,循环链表基于单向链表而生,单是比循环链表多了游标这个概念。要想实现循环链表的插入,删除的关键是考虑头结点问题,因为在头插法方式(往链表的头部插入数据)中,需要将末尾数据元素的指针域指向新插入的节点。将新插入的节点的指针域指向头结点的指针域的指针域,还需要将头结点指向新插入的结点。(插入相同)。

二,循环链表新增概念和功能

1,什么是游标

  所谓的游标就是在链表中可以移动的指针,游标初始化一般是指向链表的第一个结点。

2,游标的功能

  • 初始化游标
  • 移动游标:将移动前的游标所对应得结点返回,并将游标指向下一个数据元素。
  • 获取游标:获取当前游标所对应得数据元素
  • 删除游标:删除当前游标所对应得数据元素,并将游标指向下一个数据元素。

三,循环链表的实现

1,循环链表的功能

# ifndef CIRCLE_LINK_LIST
# define CIRCLE_LINK_LIST

/* 业务节点 */
typedef void Node;

/* 链表节点(被包含) */
typedef struct CircleNode
{
    struct CircleNode * next;
}CircleNode;

/* 链表 */
typedef struct CircleLinkList
{
    /* 头指针 */
    Node * ptr;
    /* 头结点 */
    CircleNode header;
    /* 游标 */
    CircleNode slider;
    /* 长度 */
    int length;
}CircleLinkList;

/* 创建循环链表 */
CircleLinkList * createList();

/* 获取链表长度 */
int length(CircleLinkList * list);

/* 销毁链表 */
void destory(CircleLinkList * list);

/* 清空链表 */
void clear(CircleLinkList * list);

/* 判断链表是否为空 */
int empty(CircleLinkList * list);

/* 插入结点 */
void insert(CircleLinkList * list,int pos, Node * node);

/* 删除结点 */
Node * del(CircleLinkList * list, int pos);

/* 获取结点 */
Node * get(CircleLinkList * list, int pos);

/* 将游标重置指向链表的第一个元素 */
void resetSlider(CircleLinkList * list);

/* 获取当前游标指向的数据元素 */
Node * current(CircleLinkList * list);

/* 将游标指向到链表的下一个数据元素并返回当前游标的数据元素 */
Node * next(CircleLinkList * list);

/* 删除游标,并将游标指向下一个数据元素 */
Node * deleteNode(CircleLinkList * list);

# endif

2,循环链表的实现

# include<stdio.h>
# include<stdlib.h>
# include"CircleLinkList.h"

/* 创建循环链表 */
CircleLinkList * createList()
{
    /* 在堆上分配内存 */
    CircleLinkList * list = (CircleLinkList *)malloc(sizeof(CircleLinkList));
    /* 初始化 */
    list->ptr = &list->header;
    list->header.next = NULL;
    list->slider.next = NULL;
    list->length = 0;
    return list;
}

/* 获取链表长度 */
int length(CircleLinkList * list)
{
    return list->length;
}

/* 销毁链表 */
void destory(CircleLinkList * list)
{
    if (list != NULL)
    {
        free(list);
    }
}

/* 清空链表 */
void clear(CircleLinkList * list)
{
    if (list != NULL)
    {
        list->header.next = NULL;
        list->slider.next = NULL;
    }
}

/* 判断链表是否为空 */
int empty(CircleLinkList * list)
{
    if (list->length == 0)
    {
        return 1;
    }
    else {
        return 0;
    }
}

/* 插入结点 */
void insert(CircleLinkList * list, int pos, Node * node)
{
    if (list == NULL)
    {
        printf("链表为NULL\n");
        return;
    }
    /* 判断插入位置是否超过链表长度或小于0 */
    pos = pos < 0 ? 0 : pos;
    pos = pos > length(list) ? length(list) : pos;
    /* 注意:如果在第一个位置插入,则需要遍历到最后一个元素,然后再把最后一个元素的指针域指向第一个 */
    /* 将业务节点转换为链表节点 */
    CircleNode * _node = (CircleNode *)node;
    /* 判断是否第一次插入 */
    if (length(list) == 0)
    {
        list->header.next = _node;
        _node->next = _node;
        list->length++;
        return;
    }
    /* 定义posNode结点 */
    CircleNode * posNode = list->header.next;
    /* 判断是否在头部插入 */
    if (pos == 0)
    {
        /* 将posNode移动到尾部 */
        for (int i = 0; i < length(list)-1; i++)
        {
            posNode = posNode->next;
        }
        /* 将尾部指针指向新节点 */
        posNode->next = _node;
        /* 将新节点指针指向头节点指向的节点 */
        _node->next = list->header.next;
        /* 将头节点指向新节点 */
        list->header.next = _node;
    }
    else {
        /* 将posNode移动到pos位置上 */
        for (int i = 0; i < pos-1; i++)
        {
            posNode = posNode->next;
        }
        /* 插入 */
        _node->next = posNode->next;
        posNode->next = _node;
    }
    list->length++;
}

/* 删除结点 */
Node * del(CircleLinkList * list, int pos)
{
    if (list == NULL)
    {
        printf("链表为NULL\n");
        return NULL;
    }
    if (length(list) <= 0)
    {
        printf("链表已空\n");
        return NULL;
    }
    /* 判断插入位置是否超过链表长度或小于0 */
    pos = pos < 0 ? 0 : pos;
    pos = pos > length(list) ? length(list) : pos;
    /* 定义要删除的节点 */
    CircleNode * result = NULL;
    /* 定义posNode结点 */
    CircleNode * posNode = list->header.next;
    /* 判断是否删除头结点 */
    if (pos == 0)
    {
        /* 移动posNode到pos位置 */
        for (int i = 0; i < length(list) - 1; i++)
        {
            posNode = posNode->next;
        }
        /* 保存要删除的节点 */
        result = posNode->next;
        /* 删除 */
        posNode->next = list->header.next->next;
        list->header.next = list->header.next->next;
    }
    else {
        /* 定义缓存上一个结点 */
        CircleNode * previous = posNode;
        /* 移动posNode到pos位置 */
        for (int i = 0; i < pos; i++)
        {
            previous = posNode;
            posNode = posNode->next;
        }
        /* 保存要删除的节点 */
        result = posNode;
        /* 删除 */
        previous->next = posNode->next;
    }
    list->length--;
    return result;
}

/* 获取结点 */
Node * get(CircleLinkList * list, int pos)
{
    if (list == NULL)
    {
        printf("链表为NULL\n");
        return NULL;
    }
    /* 判断插入位置是否超过链表长度或小于0 */
    pos = pos < 0 ? 0 : pos;
    pos = pos > length(list) ? pos%length(list) : pos;
    /* 定义头结点 */
    CircleNode * header = list->header.next;
    /* 定义pos结点 */
    CircleNode * posNode = header;
    /* 移动posNode到指定位置 */
    for (int i = 0; i < pos; i++)
    {
        posNode = posNode->next;
    }
    return posNode;
}

/* 将游标重置指向链表的第一个元素 */
void resetSlider(CircleLinkList * list)
{
    list->slider.next = list->header.next;
}

/* 获取当前游标指向的数据元素 */
Node * current(CircleLinkList * list)
{
    return list->slider.next;
}

/* 将游标指向到链表的下一个数据元素并返回当前游标的数据元素 */
Node * next(CircleLinkList * list)
{
    CircleNode * result = list->slider.next;
    list->slider.next = list->slider.next->next;
    return result;
}

/* 删除游标,并将游标指向下一个数据元素 */
Node * deleteNode(CircleLinkList * list)
{
    if (list == NULL)
    {
        printf("链表为NULL\n");
        return NULL;
    }
    if (length(list) <= 0)
    {
        printf("链表已空\n");
        return NULL;
    }
    /* 获取当前游标的数据结点 */
    Node * node = current(list);
    /* 将当前游标下移 */
    next(list);
    /* 定义要删除的节点 */
    CircleNode * result = NULL;
    /* 定义posNode结点 */
    CircleNode * posNode = list->header.next;
    /* 将业务节点转换为链表节点 */
    CircleNode * _node = (CircleNode *)node;
    /* 判断是否删除头结点 */
    if (_node == list->header.next)
    {
        /* 移动posNode到pos位置 */
        for (int i = 0; i < length(list) - 1; i++)
        {
            posNode = posNode->next;
        }
        /* 保存要删除的节点 */
        result = posNode->next;
        /* 删除 */
        posNode->next = list->header.next->next;
        list->header.next = list->header.next->next;
    }
    else {
        /* 定义缓存上一个结点 */
        CircleNode * previous = posNode;
        /* 移动posNode到pos位置 */
        while (posNode != _node)
        {
            previous = posNode;
            posNode = posNode->next;
        }
        /* 保存要删除的节点 */
        result = posNode;
        /* 删除 */
        previous->next = posNode->next;
    }
    list->length--;
    return result;
}

3,循环链表的测试

# include<stdio.h>
# include"CircleLinkList.h"

typedef struct Student
{
    CircleNode next;
    char name[64];
    int age;
}Student;

int main()
{
    Student s1 = { NULL,"刘备",56 };
    Student s2 = { NULL,"关羽",40 };
    Student s3 = { NULL,"张飞",36 };
    Student s4 = { NULL,"赵云",34 };
    Student s5 = { NULL,"马超",20 };
    Student s6 = { NULL,"黄忠",80 };

    CircleLinkList * list = createList();
    insert(list, length(list), &s1);
    insert(list, length(list), &s2);
    insert(list, length(list), &s3);
    insert(list, length(list), &s4);
    insert(list, length(list), &s5);
    insert(list, length(list), &s6);

    printf("##############遍历################\n");
    for (int i = 0; i < length(list); i++)
    {
        Student * student = (Student *)get(list, i);
        printf("name = %s,age = %d\n", student->name, student->age);
    }

    //printf("##############删除################\n");
    //for (int i = 0; i < 6; i++)
    //{
    //    Student * student = (Student *)del(list, length(list)-1);
    //    printf("name = %s,age = %d\n", student->name, student->age);
    //}

    resetSlider(list);
    printf("##############游标遍历################\n");
    for (int i = 0; i < length(list); i++)
    {
        Student * student = next(list);
        printf("name = %s,age = %d\n", student->name, student->age);
    }

}

三,约瑟夫环问题

/*
*    约瑟夫环运作如下:
*    1、一群人围在一起坐成环状(如:N)
*    2、从某个编号开始报数(如:K)
*    3、数到某个数(如:M)的时候,此人出列,下一个人重新报数
*    4、一直循环,直到所有人出列,约瑟夫环结束
*/
# include<stdio.h>
# include"CircleLinkList.h"

typedef struct value
{
    CircleNode next;
    int value;
}value;

int main()
{
    /* 创建循环链表 */
    CircleLinkList * list = createList();

    /* 从0到9十个学生围成环状 */
    value v0 = { NULL,0 };
    value v1 = { NULL,1 };
    value v2 = { NULL,2 };
    value v3 = { NULL,3 };
    value v4 = { NULL,4 };
    value v5 = { NULL,5 };
    value v6 = { NULL,6 };
    value v7 = { NULL,7 };
    value v8 = { NULL,8 };
    value v9 = { NULL,9 };

    /* 尾插法 */
    insert(list, length(list), &v0);
    insert(list, length(list), &v1);
    insert(list, length(list), &v2);
    insert(list, length(list), &v3);
    insert(list, length(list), &v4);
    insert(list, length(list), &v5);
    insert(list, length(list), &v6);
    insert(list, length(list), &v7);
    insert(list, length(list), &v8);
    insert(list, length(list), &v9);

    /* 重置游标 */
    resetSlider(list);

    /* 将游标移动到2位置,从第三个人开始报数 */
    for (int i = 0; i < 2; i++)
    {
        next(list);
    }

    /* 将数到4的人删除 */
    while (empty(list) == 0)
    {
        /* 循环4次 */
        for (int i = 0; i < 3; i++)
        {
            next(list);
        }
        /* 删除当前游标 */
        value * t = (value *)deleteNode(list);
        printf("result = %d\n", t->value);
    }
    return 0;
}
时间: 2024-08-11 05:32:19

单向循环链表的相关文章

链表(五)——单向循环链表

1.单向循环链表 区分单向链表和单向循环链表:单向循环链表的尾指针指向头结点. 2.单向循环链表的基本操作 #include <stdio.h> #include <malloc.h> #define NULL 0 typedef struct node { int data; struct node *next; }ElemSN; ElemSN * creat_link(int ms); //创建一个单向循环链表 void print_link(ElemSN *head); //

Python 单向循环链表

操作 is_empty() 判断链表是否为空 length() 返回链表的长度 travel() 遍历 add(item) 在头部添加一个节点 append(item) 在尾部添加一个节点 insert(pos, item) 在指定位置pos添加节点 remove(item) 删除一个节点 search(item) 查找节点是否存在 class Node(object): """节点""" def __init__(self, item): se

数据结构与算法 1 :基本概念,线性表顺序结构,线性表链式结构,单向循环链表

[本文谢绝转载] <大纲> 数据结构: 起源: 基本概念 数据结构指数据对象中数据元素之间的关系  逻辑结构 物理结构 数据的运算 算法概念: 概念 算法和数据结构区别 算法特性 算法效率的度量 大O表示法 时间复杂度案例 空间复杂度 时间换空间案例 1)线性表: 线性表初步认识: 线性表顺序结构案例 线性表顺序结构案例,单文件版 线性表的优缺点 企业级线性表链式存储案例:C语言实现 企业级线性表链式存储案例:C语言实现 单文件版 企业级线性表链式存储案例,我的练习  线性表链式存储优点缺点

线性表—单向循环链表

开始没看单向循环链表,感觉应该很简单,但实际上有几个概念不是很清楚: 头结点,头指针,尾指针,尾节点??? [个人理解]:头结点就是一个链表中实际存储数据的那个节点的前一个节点,这个节点不存储数据,只有一个指针指向第一个实际存储数据的节点(这个有效节点专业术语叫:首元素),这个节点就是头节点,指向头结点的指针就是头指针,至于尾指针,就是单纯的一个指针了,他没有一个节点作依托,孤零零的一个指针.尾指针指向最后一个存储数据的节点(尾指针内部存储的就是这个节点的地址),至于尾节点,还没有见到过. 上面

链表练习:单向循环链表变双向

已知有一个单向循环链表,其每个结点中含三个域:prior,data 和 next,其中 data 域为数据域,next 为指向后继结点的指针域,prior 也为指针域,但它的值为空 (NULL) ,试编写算法将此单向循环链表改为双向循环链表,即使 prior 成为指向前驱结点的指针域. 输入格式 输入共有三行,第一行为该单向循环链表的长度 n(1≤n≤50):第二行为该单向循环链表的各个元素 aii??(1≤ai ??≤1000),它们各不相同且都为数字:第三行为一个数字 m,表示链表中的一个元

单向循环链表C语言实现

我们都知道,单向链表最后指向为NULL,也就是为空,那单向循环链表就是不指向为NULL了,指向头节点,所以下面这个程序运行结果就是,你将会看到遍历链表的时候就是一个死循环,因为它不指向为NULL,也是周而复始的执行.串成了一个环型. #include <stdio.h> #include <stdlib.h> typedef struct node { char name[20]; struct node *link; }student; student * creat(int n

单向循环链表(约瑟夫环)

#include<stdio.h> #include<stdlib.h> #define N 10 typedef struct node{ int  data; struct node * next; }ElemSN; ElemSN*Createlink(int a[],int n){ int i; ElemSN*h=NULL,*p,*t; for(i=0;i<N;i++){ p=(ElemSN*)malloc(sizeof(ElemSN)); p->data=a[i

用单向循环链表实现约瑟夫环问题

约瑟夫环的问题和种类有很多,这是一道基本的约瑟夫环问题,通过单向循环链表实现模拟此过程. 代码如下: #include<stdio.h> #include<stdlib.h> typedef struct Node { int num; int password; struct Node *next; }CircleNode,*CircleList; CircleList Create(int n); void Knockout(CircleList L,int n,int m);

(java实现)单向循环链表

什么是单向循环链表 单向循环链表基本与单向链表相同,唯一的区别就是单向循环链表的尾节点指向的不是null,而是头节点(注意:不是头指针). 因此,单向循环链表的任何节点的下一部分都不存在NULL值. 由于单向循环链表的特性,它在处理一些环状数据的时候十分有效.大名鼎鼎的约瑟夫环问题就可以用循环单向链表求解,下面我们会有进一步的介绍. 由于单向循环链表和单向链表的差别真的不大,增添改查原理都相同.因此在这里我们不详细讲解,只提供源码.(如果你还是不理解的话,这里有单向链表的传送门) 源码实现(Ja