C语言单链表基本操作,非常全面
单链表的基本操作分享:
/********************* 单链表的常规操作 ****************************/LinkList CreateHeadListH(); // 头插法创建单链表
LinkList CreateHeadListT(); // 尾插法创建单链表
int ListEmpty(); // 单链表判空
int ListLength(); // 求单链表长度
void Travel(); // 遍历单链表
int InsertNode(); // 插入结点
int DeleteNode(); // 删除结点
ElemType GetElem(); // 按址查值
int GetLocate(); // 按值查址
int RemoveRepeat(); // 去除重复的值/*****************************************************************/
定义单链表结构体
单链表是由多个结点链接组成,它的每个结点包含两个域,一个数据域和一个链接域(地址域)。
- 数据域
data
用来存放具体的数据。 - 地址域
next
用来存放下一个节点的位置。
#include "stdio.h"
#include "malloc.h"#define TRUE 1
#define FALSE 0typedef int ElemType; // 单链表存储元素的数据类型// 定义单链表结构体
typedef struct Node(){ElemType data; // 单链表结点数据域struct Node *next; // 单链表结点地址域(指向下一个结点)
}*LinkList, Node;
构造单链表
头插法实现
/** 头插法创建单链表(带头结点)* datas 接收数组,用于赋值链表的结点数据* len datas数组的长度,便于遍历
*/
LinkList CreateHeadListH(ElemType *datas, int len){// 创建头结点LinkList head, new_node;head = (LinkList)malloc(sizeof(Node));// head -> data = len; // 可以把链表长度存在头结点的数据域中head -> next = NULL;// 分配新节点并用头插法链接起来for(int i=0;i<len;i++){new_node = (LinkList)malloc(sizeof(Node));new_node -> data = datas[i];new_node -> next = head -> next;head -> next = new_node;}return head;
}
头插法构造单链表时一直往单链表的头部插入结点。
尾插法实现
/** 尾插法创建单链表(带头结点)* datas 接收数组,用于赋值链表的结点数据* len datas数组的长度,便于遍历
*/
LinkList CreateHeadListT(ElemType *datas, int len){// 创建头结点LinkList head, p, new_node;head = (LinkList)malloc(sizeof(Node));head -> next = NULL;p = head;// 分配新节点并用尾插法链接起来for(int i=0;i<len;i++){new_node = (LinkList)malloc(sizeof(Node));new_node -> data = datas[i];new_node -> next = NULL;p -> next = new_node;p = new_node;}return head;
}
尾插法构造单链表时一直往单链表的尾部插入结点。
单链表的头尾插法详解
为了不让文章篇幅过长,关于单链表头尾插法的更多具体内容请观看我的另一篇博客 单链表的头尾插法详解
单链表判空
/** 单链表判空* list 接收单链表
*/
int ListEmpty(LinkList list){return (list == NULL || list -> next == NULL);
}
计算单链表长度
/** 计算单链表的长度* list 接收单链表
*/
int ListLength(LinkList list){LinkList p = list;int len = 0;if(ListEmpty(list)){return len;}p = p -> next;while(p != NULL){len ++;p = p -> next;}return len;
}
遍历单链表
/** 遍历单链表* list 单链表
*/
void Travel(LinkList list){LinkList p = list;if(!ListEmpty(list)){ // 单链表非空情况下才遍历p = p -> next; // 因为带头结点单链表所以要先走一步while(p != NULL){printf("%d\t", p -> data);p = p -> next;}printf("\n");}
}
单链表头、尾插法构造效果
int main(int argc, char const *argv[])
{int datas[] = {2, 4, 6, 8, 10};// 动态计算datas数组的长度// 数组长度 = 数组的总空间大小 / 数组中每个元素所占空间大小int len = sizeof(datas) / sizeof(datas[0]);printf("头插法构造单链表\n");LinkList list_h = CreateHeadListH(datas, len);printf("ListEmpty():%d\n", ListEmpty(list_h)); // 判空printf("ListLength():%d\n", ListLength(list_h)); // 求长printf("Travel():"); Travel(list_h); // 遍历printf("\n尾插法构造单链表\n");LinkList list_t = CreateHeadListT(datas, len);printf("ListEmpty():%d\n", ListEmpty(list_t));printf("ListLength():%d\n", ListLength(list_t));printf("Travel():");Travel(list_t);return 0;
}
因为数组是在连续的地址上存储元素,所以可以动态的计算数组的长度,方便遍历。
输入结果如下:
头插法构造单链表
ListEmpty():0
ListLength():5
Travel():10 8 6 4 2尾插法构造单链表
ListEmpty():0
ListLength():5
Travel():2 4 6 8 10请按任意键继续. . .
单链表指定位置插入结点
代码实现
/** 单链表指定位置插入结点* list 单链表* data 要插入的结点的数据* pos 结点插入的位置(逻辑位置(1,2,3,...))
*/
int InsertNode(LinkList list, ElemType data, int pos){LinkList p = list, new_node;if(ListEmpty(list)){printf("空单链表\n");return FALSE;}// 判断插入位置是否合理if(pos <= 0 || pos > ListLength(list) + 1){printf("插入位置不合理\n");return FALSE;}// 寻找到要插入位置的前一个结点for(int i = 0; i < pos - 1 && p != NULL; i++){p = p -> next;}// 准备新结点new_node = (LinkList)malloc(sizeof(Node));new_node -> data = data;// 此时p就是要插入位置的前一个结点,p -> next就是要插入位置的结点new_node -> next = p -> next; p -> next = new_node;return TRUE;
}
详细图解
假设原单链表为:head --> 2 --> 6
,要插入的结点值为 4,插入位置为 2。
只需找要插入位置的前一个结点就行,因为插入位置的前一个结点的地址域保存着要插入位置的结点。
此时找到的结点是 new_code1
,而 new_code1 -> next
就是结点 new_code2
。
所以我们只要
new_code3 -> next = new_code1 -> next;
new_code1 -> next = new_code3;
先让待插入结点的地址域指向插入位置的结点
后让插入位置的前一个结点的地址域指向待插入结点。
1, 2, 3代表单链表结点位置
①,②,③代表插入操作的执行步骤顺序
注意:千万不能先让插入位置的前一个结点的地址域指向待插入结点,后让待插入结点的地址域指向插入位置的结点
new_code1 -> next = new_code3;
// 此时new_code1 -> next 等于 new_code3, now_code3 -> next = new_code3 没有达到链接
new_code3 -> next = new_code1 -> next;
单链表指定位置删除结点
代码实现
/** 单链表指定位置删除结点* list 单链表* *val 用来存储删除结点的数据* pos 结点删除的位置(逻辑位置(1,2,3,...))
*/
int DeleteNode(LinkList list, ElemType *val, int pos){LinkList p = list, r;if(ListEmpty(list)){printf("空单链表\n");return FALSE;}// 判断删除位置是否合理if(pos <= 0 || pos > ListLength(list)){printf("删除位置不合理\n");return FALSE;}// 寻找到要删除结点的前一个位置for(int i = 0; i < pos - 1 && p != NULL; i++){p = p -> next;}r = p -> next; // 记录要删除的结点*val = r -> data; // 把删除结点的数据利用指针返回去p -> next = r -> next; // 把链表重新链接起来free(r); // 释放删除结点的资源return TRUE;
}
详细图解
假设原单链表为:head --> 2 --> 4 --> 6
,删除第 2 个结点。
还是跟插入一样只需找要删除位置的前一个结点就行。
此时找到的结点是 new_code1
,而 new_code1 -> next
就是结点 new_code2
,就是要删除的结点。
r = new_code1 - > next;
new_code1 -> next = r -> next; // new_code1 -> next = new_code2 -> next;
free(r);
先让变量 r
等于要删除的结点 ,
r = new_code1 - > next; --> r = new_code2;
后让删除位置的前一个结点的地址域指向要删除结点的后一个结点。
new_code1 -> next = r -> next; // 此时r -> next 等于 new_code2↓↓
new_code1 -> next = new_code2 -> next; // new_code2 -> next 等于 new_code3↓↓
new_code1 -> next = new_code3;
最后释放删除结点空间 free(r)
删除第二个位置节点后的单链表:head --> 2 --> 6
按址求值
/** 根据指定位置求结点的值(没有找到返回 0 )* list 单链表* pos 结点位置(逻辑位置(1,2,3,...))
*/
ElemType GetElem(LinkList list, int pos){LinkList p = list;if(ListEmpty(list)){printf("空单链表\n");return FALSE;}if(pos <= 0 || pos > ListLength(list)){printf("位置不合理\n");return FALSE;}for(int i = 0; i < pos && p !=NULL; i++){p = p -> next;}return p -> data;
}
按值求址
/** 根据指定的值寻找结点的位置* (如果有多个值相同返回第一个找到的结点的位置, 没找到则返回 0)* list 单链表* data 要查找的值
*/
int GetLocate(LinkList list, ElemType data){LinkList p = list;int pos = 0;if(ListEmpty(list)){return FALSE;}p = p -> next;while(p != NULL){pos ++;if(p -> data == data){return pos;}p = p -> next;}return FALSE;
}
单链表去重
/** 去除单链表中重复的值(重复的值只保留一个)* list 单链表* 返回值:对单链表进行了去重操作返回 1,否则返回 0
*/
int RemoveRepeat(LinkList list){LinkList p = list, q, r;int flag = 0;if(ListEmpty(list)){return FALSE;}p = p -> next;while(p != NULL){q = p;while(q != NULL && q -> next != NULL){if(p -> data == q -> next -> data){r = q -> next; // 记录值相同的结点q -> next = r -> next;free(r);flag = 1;}else{q = q -> next;}}p = p -> next;}return flag;
}
原理就是每次拿没有比较过的结点跟单例表中的每一个结点进行比较,遇到相同的就删除其中一个结点。
例如:单链表序列为 2 4 2 8 8 6 6 8 12
首先拿第一个结点 2 跟单链表的其他结点比较
2 4 2 8 8 6 6 8 12
↑↓↓
遇到相同的就删除
2 4 8 8 6 6 8 122比完了一轮去重了2,然后用第二个结点 4 跟单链表的其他没有比较过的结点比较
2 4 8 8 6 6 8 12 ↑ ↓↓
2 4 8 8 6 6 8 12 4比完了一轮去重了4,然后用第三个结点 8 跟单链表的其他没有比较过的结点比较
2 4 8 8 6 6 8 12 ↑↓↓
2 4 8 6 6 12循环类推
2 4 8 6 6 12↑↓↓
2 4 8 6 12 最后一步
2 4 8 6 12↑↓↓
2 4 8 6 12
看看去重效果
去重前的单链表
ListLength():9
Travel():2 4 2 8 8 6 6 8 12去重后的单链表
ListLength():5
Travel():2 4 8 6 12
源代码
源代码已上传到 GitHub Data-Structure-of-C,欢迎大家来访。
C语言单链表基本操作,非常全面相关推荐
- C语言单链表基本操作总结
C语言单链表基本操作 本文是参考他人实现的C语言单链表,对多篇博文整理的结果,仅作为学习笔记.文末有参考出处. 1.单链表定义 链表是通过一组任意的存储单元来存储线性表中的数据元素,这些存储单 ...
- C 语言单链表基本操作
复习下数据结构,使用 C 语言实现了带头节点的单链表.单链表基本操作有:节点初始化.链表初始化.插入节点.查找节点.删除节点和删除链表等.下面的程序中,list.h 为头文件, 其中包含了上述基本操作 ...
- 数据结构-单链表基本操作-C语言代码
单链表基本操作 1.头插法建立单链表 2.尾插法建立单链表 3.查找结点 3.修改结点 4.插入结点 5.删除结点 本篇只有c语言代码,具体思路讲解请看这篇博客:数据结构-线性结构-单链表 1.头插法 ...
- C语言单链表实现初始化、创建、增、删、查等基本操作(详细)
C语言单链表实现初始化.创建.增.删.查等基本操作 #include <stdio.h> #include <stdlib.h> #include <malloc.h&g ...
- C语言实现单链表基本操作
C语言实现单链表基本操作的 目录
- c语言单链表功能,[数据结构]单链表(C语言)的各种功能
06-03阅读200,000 + 链表是一种常见的基本数据结构,在此充分利用了结构指针. 链表可以动态存储和分配,即链表是一个功能非常强大的数组. 他可以在节点中定义多种数据类型,并可以根据需要随意添 ...
- C语言一趟冒泡交换最小值,C语言单链表冒泡排序为啥以下代码实现不了?
struct node *sort(struct node *head)/*排序*/ { struct node *p,*q; struct node *temp; for(p=head;p!=NUL ...
- C语言单链表,能直接运行的代码!
C语言单链表,实现增删改查 不废话 直接上代码,COPY就能运行 #include <stdio.h> #include <stdlib.h> /** *定义数据元素 */ t ...
- 7-4 单链表基本操作
7-4 单链表基本操作 请编写程序实现单链表插入.删除结点等基本算法.给定一个单链表和一系列插入.删除结点的操作序列,输出实施上述操作后的链表.单链表数据域值为整数. 输入格式: 输入第1行为1个正整 ...
最新文章
- 到底有多火?三家单位争抢发布,谷歌、清华、牛津同时提超越注意力的新机制...
- 死猪脑”能复活吗?---评美国耶鲁大学医学院实验研究
- 全球第一家只接收BCH的慈善组织
- C#玩转指针(二):预处理器、using、partial关键字与region的妙用
- OpenCV学习之Mat::at()理解
- 2020年最畅销的20款电动汽车,特斯拉和五菱你偏向谁?
- html邮件和纯文本邮件区别,邮件营销必读系列五--纯文本和HTML邮件类型——哪一种邮件类型更适合你?...
- mybatis mysql 调用存储过程 多个返回值_图解MyBatis的SQL执行流程(干货)
- 说说windows10自带浏览器Edge的好与不好
- linux中,一个目录的权限是777,普通用户为什么删除不了它呢?
- 【mysql】mysql常用语句
- Velocity笔记(上)
- win7计算机打开显卡设置在哪里,win7在哪里打开显卡设置
- 在eclipse中编写HDFS的Java程序
- 【MYSQL】学习笔记
- 人工智能未来的发展前景
- 十大宽带共享组网方式推荐
- Socket编程 TCP粘包问题及解决方案
- 将图片进行base64 编码后的数据进行读取,以io流的方式传给前台并显示出来并且不断刷新图片
- 2023计算机毕业设计SSM最新选题之java住宅小区停车管理系统494ak