数据结构单链表的创建以及简单操作

2023-10-26

在数据结构中:

目录

一、数据节点类型结构体封装

二、创建单链表

1、创建链表

2、头部插入

3.遍历链表

 4.尾部插入

5.释放链表


        链表可以解决顺序表无法开辟连续空间的问题,大大提高了内存的利用率。这使我们在开发中不再局限于小型数据量的项目。

        链表:

  •  逻辑结构为:线性结构(是一对一的)。
  • 存储结构为:链式结构(但是在内存中不一定是连续的)。

根据上图, 顺序表和链表的区别一目了然。

在链表中,每个数据元素都是一个节点,每个节点都分为数据域和指针域:

数据域:用于存储数据元素的信息

指针域:指向下一个节点的首地址

这样的节点数据类型在传统的C语言库里没有这样数据类型定义,所以我们就要用结构体的方式来封装节点。

一、数据节点类型结构体封装

typedef int data_t;//此处是用类型重命名了一个数据节点类型 data_t
                   //用typdef可以类型重定义任意数据类型
//结构体分装
struct node{
    data_t data;//数据域
    struct node *next;//指针域
};

以上就是一个数据节点类型的声明,用于链表的节点创造。

二、创建单链表

1、创建链表

链表分为有头链表和无头链表,此文章创建的是有头链表。

有头链表的创建本质就是创建头节点,一般来说头节点是数据域不作使用,所以可以设置成任意数据。

//重定义数据类型
typedef int data_t;
//封装数据节点类型
typdef struct node{
    data_t data;//数据域
    struct node *next;//指针域
} link_list_t;

1.定义链表头节点指针
link_list_t *L = NULL;//初始化为空

2.从堆区申请头节点空间
L = (link_list_t *)malloc(sizeof(link_list_t));

3.初始化头节点
L->data = -1;//不作使用 随意设置
L->next = NULL;

4.返回头结点首地址
return L;

总结(代码实现):

//函数源码

/*
*功能:创建新节点
*参数:空
*返回值:成功创建新节点的首地址
*/
link_list_t *create_list(void){
    link_list_t *L = NULL;
    
    //申请节点空间
    L = (link_list_t *)malloc(sizeof(link_list_t));
    if(NULL == L){
        puts("创建链表节点失败");
        return NULL;
    }
    
    //初始化节点
    L->data = -1;
    L->next = NULL;

    //返回节点首地址
    return L;
}    

2、头部插入

当创建链表成功后(本质是创建头节点),对链表进行操作--头部插入。

以上是头部插入的逻辑图。

算法思路:

1.要先创建一个新节点来存放要插入的数据
link_list_t *N = (link_list_t *)malloc(sizeof(link_list_t));
2.初始化新节点
N->data = value;//要插入的数据 value为传参过来的数值
N->next = NULL;
3.进行头部插入
link_list_t *Q = L;//建议定义一个新的同类型指针来接收传过来的指针参数
                    //目的是为了保护调用者的数据 防止出现不可预知的错误
N->next = Q->next;//将头节点的指针域赋值给新节点的指针域 
Q->next = N;//再将头节点的指针域存放新节点的首地址即可指向新节点

总结(代码实现):

//函数源码
/*
*功能:对单链表进行头部插入
*参数:原单链表的首地址,要插入的数据值
*返回值:成功返回0 失败返回-1
*/

int head_insert(link_list_t *L,data_t value){
    //健壮性判断 判断传过来的指针参数是否合法
    if(NULL == L){
        puts("传入参数非法");
        return -1;
    }
    
    //创建新节点
    link_list_t *N = (link_list_t*)malloc(sizeof(link_list_t));
    if(NULL==N){
		puts("创建新节点失败");
  		return -1;
    }
    //初始化新节点 
    N->data = value;
    N->next = NULL;
    //头部插入操作
    link_list_t *Q = L;
    N->next = Q->next;
    Q->next = N;

    return 0;
}

3、遍历链表

算法思路:

1.首先判断传过来的表参数是否合法
2.确保表内不为空,有数据遍历
3.我们操作的是有头链表,有效数据为头节点的下一个节点的数据域开始,可以定义一个指针指向头节点的下一个节点
4.单链表的尾节点的指针域为NULL,所以可以循环遍历,直到节点的指针域为NULL,作为结束的条件

总结(代码实现):

//函数源码
/*
*功能:遍历输出单链表内所有节点的数据域
*参数:原表参数的首地址
*返回值:成功返回0 失败返回-1
*/

int show_list(link_list_t *L){
    //健壮性判断
    if(NULL == L){
        puts("传入参数非法");
        return -1;    
    }
    //判断表是否为空
    if(NULL == L->next){
        puts("链表为空 无数据可遍历");
        return -1;
    }

    //定义一个数据节点指针变量Q指向头节点的下一位
    //直接指向有效数据位
    link_list_t *Q = L->next;
    //循环遍历输出数据域
    while(NULL == Q){
        printf("%d ",Q->data);//打印有效数据节点的数据
        Q = Q->next; 
    }
    return 0;
}

 4、尾部插入

算法思路:

1.判断表参数是否合法
2.创建新节点并初始化来存放要插入的数据
3.移动指针到链表的尾部,可以根据尾部节点指针域为NULL的特点作为循环移动的结束条件
4.插入操作
将新的节点的首地址赋值给为节点的指针域即可

总结(代码实现):

//函数源码
/*
*功能:单链表尾部插入新节点
*参数:链表的指针参数,要插入的数据值
*返回值:成功返回0 失败返回-1
*/

int tail_insert(link_list_t *L,data_t value){
    //健壮性判断
    if(NULL == L){
        puts("传入参数非法");
        return -1;
    }
    //创建新节点
    link_list_t *N = (link_list_t *)malloc(sizeof(link_list_t));
    if(N == NULL){
        puts("创建新节点失败");
        return -1;
    }
    //初始化新节点
    N->data = value;
    N->next = NULL;
    //循环移动指针指向尾部节点
    while(Q->next != NULl){
        Q = Q->next;//遍历到链表最后一个节点
    }
    //进行插入操作
    Q->next = N;
    
    return 0;
}

5、释放链表

算法思路:

1.定义一个节点指针变量Q作为动点 初始化为链表头节点的指向
link_list_t *Q = *L;//*L的原因是指针传参要用二级指针 
                    //不然释放的只是给传过来的指针参数开辟的内存空间 并不是原单链表
2.Q作为动点指向头节点的下一位
Q = Q->next;
3.将头节点*L释放掉
free(*L);
4.将Q重新赋值给*L,同时Q指向下一个节点
*L = Q;
Q = Q->next;//此步骤可以放在循环里的第一步

总结(代码实现):

//函数源码
/*
*功能:释放单链表
*参数:链表参数首地址
*返回值:成功返回0 失败返回-1
*/

int free_list(link_list_t **L){
    //健壮性判断
    if(NULL == L || NULL == *L){
        puts("传入参数非法");
        return -1;
    }
    //定义一个节点数据类型变量作为动点
    link_list_t *Q = *L;
    //循环释放
    while(*L != NULL){
        Q = Q->next;
        printf("%d即将被释放",(*L)->data);
        free(*L);
        *L = Q;
    }
    *L = NULL;
    return 0;
}

6、任意位置插入数据

算法思路:

1.进行健壮性判断指针参数是否合法 以及表内是否为空
2.定义一个动点指针--用来确定插入位置的定位左右
link_list_t *Q = L;
3.用循环 移动Q的指向  用于定位 将位置确定到要插入位置的前一个位置
4.注意:
    单链表在任意位置插入数据时需要考虑 空表和非空表两种情况
        空表时 位置参数只能是 0 其他都不可以
    单链表在插入操作时由于每个节点保存的信息只有 节点数据和下一个节点的地址信息,所以我们在插    入操作时要定位到要插入的位置的前一个节点位置。否则直接定位到要插入的位置是无法让新节点插入到对应位置上的,如果想要插入对应位置又无法与前一个节点建立联系(节点中没有存储前一个节点的信息)
5.插入操作:(定义一个新节点存放要插入的数据  再定义一个指针类型节点Q接收传过来的指针参数L)
            新节点N的指针域 = Q指向的节点的指针域
            Q指向的节点的指针域 = 新节点N

总结(代码实现):

//函数源码
/*
*功能:能在单链表任意位置插入数据
*参数:原单链表指针参数,要插入的位置,要插入的数据
*返回值:成功返回0 失败返回-1
*/

int insert_pos(link_list_t *L,int pos,data_t value){
    //健壮性判断
    if(NULL == L){    
        puts("传入参数非法");
        return -1;
    }
    //位置参数判断
    if(pos < 0){
        puts("位置参数非法");
        return -1;
    }

    //创建新节点
    link_list_t *N = (link_list_t *)malloc(sizeof(link_list_t));
    if(N == NULL){
        puts("创建节点失败");
        return -1;
    }
    //初始化节点
    N->data = value;
    N->next = NULL;
    //定位操作
    int i= 0;
    link_list_t *Q = L;
    for(;i<pos;i++){
        if(NULL = Q && pos>0){
            printf("表为空表 未知参数必须等于0\n");
            return -1;    
        }
        if(NULL==Q->next && (i<pos-1||pos<0)){
            printf("传入位置参数超出链表长度 应传入大于0且小于等于¥d\n",i+1);
            return -1;
        }
        Q = Q->next;
    }
    //插入操作
    N->next = Q->next;
    Q->next = N;
    return 0;
}

7、任意位置删除数据

算法思路:

1.判断表参数和位置参数是否都合法
2.定位节点 循环移动指针直到指向要删除的位置的前一个节点
3.删除操作

总结(代码实现):

//函数源码
/*
*功能:能在链表中任意位置删除数据
*参数:原链表指针参数,要删除的数据的位置
*返回值:成功返回0 失败返回-1
*/

int insert_pos(link_list_t *L,int pos){
    //健壮性判断 
    if(NULL==L){
    	puts("传入表参数非法");
    	return -1;
    }
    if(NULL==L->next){
    	puts("表为空表 无数据可删除");
    	return -1;
    }
    if(0>pos){
    	puts("位置参数必须大于 0");
    	return -1;
    }
    	
    //定位操作 
    int i = 0;
    link_list_t *Q = L;
    for(;i<pos;i++){//循环移动 q指针的指向,直到q指向要删除位置的前一个节点(i<pos)
    	Q = Q->next;
    	if(NULL==Q->next && pos>0){//此处判断表只有一个元素时 pos的合法性
    		printf("表只有一个元素 只可以删除 0 位置节点\n");
    		return -1;
    	}
    	if(NULL==Q->next->next && i<pos-1){//此处判断遍历到链表尾部时还要遍历则pos超出表长
    		printf("位置参数超越表长 位置参数应大于等于 0 且 小于 %d\n",i);
    		return -1;
    	}
    }
    
    //删除操作
    link_list_t *P = Q->next;
    Q->next = Q->next->next;
    printf("%d 将被删除\n",P->data);
    P->next = NULL;
    free(P);
    P = NULL;
    
    return 0;
}

9、翻转链表

算法思路:

1.可以将一个链表截断成两个链表,一分为二
如:我们有一个 L 表,可以将它截成 L 表和 Q表。
link_list_t *Q = L->next->next;//此处的的截断点在 L 表数据节点的第一个节点后
                               //将第一个数据节点后的节点由 Q控制
L->next->next = NULL;//彻底断掉
2.将 Q 表的节点通过头删法 依次对 L表进行头部插入数据
link_list_t *P = Q;
while(Q!=NULL){
    P = Q;
    Q = P->next;        
    P->next = L->next;
    L->next = P;
}
直至Q 表所有节点插入L表停止,
至此链表翻转完成

总结(代码实现):

//函数源码
/*
*功能:将单链表翻转
*参数:原链表指针参数
*返回值:成功返回0 失败返回-1
*/

int turn_list(link_list_t *L){
	//健壮性判断 
	if(NULL==L){
		puts("传入表参数非法");
		return -1;
	}
	if(NULL==L->next || NULL==L->next->next){
		puts("表为空表或只有一个数据元素 无需翻转");
		return -1;
	}
	//翻转操作 
	//将原表分为两个表 Q 表和 L表
	link_list_t *Q = L->next->next;
	L->next->next = NULL;

	//将 P 表中的节点 依次对 L 表进行头部插入
	link_list_t *P = Q;
	while(Q!=NULL){
		P = Q;
		Q = P->next;
		P->next = L->next;
		L->next = P;
	}
	return 0;
}

至此,数据结构创建单链表以及相关简单操作的算法思想以及思路就分享完毕啦!作者也是个数据结构的初学者,这是在学习过程中的笔记整理,在此分享,如果文章内容有什么错误或者是不足的地方,欢迎伙伴们向我私信或者评论告诉我,我会及时改正~谢谢(#^.^#)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构单链表的创建以及简单操作 的相关文章

随机推荐

  • 制作跟文件系统时出现的一些错误

    在make busybox的时候出现如下错误 arm none linux gnueabi libc usr include linux netfilter h 44 error field in has incomplete type a
  • 微信小程序——点击列表里某元素,实现跳转+显示点击元素的内容

    最近刚刚学微信小程序没多久 碰到了很多问题 这也是我碰到的一个问题 思路 跳转 组件绑定一个点击事件 在事件里面实现跳转 显示点击元素的内容 获取点击元素在列表里面的id 但是我发现我获取到的id是空的 啥也没有 呜呜呜 可怜的小白 都不知
  • Ubuntu下安装man中文手册(中英文共有)

    安装步骤 1 先安装所需要的依赖包 automake 工具 sudo apt get install autoconf automake libtool git 工具 sudo apt get install git 2 下载中文man安装
  • 自学Mysql-存储过程--判断和循环语句

    case 用case解决传入的值是否是数字 CREATE PROCEDURE test case num int out flag varchar 20 BEGIN case num 类似于swtich when num gt 0 then
  • android recycleview 没有填满屏幕

    最近使用recyclerview 每次绘制的item 虽然写的是填充父控件 但是每次效果都是包裹内容 没有填满手机屏幕 后来才意识到是填充子view的时候出现了问题 没有填满屏幕的时候 你可以试着在item view的主布局设置一个back
  • GBase 8c亮相国内首款金融数据库性能测试工具开源发布会

    2 月 17 日 由信通院主办的国内首款金融数据库性能测试工具开源发布会在线上召开 会上 定位于国家高端专业智库 产业创新发展平台的信通院宣布开源该测试工具 并详细阐述了开源此工具的背景 初心 历程以及愿景 南大通用受邀参加此次发布会 GB
  • 用for语句算15的阶乘

    x 1 for i in range 1 16 i从1依次取到15 x x i print x
  • webrtcvad 安装失败

    倒腾了两个小时终于解决了这个问题 所有办法都试了 只有这个管用 下载安装VC 不要下载那种在线安装的 我试了很多次 都是安装包丢失或损坏 直接复制下面链接去下载 百度网盘链接 https pan baidu com s 1IaqkukMzb
  • 我的个人博客

    经过几天鼓捣 我的个人博客终于建成了 为了提高网站安全性我把http协议升级成了https的 带有传输加密的协议能保证传输的安全而且可以防止篡改网站的网页 网站的访问速度也不能慢 为此我有花费了一些精力配置了CDN 现在通过https ww
  • es数据量过大,内存撑不住,关闭部分历史数据索引,以及备份删除索引

    关闭索引命令 curl XPOST http ip 9200 索引名称 close 恢复索引命令 curl XPOST http ip 9200 202205 open 遇到问题索引权限问题关闭失败 type cluster block e
  • 12.话题消息的定义与实现

    学习视频 https www bilibili com video BV1zt411G7Vn p 12 目标 消息的自定义 发布及订阅个人信息 一 自定义话题信息 1 定义msg文件 mkdir catkin ws src learning
  • OkHttp:基本使用详解

    简介 OkHttp是一个高效的HTTP客户端 它有以下默认特性 支持HTTP 2 允许所有同一个主机地址的请求共享同一个socket连接 连接池减少请求延时 透明的GZIP压缩减少响应数据的大小 缓存响应内容 避免一些完全重复的请求 当网络
  • TypeError: test_recharge() missing 1 required positional argument: ‘item‘

    提示test recharge 缺少一个必须的位置参数 item 说明是item有问题 debug后 发现item没有出现用例中的数据 再看是发现没有写 ddt 写上去就好了
  • CorelDraw X4 unable to load resource dll_七夕小子_新浪博客

    CorelDraw X4以前安装过 现在重新安装后 运行时 弹出几个对话框 Unable to load resource dll CRLUTLINTL dll Unable to load resourse DLL CrlCmnMappe
  • java中让控制台输出彩色字符的方法-Jansi

    在网上有很多类似的文章 教你如何在控制台输出彩色字符 其中比较好的方法是用别人的做好的包 Jansi 但是在网上很多的文章没有给出完整的操作过程 只是给出了方法 在这里将会有完整的过程 1 下载jnsi包 http maven outofm
  • 【报告分享】ChatGPT:AI模型框架研究.pdf(附下载链接)

    省时查报告 专业 及时 全面的行研报告库 省时查方案 专业 及时 全面的营销策划方案库 免费下载 2023年2月份热门报告合集 限时免费 ChatGPT4体验 无需翻墙直接用 ChatGPT团队背景研究报告 ChatGPT的发展历程 原理
  • Selenium基础用法

    目录 一 概念和自己的理解 二 安装 三 浏览器驱动 四 正真的基础上场 1 先要打开浏览器 打不开 我们后面也就做不了 万事开头先有前提 2 获取元素的方法 3 操作元素 4 浏览器操作 5 鼠标操作 6 键盘操作 7 下拉框操作 8 页
  • JDK 1.5 新特性

    文章目录 1 JDK 1 5 新特性 1 1 自动装箱和拆箱 1 1 1 什么是自动装箱和拆箱 1 1 2 自动装箱拆箱要点 1 1 3 自动装箱拆箱样例 1 1 4 自动装箱缺点 1 1 5 重载与自动装箱 1 1 6 自动拆装箱的缓存机
  • 【位图&&布隆过滤器&&海量数据面试题】

    文章目录 1 位图 2 布隆过滤器 1 位图 首先我们来看看一个腾讯的面试题 给40亿个不重复的无符号整数 没排过序 给一个无符号整数 如何快速判断一个数是否在这40亿个数中 分析 40亿个不重复整形数据 大概有160亿字节 也就是16GB
  • 数据结构单链表的创建以及简单操作

    在数据结构中 目录 一 数据节点类型结构体封装 二 创建单链表 1 创建链表 2 头部插入 3 遍历链表 4 尾部插入 5 释放链表 链表可以解决顺序表无法开辟连续空间的问题 大大提高了内存的利用率 这使我们在开发中不再局限于小型数据量的项