IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 数据结构之单向循环链表 -> 正文阅读

[数据结构与算法]数据结构之单向循环链表

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

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

两者不同的是,单向链表末结点的指针域为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);

   
}

三、运行展示

?四、感受

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

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 12:25:10  更:2022-10-31 12:27:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 19:28:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码