【数据结构与算法】单链表(无头单向非循环)

文章目录

  • 1. 概念
  • 2. 链表分类
  • 3. 链表与顺序表对比
  • 4. 无头单向非循环链表实现(C语言)
    • 4.1 SingleLinkedList.h
    • 4.2 Test.c
    • 4.3 SingleLinkedList.c

1. 概念

  链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
在这里插入图片描述
链表在逻辑上是连续的,物理上则不一定连续(因为每个节点内存由操作系统分配),节点一般从堆内存申请,堆内存空间是按照一定策略分配的,两次申请的空间可能连续,也可能不连续。

2. 链表分类

以下不同情况下组合起来有8种链表结构:

  1. 单向或双向;
  2. 带头或不带头;
  3. 循环或非循环;

不过实际中最常用还是两种结构:

  1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等,另外这种结构在笔试面试中出现很多。
    在这里插入图片描述
  2. 带头双向循环链表::结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,不同功能的实现反而简单了。
    在这里插入图片描述

3. 链表与顺序表对比

不同角度顺序表链表
存储结构逻辑、物理连续逻辑连续,物理不一定连续
随机访问支持,效率高不支持,效率较低
插入或删除效率低效率高
容量容量不足时需要扩容不需要扩容
缓存利用率
应用场景高效存储和频繁访问频繁插入和删除

4. 无头单向非循环链表实现(C语言)

4.1 SingleLinkedList.h

#pragma once

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>

// 单链表结构(无哨兵位)
typedef int datatype;
typedef struct SingleLinkedListNode {
	datatype val;
	struct SingleLinkedListNode* next;
} Node, * SingleLinkedList;

// malloc返回新节点
Node* CreateNode(datatype val);

// 头插尾插
void AddFront(Node** pphead, datatype val);
void AddBack(Node** pphead, datatype val);

void Print(Node* phead);

// 头删尾删
void RemoveFront(Node** pphead);
void RemoveBack(Node** pphead);

// 查找是否存在
bool IsExist(Node** pphead, datatype target);
// 目标节点前面插入
void Insert(Node** pphead, datatype val, datatype target);
// 删除节点
void Erase(Node** pphead, datatype target);
// 删除全部
void Destroy(Node** pphead);

// 查找并返回节点
Node* Find(Node** pphead, datatype target);
// 目标节点后面插入
void InsertAfter(Node* targetNode, datatype val);
// 删除目标节点后面的节点
void EraseAfter(Node* targetNode);

4.2 Test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "SingleLinkedList.h"

void TestAddFront() {
	SingleLinkedList linkedList = NULL;
	AddFront(&linkedList, 1);
	AddFront(&linkedList, 2);
	AddFront(&linkedList, 3);
	AddFront(&linkedList, 4);
	printf("\nTestAddFront(): ");
	Print(linkedList);
}

void TestAddBack() {
	SingleLinkedList linkedList = NULL;
	AddBack(&linkedList, 1);
	AddBack(&linkedList, 2);
	AddBack(&linkedList, 3);
	AddBack(&linkedList, 4);
	printf("\nTestAddBack(): ");
	Print(linkedList);
}

void TestRemoveBack() {
	SingleLinkedList linkedList = NULL;
	AddBack(&linkedList, 1);
	AddBack(&linkedList, 2);
	AddBack(&linkedList, 3);
	AddBack(&linkedList, 4);
	RemoveBack(&linkedList);
	printf("\nTestRemoveBack(): ");
	Print(linkedList);
}

void TestTestFront() {
	SingleLinkedList linkedList = NULL;
	AddFront(&linkedList, 1);
	AddFront(&linkedList, 2);
	AddFront(&linkedList, 3);
	AddFront(&linkedList, 4);
	RemoveFront(&linkedList);
	printf("\nTestTestFront(): ");
	Print(linkedList);
}

void TestInsert() {
	SingleLinkedList linkedList = NULL;
	Insert(&linkedList, 10, 11); // 测试空链表时
	Insert(&linkedList, 20, 10); // 测试空链表或节点数<=1时
	Insert(&linkedList, 30, 11); // 测试目标节点不存在时
	Insert(&linkedList, 40, 10); // 测试正常情况
	Insert(&linkedList, 1, 20); // 测试目标节点是头结点时
	printf("\nTestInsert(): ");
	Print(linkedList); // 1->20->40->10->30->NULL
}

void TestErase() {
	SingleLinkedList linkedList = NULL;
	Insert(&linkedList, 10, 11); 
	Insert(&linkedList, 20, 10); 
	Insert(&linkedList, 30, 11); 
	Insert(&linkedList, 40, 10); 
	Insert(&linkedList, 1, 20); 
	Erase(&linkedList, 30);
	printf("\nTestDelete(): ");
	Print(linkedList); // 1->20->40->10->NULL
}

void TestDestroy() {
	SingleLinkedList linkedList = NULL;
	Insert(&linkedList, 10, 11);
	Insert(&linkedList, 20, 10);
	Insert(&linkedList, 30, 11);
	Insert(&linkedList, 40, 10);
	Insert(&linkedList, 1, 20);
	Destroy(&linkedList);
	printf("\nTestDestroy(): ");
	Print(linkedList);
}

void TestInsertAfter() {
	SingleLinkedList linkedList = NULL;
	Insert(&linkedList, 10, 11);
	Insert(&linkedList, 20, 10);
	Insert(&linkedList, 30, 11);
	Insert(&linkedList, 40, 10);
	Insert(&linkedList, 1, 20);
	InsertAfter(Find(&linkedList, 30), 50); // 1->20->40->10->30->50->NULL
	InsertAfter(Find(&linkedList, 50), 100);  // 1->20->40->10->30->50->100->NULL
	InsertAfter(Find(&linkedList, 1), 1000); // 1->1000->20->40->10->30->50->100->NULL
	printf("\nTestInsertAfter(): ");
	Print(linkedList);
}

void TestEraseAfter() {
	SingleLinkedList linkedList = NULL;
	Insert(&linkedList, 10, 11);
	Insert(&linkedList, 20, 10);
	Insert(&linkedList, 30, 11);
	Insert(&linkedList, 40, 10);
	Insert(&linkedList, 1, 20);
	InsertAfter(Find(&linkedList, 30), 50);
	InsertAfter(Find(&linkedList, 50), 100);
	InsertAfter(Find(&linkedList, 1), 1000); // 1->1000->20->40->10->30->50->100->NULL
	EraseAfter(Find(&linkedList, 1)); // 1->20->40->10->30->50->100->NULL
	EraseAfter(Find(&linkedList, 100)); // 1->20->40->10->30->50->100->NULL
	EraseAfter(Find(&linkedList, 10)); // 1->20->40->10->50->100->NULL
	printf("\nTestEraseAfter(): ");
	Print(linkedList);
}

int main() {
	TestAddFront();
	TestAddBack();
	TestRemoveBack();
	TestTestFront();
	TestInsert();
	TestErase();
	TestDestroy();
	TestInsertAfter();
	TestEraseAfter();
	return 0;
}

4.3 SingleLinkedList.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "SingleLinkedList.h"

Node* CreateNode(datatype val) {
	Node* node = (Node*)malloc(sizeof(Node));
	if (node == NULL) {
		perror("CreateNode() malloc error");
		exit(-1);
	}
	node->val = val;
	node->next = NULL;
	return node;
}

void AddFront(Node** pphead, datatype val) {
	Node* newNode = CreateNode(val);
	newNode->next = *pphead;
	*pphead = newNode;
}

void AddBack(Node** pphead, datatype val) {
	Node* newNode = CreateNode(val);
	if (*pphead == NULL) { // 空链表
		*pphead = newNode;
	}
	else { /* 节点数>=1 */
		Node* tail = *pphead;
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newNode;
	}
}

void Print(Node* phead) {
	Node* cur = phead;
	while (cur != NULL) {
		printf("%d->", cur->val);
		cur = cur->next;
	}
	printf("NULL\n");
}

void RemoveFront(Node** pphead) {
	assert(*pphead); // 空链表
	/* 链表节点数>=1 */
	Node* pNext = (*pphead)->next;
	free(*pphead);
	*pphead = pNext;
	//Node* tmp = *pphead;
	//*pphead = (*pphead)->next;
	//free(tmp);
	//tmp = NULL;
}

void RemoveBack(Node** pphead) {
	assert(*pphead); // 空链表
	if ((*pphead)->next == NULL) { /* 只有1个节点 */
		free(*pphead);
		*pphead = NULL;
	}
	else { /* 节点数>=2 */
		Node* prev = *pphead;
		while (prev->next->next) {
			prev = prev->next;
		}
		free(prev->next);
		prev->next = NULL;
	}
}

bool IsExist(Node** pphead, datatype target) {
	Node* cur = *pphead;
	while (cur) {
		if (cur->val == target) {
			return true;
		}
		cur = cur->next;
	}
	return false;
}

void Insert(Node** pphead, datatype val, datatype target) {
	// 当 (1)空链表 或 (2)节点数<=1 或 (3)目标节点是头节点时 则直接头插
	if (*pphead == NULL || (*pphead)->next == NULL || (*pphead)->val == target) {
		AddFront(pphead, val);
	}
	else { // 节点数>=2
		if (IsExist(pphead, target)) {
			Node* prev = *pphead;
			while (prev->next->val != target) {
				prev = prev->next;
			}
			Node* targetNode = prev->next;
			Node* newNode = CreateNode(val);
			prev->next = newNode;
			newNode->next = targetNode;
		}
		else { // 当目标节点不存在时尾插
			AddBack(pphead, val);
		}
	}
}

void Erase(Node** pphead, datatype target) {
	assert(*pphead); // 空链表
	Node* cur = *pphead;
	Node* pPrev = NULL; // 当节点数>=2必有pPrev != NULL
	while (cur) {
		if (cur->next != NULL && cur->next->val == target) {
			pPrev = cur;
		}
		if (cur->val == target) {
			Node* pNext = cur->next;
			free(cur);
			cur = NULL;
			if (pPrev != NULL) {
				pPrev->next = pNext;
			}
			else { // 说明删除的是头结点,pNext=NULL。
				*pphead = pNext;
			}
			break;
		}
		cur = cur->next;
	}
}

void Destroy(Node** pphead) {
	Node* cur = *pphead;
	Node* del = NULL;
	while (cur) {
		del = cur;
		cur = cur->next;
		free(del);
		del = NULL;
	}
	*pphead = NULL;
}

Node* Find(Node** pphead, datatype target) {
	Node* cur = *pphead;
	while (cur) {
		if (cur->val == target) {
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

void InsertAfter(Node* targetNode, datatype val) {
	assert(targetNode);
	Node* newNode = CreateNode(val);
	Node* pNext = targetNode->next;
	targetNode->next = newNode;
	newNode->next = pNext;
}

void EraseAfter(Node* targetNode) {
	assert(targetNode);
	Node* pDel = targetNode->next;
	if (pDel != NULL) { // 避免targetNode是尾结点时pDel=NULL的情况
		Node* pNext = pDel->next;
		free(pDel);
		pDel = NULL;
		targetNode->next = pNext;
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/316434.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux Kernel 4.14--EOF

2017 年&#xff0c;Linux 内核长期支持版本&#xff08;LTS&#xff09;的支持时间从原来的2年增加到6年。2023年下半年举行的开源欧洲峰会&#xff0c;LTS 的支持时间取消来了6年&#xff0c;再次缩短到了 2 年。 首个获得6年支持的版就是是 4.14。 在六年支持之后&#xf…

macbook安装配置maven3.6.1(包含将jdk更新至11版本)

参考博客&#xff1a; https://blog.csdn.net/qq2019010390/article/details/125472286 下载和安装 首先&#xff0c;在maven官网下载macOS系统所需的压缩包 官网的地址&#xff1a;https://maven.apache.org/download.cgi 因为要下载的版本是3.6.1&#xff0c;所以要在历史…

C++力扣题目98--验证二叉搜索树

给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例 1&#xff1a; 输入…

iOS xcode 15.1 打包报错

真机调试的时候没问题&#xff0c;打包的时候报错了 #报错 解决办法 pods.xcodeproj - build phases - compile sources - compiler flags pods.xcodeproj - Targets-support files pods-xx-frameworks

Python--装饰器

在 Python 中&#xff0c;装饰器是一种特殊类型的函数&#xff0c;它们用于修改或增强其他函数或方法的行为。装饰器本质上是一个函数&#xff0c;它接受一个函数作为参数&#xff0c;并返回一个新的函数。使用装饰器可以在不修改原函数代码的前提下&#xff0c;给函数添加新的…

计算机毕业设计-----SSH企业人力资源管理系统

项目介绍 企业人力资源管理系统&#xff0c;分为超级管理员与普通管理员两种角色,超级管理员可以对普通管理员进行添加、删除等操作&#xff1b; 超级管理员主要功能有&#xff1a; 部门管理、员工管理、招聘管理、培训管理、奖惩管理、薪资管理、用户信息修改、系统管理&…

【数据库基础】Mysql与Redis的区别

看到一篇不错的关于“Mysql与Redis的区别”的文章&#xff0c;转过来记录下~ 文章目录 一、数据库类型二、运行机制三、什么是缓存数据库呢&#xff1f;四、优缺点比较五、区别总结六、数据可以全部直接用Redis储存吗&#xff1f;参考资料 一、数据库类型 Redis&#xff1a;NOS…

【开源】基于JAVA+Vue+SpringBoot的桃花峪滑雪场租赁系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 游客服务2.2 雪场管理 三、数据库设计3.1 教练表3.2 教练聘请表3.3 押金规则表3.4 器材表3.5 滑雪场表3.7 售票表3.8 器材损坏表 四、系统展示五、核心代码5.1 查询教练5.2 教练聘请5.3 查询滑雪场5.4 滑雪场预定5.5 新…

基于JAVA的固始鹅块销售系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 鹅块类型模块2.3 固始鹅块模块2.4 鹅块订单模块2.5 评论管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 鹅块类型表3.2.2 鹅块表3.2.3 鹅块订单表3.2.4 鹅块评论表 四、系统展示五、核心代码5.…

计算机图形学作业:三维线段的图形变换

1. 将三维空间某线段 P1P2进行如下的操作&#xff0c;请按要求回答问题&#xff1a; &#xff08;1&#xff09; 沿 X 轴、Y 轴和 Z 轴分别平移 dx、dy 和 dz 的长度&#xff0c;给出相应的变换矩阵。 变换矩阵为&#xff1a; T100001000010dxdydz1 &#xff08;2&#xff09…

VScode全局搜索屏蔽、显示屏蔽指定文件类型及文件夹

1.键盘上按快捷键“ crtl 逗号 ”启动设置界面 crtl ,设置界面显示如下&#xff1a; 2.搜索屏蔽 2.1.输入 search.exclude search.exclude 设置界面显示如下&#xff1a; 2.2. 点击下图红色箭头“Add Pattern”&#xff0c;添加想要屏蔽的文件类型或文件夹 **/*.git *…

【Mybatis系列】Mybatis空值关联

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Multimodal Attention-based Fusion Networks for Diagnosis Prediction

C, N and U represent medical codes information, clinical notes information and demographics information of any patient,respectively. 作者未提供代码

LLaMA-Factory添加adalora

感谢https://github.com/tsingcoo/LLaMA-Efficient-Tuning/commit/f3a532f56b4aa7d4200f24d93fade4b2c9042736和https://github.com/huggingface/peft/issues/432的帮助。 在LLaMA-Factory中添加adalora 1. 修改src/llmtuner/hparams/finetuning_args.py代码 在FinetuningArg…

jQuery文字洗牌动效

html代码 效果展示 jQuery文本洗牌效果插件 <div class"container"><p class"lead">文本洗牌动画特效</p><h1 id"basic">A time to seek,</h1><h1 id"custom">and a time to lose;</h1> &…

Sectigo增强型多域名SSL证书买一年送一月

Sectigo EV增强型多域名SSL证书是一种高安全性的数字证书。相比于DV基础型的多域名SSL证书和OV企业型的多域名SSL证书&#xff0c;EV增强型多域名SSL证书功能更多、安全等级更高&#xff0c;但是相应的&#xff0c;这款SSL证书的审核也比较严格。今天就随SSL盾小编了解Sectigo旗…

MySQL夯实之路-事务详解

事务四大特性 事务需要通过严格的acid测试。Acid表示原子性&#xff0c;一致性&#xff0c;隔离性&#xff0c;持久性。 原子性&#xff08;atomicity&#xff09; 事务是不可分割的最小单元&#xff0c;对于整个事务的操作&#xff0c;要么全部提交成功&#xff0c;要么全部…

数据挖掘实战-基于机器学习的电商文本分类模型

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

获得利润和成长,应采取什么步骤, 澳福认为只需坚持一点

大多数交易者通常会考虑在外汇交易中获取利润&#xff0c;但只有少数人会思考这样一个问题:为了获得利润和专业成长&#xff0c;应该采取什么步骤。像“外汇交易怎么赢利”这样的文章很受市场欢迎&#xff0c;但是很少有人在交易中使用这些文章中给出的建议&#xff0c;因为在生…

安装rlwrap库出现问题

背景&#xff1a;oracle的sqlplus还是那么难用&#xff0c;不知道为什么不打包解决这个问题&#xff0c;留给用户&#xff0c;内核硬&#xff0c;就是猖狂。废话不多说。下载解压rlwrap-0.46.1.tar.gz;进入/tmp/database/rlwrap-0.46.1源码包&#xff0c;./configure checki…