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

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

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

0. 结点结构

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

1. 初始化

不带头结点的初始化,即只需将头指针初始化为NULL即可

void InitLink2(LinkList* L) {
	*L = NULL;
}

2. 头插法

对于不带头结点的单链表,头插法的核心思想是:
每次插入新节点时,将新节点的 next 指针指向当前链表的头节点,然后更新链表的头指针,使其指向新节点。
这样新节点就成为了链表的第一个节点,插入操作的时间复杂度为 O(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;
	*L = 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;  //因为新结点s要插入到链表尾部
	//若链表为空,新结点就是头结点
	if (*L == NULL)
	{
		*L = s;
	}
	else
	{
		//1.找到链表的尾结点
		LinkList p = *L;
		while (p->next != NULL) {
			p = p->next;
		}
		p->next = s;  //将新节点插入到尾结点后面
	}
	return 0; //插入成功
}

4. 插入

int insertLNode2(LinkList* L, int pos, ElemType value) {
	if (pos < 1) {
		printf("插入位置不合法!\n");
		return -1;
	}
	LinkList s = (LinkList)malloc(sizeof(LNode));
	if (s == NULL) {
		printf("内存分配失败!\n");
		return -2;
	}
	s->data = value;

	if (pos == 1) {
		s->next = *L;
		*L = s;
	}
	else {
		LinkList p = *L;
		int i = 1;
		while (p != NULL && i < pos - 1) {
			p = p->next;
			i++;
		}
		if (p == NULL) {
			printf("插入位置不合法!\n");
			free(s);
			return -1;
		}
		s->next = p->next;
		p->next = s;
	}
	return 0;
}

5. 删除

!不带头结点的单链表进行删除结点操作需要分情况考虑:

若要删除的是头节点,需要直接更新头指针;
若删除的是其他节点,需要找到该节点的前一个节点。

int deleteNode(LinkList* L, int pos) {
	if (pos < 1)
	{
		printf("删除位置无效!\n");
		return -1;
	}
	if (*L == NULL)
	{
		printf("当前链表为空!\n");
		return -2;
	}
	if (pos == 1)  //即删除头结点,(更新头结点)
	{
		LinkList temp = *L;
		*L = (*L)->next;
		free(temp);
	}
	else
	{
		LinkList p = *L;
		int i = 1;
		//找到第pos个结点
		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;
	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;
	int pos = 1;
	while (p != NULL) {
		if (p->data == value)
		{
			return pos;
		}
		p = p->next;
		pos++;
	}
	return -1;  //查找失败,未在该链表中找到该value值
}

8. 链表打印

void printLink2(LinkList L) {
	if (L == NULL) {
		printf("链表为空!\n");
		return;
	}
	LinkList s = L;
	while (s != NULL) {
		printf("%d ", s->data);
		s = s->next;
	}
	printf("\n--------------------------------\n");
}

9. 释放空间

void freeList2(LinkList L) {
	LinkList p = L;
	while (p != NULL) {
		LinkList temp = p;
		p = p->next;
		free(temp);
	}
}

10. 测试代码

int main() {
	//测试插入方法
	LinkList L1;
	InitLink2(&L1);
	insertLNode2(&L1, 1, 11);
	insertLNode2(&L1, 2, 22);
	insertLNode2(&L1, 3, 33);
	printLink2(L1);  // 11 22 33 
	freeList2(L1);

	// 测试头插法
	LinkList L2;
	InitLink2(&L2);
	headInsert(&L2, 1);
	headInsert(&L2, 2);
	headInsert(&L2, 3);
	printLink2(L2);  // 3 2 1
	freeList2(L2);

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

	// 测试删除
	deleteNode(&L3, 3);
	printf("删除第三个结点后:");
	printLink2(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

	freeList2(L3);

    return 0;
}

11. 完整代码

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

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

// 操作1——不带头结点的初始化,即只需将头指针初始化为NULL即可
void InitLink2(LinkList* L) {
	*L = NULL;
}

// 操作2——不带头结点的插入操作
int insertLNode2(LinkList* L, int pos, ElemType value) {
	if (pos < 1) {
		printf("插入位置不合法!\n");
		return -1;
	}
	LinkList s = (LinkList)malloc(sizeof(LNode));
	if (s == NULL) {
		printf("内存分配失败!\n");
		return -2;
	}
	s->data = value;

	if (pos == 1) {
		s->next = *L;
		*L = s;
	}
	else {
		LinkList p = *L;
		int i = 1;
		while (p != NULL && i < pos - 1) {
			p = p->next;
			i++;
		}
		if (p == NULL) {
			printf("插入位置不合法!\n");
			free(s);
			return -1;
		}
		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;
	*L = s;  //更新头结点指向新结点
	return 0;  //插入成功
}

//操作2.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;  //因为新结点s要插入到链表尾部
	//若链表为空,新结点就是头结点
	if (*L == NULL)
	{
		*L = s;
	}
	else
	{
		//1.找到链表的尾结点
		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;
	}
	if (*L == NULL)
	{
		printf("当前链表为空!\n");
		return -2;
	}
	if (pos == 1)  //即删除头结点,(更新头结点)
	{
		LinkList temp = *L;
		*L = (*L)->next;
		free(temp);
	}
	else
	{
		LinkList p = *L;
		int i = 1;
		//找到第pos个结点
		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;
	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;
	int pos = 1;
	while (p != NULL) {
		if (p->data == value)
		{
			return pos;
		}
		p = p->next;
		pos++;
	}
	return -1;  //查找失败,未在该链表中找到该value值
}

// 操作6——不带头结点的单链表打印操作
void printLink2(LinkList L) {
	if (L == NULL) {
		printf("链表为空!\n");
		return;
	}
	LinkList s = L;
	while (s != NULL) {
		printf("%d ", s->data);
		s = s->next;
	}
	printf("\n--------------------------------\n");
}

// 操作7——释放不带头结点链表内存
void freeList2(LinkList L) {
	LinkList p = L;
	while (p != NULL) {
		LinkList temp = p;
		p = p->next;
		free(temp);
	}
}

int main() {
	//测试插入方法
	LinkList L1;
	InitLink2(&L1);
	insertLNode2(&L1, 1, 11);
	insertLNode2(&L1, 2, 22);
	insertLNode2(&L1, 3, 33);
	printLink2(L1);  // 11 22 33 
	freeList2(L1);

	// 测试头插法
	LinkList L2;
	InitLink2(&L2);
	headInsert(&L2, 1);
	headInsert(&L2, 2);
	headInsert(&L2, 3);
	printLink2(L2);  // 3 2 1
	freeList2(L2);

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

	// 测试删除
	deleteNode(&L3, 3);
	printf("删除第三个结点后:");
	printLink2(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

	freeList2(L3);

    return 0;
}

12. 运行截图

相关文章:

  • fopen
  • 简单的二元语言模型bigram实现
  • Maxbotix超声波测距传感器:高灵敏度与抗干扰能力,确保复杂环境中的稳定性能
  • 一周学会Flask3 Python Web开发-SQLAlchemy简介及安装
  • 4.归一化技术:深度网络中的关键优化手段——大模型开发深度学习理论基础
  • R语言使用scitable包交互效应深度挖掘一个陌生数据库
  • 数字化转型的点、线、面、体
  • 基于磁数据的伤痕、生锈、断丝分类训练平台搭建规划
  • 单细胞的一组基因或富集分数的高低表达分组差异分析作图教程
  • 【GoTeams】-4:为项目引入etcd
  • Day03 反向代理配置 -V2024小迪全栈
  • Baklib驱动企业知识资产增值
  • MySQL有哪些高可用方案?
  • Bazel搭建CUDA工程入门
  • Spring Boot + MyBatis + MySQL:快速搭建CRUD应用
  • Vue 2 和 Vue 3 区别
  • 数据类型——long long
  • Jupyter Notebook 入门教程
  • P8:使用pytorch实现YOLOv5-C3模块
  • STM32之软件SPI
  • 株洲品牌网站建设/百度爱采购优化
  • php旅游网站模板下载/100%上热门文案
  • 自动推广工具/seo优化教学视频
  • 个人定制网站怎么做/汕头网站建设方案优化
  • 企业网站建设套餐/最近营销热点
  • 山东泰山队深圳队/临沂seo整站优化厂家