数据结构之单向循环链表

单向循环链表与单向链表的结点结构相同,每个结点都有一个数据域、一个指针域。

数据域用来存储结点的数据,指针域用来存储下一个结点所在的内存空间地址。

两者不同的是,单向链表末结点的指针域为NULL,而单向循环链表末结点的指针域存储了头结点所在的内存空间地址。

这里实现了单向循环链表的六个基本功能,初始化链表、添加结点(头插法、尾插法)、删除结点、反转链表、遍历链表。

一、代码结构

1.1单向循环链表的数据结构

typedef struct node
{
    int data;//数据域
    struct node * next;//指针域
}Node;

1.2单向循环链表的六个方法

        1.2.1 Node * initialize()

                初始化单向循环链表,返回头结点的指针

        1.2.2 void headInsert(Node * head,int data)

                添加结点(头插法),每次插入的新结点都会作为第一个结点存在

        1.2.3 void tailInsert(Node * head,int data)

                添加结点(尾插法),每次插入的新结点都会作为末结点存在

        1.2.4 int delete(Node * head,int data)

                删除结点,将指定数据的结点删除,删除成功返回1,删除失败返回0

        1.2.5 void reverse(Node * head)

                反转链表,将链表结点的原有顺序首尾颠倒过来,其实现方法的核心还是头插法

        1.2.6 void reveal(Node * head)

                遍历链表,将链表的每个结点依次展出

二、主要代码

/*
单向循环链表
    初始化链表
    增加结点(头插法、尾插法)
    删除结点
    反转链表
    遍历链表

*/
//定义循环链表的数据结构
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
    int data;//数据域
    struct node * next;//指针域
}Node;

//初始化链表
Node * initialize()
{
    Node * head=(Node*)(malloc(sizeof(Node)));
    head->data=0;//头结点的数据域用来存储链表的结点个数
    head->next=head;//头结点的指针域用来存储后继结点所在的内存空间地址,初始化时存储自身所在的内存空间地址
    return head;
}
// 增加结点(头插法)
void headInsert(Node * head,int data)
{
    Node * newborn=(Node*)(malloc(sizeof(Node)));//创建新结点
    newborn->data=data;//给新结点的数据域赋值
    newborn->next=head->next;//新结点的指针域指向头结点的指针域所指向的内存空间地址
    head->next=newborn;//头结点的指针域指向新结点
    head->data++;//头结点的数据域自增,说明链表的结点个数增加
}
// 增加结点(尾插法)
void tailInsert(Node * head,int data)
{
    Node * newborn=(Node*)(malloc(sizeof(Node)));//创建新结点
    newborn->data=data;//给新结点的数据域赋值
    Node * start=head->next;//定义循环指针变量start,其初始值为链表中首元素的内存空间地址
    for(;start->next!=head;start=start->next);//通过循环遍历链表,将start定位到末结点
    newborn->next=start->next;//新结点的指针域指向末结点的指针域所指向的内存空间,即头结点的内存空间
    start->next=newborn;//末结点的指针域指向新结点
    head->data++;//头指针的数据域自增,说明链表的结点个数增加
}
//删除结点
int delete(Node * head,int data)
{
    Node * previousNode=head;
	//变量previousNode用来存储当前结点的前驱结点内存空间地址,初始化值为头结点的内存空间地址
    Node * start=head->next;//变量start用来存储首结点的内存空间地址,作循环变量使用
    while(start!=head)//循环变量start与变量head的值不相等时,循环继续运行
    {   
        if(start->data==data)//找到指定的数据时
        {
            previousNode->next=start->next;//当前结点的前驱结点指针域就指向当前结点的后继结点
            free(start);//释放当前结点
            head->data--;//头结点的数据域自减,说明链表的结点个数减少
            return 1;
        }
        previousNode=start;//previousNode存储本次循环的结点,在下一次循环中就变当前结点的前驱结点
        start=start->next;//当前结点向后遍历
    }
    return 0;
}
//反转链表(核心还是头插法)
void reverse(Node * head)
{
    Node * start=head->next;//变量start存储首结点的内存空间,作循环变量使用
    Node * temporary;//变量temporary用来临时存储当前结点
    head->next=head;//让头结点的指针域指向自身,表明链表中无结点
    
    while(start!=head)//当循环变量start不等于头结点指针变量head时
    {
        temporary=start;//变量temporary存储循环变量start的值,也就是存储了当前结点的内存空间地址
        start=start->next;//而循环变量start向后遍历一个结点
        temporary->next=head->next;//此时,把头结点指针域的值赋给temporary的指针域
        head->next=temporary; //让头结点的指针域指向temporary
    }
}   
//遍历链表
void reveal(Node * head)
{
    for(Node * start=head->next;start!=head;start=start->next)
    {
        if(start->next!=head)
        {
            printf("%d , ",start->data);
        }else
        {
            printf("%d\n",start->data);
        }
    }
}

int main(int argc,char * argv[])
{
    puts("--------------初始化链表--------------");
    Node * head=initialize();
    printf("head=\t%p\n\n",head);

    puts("-------------- 增加结点(头插法)--------------");
    headInsert(head,13);
    headInsert(head,14);
    headInsert(head,15);
    reveal(head);
    printf("length:\t%d\n\n",head->data);

    puts("-------------- 增加结点(尾插法)--------------");
    tailInsert(head,21);
    tailInsert(head,22);
    tailInsert(head,23);
    reveal(head);
    printf("length:\t%d\n\n",head->data);

   
    puts("--------------删除结点--------------");
    puts("删除数据为5120的结点");
    puts("删除前:");
    reveal(head);
    printf("%s\n",delete(head,5120)==1?"--------删除成功":"--------删除失败");
    reveal(head);
    printf("length:\t%d\n\n",head->data);

    puts("删除数据为13的结点");
    puts("删除前:");
    reveal(head);
    printf("%s\n",delete(head,13)==1?"--------删除成功":"--------删除失败");
    reveal(head);
    printf("length:\t%d\n\n",head->data);
    
    puts("删除数据为23的结点");
    puts("删除前:");
    reveal(head);
    printf("%s\n",delete(head,23)==1?"--------删除成功":"--------删除失败");
    reveal(head);
    printf("length:\t%d\n\n",head->data);

	puts("--------------反转链表--------------");
    puts("-------反转前");
    reveal(head);
    reverse(head);
    puts("-------反转后");
    reveal(head);

   
}

三、运行展示

 四、感受

        玩单向循环链表,很容易陷入思维惯性,把单向链表的实现方法带进来,导致一些逻辑错误。在写代码前先画画图,让自己的脑子进入单向循环链表,这样就能一次性完美实现单向循环链表。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值