当前位置: 首页 > news >正文

C语言_数据结构总结3:带头结点的单链表

 纯c语言代码,不涉及C++

0. 结点结构

typedef int ElemType;
typedef struct LNode {
    ElemType data;  //数据域
    struct LNode* next;  //指针域
}LNode, * LinkList;
 

1. 初始化

带头结点的初始化,即创建一个头结点,并让头指针指向头结点,头结点的next域初始化为NULL

void InitLink1(LinkList* L) {
	*L = (LinkList)malloc(sizeof(LNode));
	if (*L == NULL)
	{
		printf("内存分配失败!\n");
		exit(1);  //退出系统的操作
	}
	(*L)->next = NULL;
}

2. 头插法

int headInsert(LinkList L,ElemType value) {
	LinkList s = (LinkList)malloc(sizeof(LNode));
	if (s == NULL)
	{
		printf("内存分配失败!\n");
		return -2;
	}
	s->data = value;
	s->next = L->next;
	L->next = s;
	return 0;  //插入成功
}

3. 尾插法

int tailInsert(LinkList L,ElemType value) {
	LinkList s = (LinkList)malloc(sizeof(LNode));
	if (s == NULL)
	{
		printf("内存分配失败!\n");
		return -2;
	}
	s->data = value;
	s->next = NULL;
	LinkList p = L;
	while (p->next != NULL) {
		p = p->next;
	}
	p->next = s;
	return 0;  //插入成功
}

4. 插入

    /*
    插入步骤3中判断 p == NULL 的原因:
当在遍历链表寻找插入位置的前一个结点时,可能会出现以下情况:
一:插入位置超出链表长度
如果要插入的位置 pos 大于链表的实际长度,那么在遍历过程中,指针 p 会沿着链表不断向后移动,最终会移动到链表的末尾(即 p->next == NULL),然后再尝试移动到下一个结点时,p 就会变成 NULL。
二:链表为空且插入位置不为 1
如果链表为空(只有头结点),并且要插入的位置 pos 大于 1,那么在遍历过程中,指针 p 从链表的头结点开始,由于链表没有其他结点,p 会在寻找插入位置的前一个结点时变成 NULL。
在上述两种情况下,如果继续进行插入操作会导致错误,因为此时 p 已经不指向链表中的有效结点,无法进行后续的插入操作。因此,当 p 为 NULL 时,说明插入位置不合法,函数会返回 -1 表示插入失败。
    */

int insertLNode1(LinkList L, int pos, ElemType value) {
	//1. 判断插入位置是否合理
	if (pos < 1)
	{
		printf("插入位置不合法!\n");
		return -1;
	}
	//2. 找到待插入位置的前一个结点
	LinkList p = L;  // 定义一个新指针,让其刚开始指向头结点
	int i = 0;
	while (p != NULL && i < pos - 1) {
		p = p->next;
		i++;
	}
	//3. 判断
	if (p == NULL)
	{
		printf("插入位置不合法!\n");
		return -1;
	}

	LinkList s = (LinkList)malloc(sizeof(LNode));  //为要插入的数据分配空间
	if (s == NULL)
	{
		printf("内存分配失败!\n");
		return -2;
	}
	s->data = value;
	s->next = p->next;
	p->next = s;
	return 0;  //插入成功
}

5. 删除

即:删除第pos个位置上的值

int deleteNode(LinkList L, int pos) {
	if (pos < 1)
	{
		printf("删除位置不合理!\n");
		return -1;
	}
	LinkList p = L;
	int i = 0;
	while (p != NULL && i < pos - 1) {
		p = p->next;
		i++;
	}
	if (p == NULL || p->next == NULL)
	{
		printf("删除位置不合理!\n");
		return -1;
	}
	LinkList temp = p->next;
	p->next = temp->next;
	free(temp);
	return 0;  //删除成功
}

6. 按位查找

即:查找第pos个位置上的value值

int findValueByPos(LinkList L, int pos, ElemType* value) {
	if (pos < 1)
	{
		printf("查找位置不合理!\n");
		return -1;
	}
	LinkList p = L->next;
	int i = 1;
	while (p != NULL && i < pos) {
		p = p->next;
		i++;
	}
	if (p == NULL)
	{
		printf("查找位置超出链表长度!\n");
		return -1;
	}
	*value = p->data;
	return 0; 
}

7. 按值查找

即:查找value值在链表的第pos个位置

int findPosByvalue(LinkList L, ElemType value) {
	LinkList p = L->next;
	int pos = 1;
	while (p != NULL) {
		if (p->data == value)
		{
			return pos;
		}
		p = p->next;
		pos++;
	}
	return -1;  //查找失败
}

8. 链表打印

void printLink1(LinkList L) {
	if (L == NULL || L->next == NULL)
	{
		printf("链表为空!\n");
		return;//提前结束函数
	}
	/*
		对判断条件的分析
1. L == NULL
	 L 是指向链表头结点的指针。
	 当 L == NULL 时,意味着这个指针没有指向任何有效的头结点,
	 也就是根本不存在链表,自然可以判定链表为空。
	 这可能是由于初始化失败或者其他异常情况导致头结点的内存分配没有成功,
	 使得头指针没有指向有效的内存区域。
2. L->next == NULL
	 当 L->next == NULL 时,说明头结点的 next 指针为空。
	 在带头结点的单链表中,头结点的 next 指针指向的是第一个存储实际数据的结点。
	 如果这个指针为空,那就表示链表中没有存储实际数据的结点,也就是链表为空。
	 虽然存在头结点,但它不存储实际数据,链表中没有有效的数据元素,所以可以判定链表为空。
	*/
	LinkList s = L->next;  //即让指针s指向了头结点后的第一格结点
	while (s != NULL) {
		printf("%d ", s->data);
		s = s->next;
	}
	printf("\n--------------------------------\n");
}

9. 释放空间

void freeList1(LinkList L) {
	LinkList p = L;  // 定义一个临时指针指向头结点(和头指针一样,指向头结点,不同的是,该结点后续有变动)
	while (p != NULL) {
		LinkList temp = p;  // 在每次循环中,创建一个临时指针 temp,并将其指向当前结点 p。这是因为在释放当前结点之前,需要先保存该结点的地址,以便后续调用 free 函数释放其内存。
		p = p->next;
		free(temp);
	}
}

10. 测试代码

int main() {
	//测试插入方法
	LinkList L1;
	InitLink1(&L1);
	insertLNode1(L1, 1, 11);
	insertLNode1(L1, 2, 22);
	insertLNode1(L1, 3, 33);
	printLink1(L1);  // 11 22 33 
	freeList1(L1);

	// 测试头插法
	LinkList L2;
	InitLink1(&L2);
	headInsert(L2, 1);
	headInsert(L2, 2);
	headInsert(L2, 3);
	printLink1(L2);  // 3 2 1
	freeList1(L2);

	// 测试尾插法
	LinkList L3;
	InitLink1(&L3);
	tailInsert(L3, 1);
	tailInsert(L3, 2);
	tailInsert(L3, 3);
	printLink1(L3);  // 1 2 3


	// 测试删除
	deleteNode(L3, 3);
	printf("删除第三个结点后:");
	printLink1(L3);  // 删除第三个结点后:1 2

	//测试按值查找
	printf("数值1在第%d个位置\n", findPosByvalue(L3, 1));  //数值1在第1个位置

	//测试按位查找
	ElemType value;
	findValueByPos(L3, 1, &value);
	printf("第1个位置的值为%d\n", value);  //第1个位置的值为1

	freeList1(L3);

	return 0;
}

11. 完整代码

#include<stdio.h>
#include<stdlib.h>
/*
    带头结点的单链表
*/

typedef int ElemType;
typedef struct LNode {
	ElemType data;  //数据域
	struct LNode* next;  //指针域
}LNode, * LinkList;

//操作1—— 带头结点的初始化,即创建一个头结点,并让头指针指向头结点,头结点的next域初始化为NULL
void InitLink1(LinkList* L) {
	*L = (LinkList)malloc(sizeof(LNode));
	if (*L == NULL)
	{
		printf("内存分配失败!\n");
		exit(1);  //退出系统的操作
	}
	(*L)->next = NULL;
}


// 操作2——带头结点的插入操作
int insertLNode1(LinkList L, int pos, ElemType value) {
	//1. 判断插入位置是否合理
	if (pos < 1)
	{
		printf("插入位置不合法!\n");
		return -1;
	}
	//2. 找到待插入位置的前一个结点
	LinkList p = L;  // 定义一个新指针,让其刚开始指向头结点
	int i = 0;
	while (p != NULL && i < pos - 1) {
		p = p->next;
		i++;
	}
	//3. 判断
	if (p == NULL)
	{
		printf("插入位置不合法!\n");
		return -1;
	}
	
	LinkList s = (LinkList)malloc(sizeof(LNode));  //为要插入的数据分配空间
	if (s == NULL)
	{
		printf("内存分配失败!\n");
		return -2;
	}
	s->data = value;
	s->next = p->next;
	p->next = s;
	return 0;  //插入成功
}

// 操作2.1——带头结点的头插法建立单链表方法
int headInsert(LinkList L,ElemType value) {
	LinkList s = (LinkList)malloc(sizeof(LNode));
	if (s == NULL)
	{
		printf("内存分配失败!\n");
		return -2;
	}
	s->data = value;
	s->next = L->next;
	L->next = s;
	return 0;  //插入成功
}

// 操作2.2——带头结点的尾插法建立单链表方法
int tailInsert(LinkList L,ElemType value) {
	LinkList s = (LinkList)malloc(sizeof(LNode));
	if (s == NULL)
	{
		printf("内存分配失败!\n");
		return -2;
	}
	s->data = value;
	s->next = NULL;
	LinkList p = L;
	while (p->next != NULL) {
		p = p->next;
	}
	p->next = s;
	return 0;  //插入成功
}

// 操作3——删除第pos个位置上的值
int deleteNode(LinkList L, int pos) {
	if (pos < 1)
	{
		printf("删除位置不合理!\n");
		return -1;
	}
	LinkList p = L;
	int i = 0;
	while (p != NULL && i < pos - 1) {
		p = p->next;
		i++;
	}
	if (p == NULL || p->next == NULL)
	{
		printf("删除位置不合理!\n");
		return -1;
	}
	LinkList temp = p->next;
	p->next = temp->next;
	free(temp);
	return 0;  //删除成功
}

// 操作4——按位查找:查找第pos个位置上的value值
int findValueByPos(LinkList L, int pos, ElemType* value) {
	if (pos < 1)
	{
		printf("查找位置不合理!\n");
		return -1;
	}
	LinkList p = L->next;
	int i = 1;
	while (p != NULL && i < pos) {
		p = p->next;
		i++;
	}
	if (p == NULL)
	{
		printf("查找位置超出链表长度!\n");
		return -1;
	}
	*value = p->data;
	return 0; 
}

// 操作5——按值查找:查找value值在链表的第pos个位置
int findPosByvalue(LinkList L, ElemType value) {
	LinkList p = L->next;
	int pos = 1;
	while (p != NULL) {
		if (p->data == value)
		{
			return pos;
		}
		p = p->next;
		pos++;
	}
	return -1;  //查找失败
}

// 操作6——带头结点的单链表打印操作
void printLink1(LinkList L) {
	if (L == NULL || L->next == NULL)
	{
		printf("链表为空!\n");
		return;//提前结束函数
	}
	LinkList s = L->next;  //即让指针s指向了头结点后的第一格结点
	while (s != NULL) {
		printf("%d ", s->data);
		s = s->next;
	}
	printf("\n--------------------------------\n");
}

// 操作7——释放带头结点链表内存
void freeList1(LinkList L) {
	LinkList p = L;  // 定义一个临时指针指向头结点(和头指针一样,指向头结点,不同的是,该结点后续有变动)
	while (p != NULL) {
		LinkList temp = p;  // 在每次循环中,创建一个临时指针 temp,并将其指向当前结点 p。这是因为在释放当前结点之前,需要先保存该结点的地址,以便后续调用 free 函数释放其内存。
		p = p->next;
		free(temp);
	}
}



int main() {
	//测试插入方法
	LinkList L1;
	InitLink1(&L1);
	insertLNode1(L1, 1, 11);
	insertLNode1(L1, 2, 22);
	insertLNode1(L1, 3, 33);
	printLink1(L1);  // 11 22 33 
	freeList1(L1);

	// 测试头插法
	LinkList L2;
	InitLink1(&L2);
	headInsert(L2, 1);
	headInsert(L2, 2);
	headInsert(L2, 3);
	printLink1(L2);  // 3 2 1
	freeList1(L2);

	// 测试尾插法
	LinkList L3;
	InitLink1(&L3);
	tailInsert(L3, 1);
	tailInsert(L3, 2);
	tailInsert(L3, 3);
	printLink1(L3);  // 1 2 3


	// 测试删除
	deleteNode(L3, 3);
	printf("删除第三个结点后:");
	printLink1(L3);  // 删除第三个结点后:1 2

	//测试按值查找
	printf("数值1在第%d个位置\n", findPosByvalue(L3, 1));  //数值1在第1个位置

	//测试按位查找
	ElemType value;
	findValueByPos(L3, 1, &value);
	printf("第1个位置的值为%d\n", value);  //第1个位置的值为1

	freeList1(L3);

	return 0;
}

12. 运行截图

相关文章:

  • MAC电脑常用操作
  • Nginx的反向代理(超详细)
  • 历史脉络总结;夏商周的主要特征
  • Springboot基础篇(4):自动配置原理
  • Android Studio 配置国内镜像源
  • 【Python 数据结构 9.树】
  • elasticsearch学习
  • 告别手动复制粘贴:可定时自动备份的实用软件解析
  • 基金股票期权期货投资方式对比
  • 数字投屏叫号器-发射端python窗口定制
  • EB-Cable许可管理的重要性
  • 我用Ai学LVGL之入门(DeepSeek版)
  • 通义万相 2.1 携手蓝耘云平台:开启影视广告创意新纪元
  • 关于浏览器中的屏幕录制案例及源码
  • ApoorvCTF Rust语言逆向实战
  • SDIO(Secure Digital Input Output)详解
  • 纯html文件实现目录和文档关联
  • Java基于SringBoot的果树的生长信息管理系统,附源码+文档说明
  • Android studio如何导入外部项目至成功运行(适合纯新手、小白、初学者的详细教程)导入安卓项目时规避报错,鹿溪IT工作室为您服务
  • sqrt与sqrtl函数
  • 中国专业室内设计公司排名/专业黑帽seo
  • 烟台网站建设便宜臻动传媒/2022新闻热点事件简短30条
  • 网站建设 网站运营/百度网址大全旧版
  • 怎么做网站的导航/免费引流推广怎么做
  • 免费自助建站工具/福州关键词搜索排名
  • 即商通网站建设推广/河源市企业网站seo价格