20分钟写一个链表

news2024/11/28 14:53:31

目录

  • 前言
  • 1.带头结点的循环双链表
    • 1.1 链表的分类、线性表的对比
    • 1.2 双链表基本操作代码实现
      • 1.2.1 初始化
      • 1.2.2 销毁、打印链表
  • 总结

前言

有一个学长在面试的时候被问到这样一个问题,“你可以用20分钟写一个链表吗?”学长第一反应是,至少要一两个小时吧,链表的基本操作实现,增删查改等。
但是带头结点的循环双链表真的可以在20分钟写出来,这个谜底我们文末揭晓。


1.带头结点的循环双链表

在这里插入图片描述
带头结点的循环双链表的结构如上图所示,双链表每个结点有两个指针,prev和next,prev指向前驱结点,next指向后继结点。

哨兵位的头结点不存储有效数据,OJ题中提到的头结点一般都是第一个结点。没有明确说明,一般认为单链表不带哨兵位的头结点。而且即使尾插要讨论链表是否为空的情况,一般也不用哨兵位的头结点,除非题目需要多个单链表进行尾插,每次都讨论过于麻烦,考虑使用哨兵位的头结点,这篇文章的第二部分是一个例子,感兴趣可以看一下。链表OJ经典题目及思路总结(二)头结点

1.1 链表的分类、线性表的对比

带哨兵位的头结点,尾插以及其他操作不需要考虑链表是否为空;
双链表,两个指针,可以很方便找到前驱结点和后继结点。
循环的双链表,在有头指针的情况下可以方便访问头、尾结点。

链表一共有八种类型,如下图所示
在这里插入图片描述

顺序表、单链表、双链表的简单比较如下
在这里插入图片描述

1.2 双链表基本操作代码实现

1.2.1 初始化

  1. 带头结点的循环双链表,初始化只有头结点,prev和next指针都指向自己。这也表明,只要头指针的next域与头指针相等,即可判定链表为空
  2. 与单链表相比,单链表是没有必要单独写一个初始化函数的;顺序表要初始化size等内容,也需要初始化函数。
  3. 带头结点的循环双链表很多操作,插入、删除都不需要更改头指针,因为有头结点的存在,链表不可能为空。所以插入、删除函数形参都是一级指针,为了保持接口的一致性,初始化函数也用一级指针,在函数内部动态申请空间、初始化,返回其地址即可。

代码实现如下方所示

//初始化
LTNode* LTInit()
{
	LTNode* phead = BuyListNode(-1);
	phead->prev = phead;
	phead->next = phead;
	return phead;
}

1.2.2 销毁、打印链表

销毁的思路是遍历并释放链表的结点,打印的思路是遍历并打印链表的结点。二者有一个共同点,就是遍历。值得一提的是,遍历的时候用临时变量cur(命名的可读性)来遍历,尽量不要动头指针,因为回头可能还有用。

List.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int LTDataType;
typedef struct ListNode
{
	struct ListNode* prev;
	struct ListNode* next;
	LTDataType data;
}LTNode;

LTNode* LTInit();
void LTDestroy(LTNode* phead);

bool LTEmpty(LTNode* phead);
LTNode* BuyListNode(LTDataType x);
void LTPrint(LTNode* phead);

void LTPushBack(LTNode* phead,LTDataType x);
void LTPopBack(LTNode* phead);

void LTPushFront(LTNode* phead, LTDataType x);
void LTPopFront(LTNode* phead);

void LTInsert(LTNode* phead, LTDataType x);
LTNode* LTFind(LTNode* phead, LTDataType x);
void LTErase(LTNode* phead);

基本操作代码实现
Test.c

#include "List.h"
LTNode* BuyListNode(LTDataType x)
{
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if(node==NULL)
	{
		perror("malloc failed");
		return NULL;//exit(-1);
	}
	node->data = x;
	node->prev = NULL;
	node->next = NULL;
	return node;
}
//初始化
LTNode* LTInit()
{
	LTNode* phead = BuyListNode(-1);
	phead->prev = phead;
	phead->next = phead;
	return phead;
}
void LTDestroy(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}
void LTPrint(LTNode* phead)
{
	assert(phead);
	printf("<=>head<=>");
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d<=>", cur->data);
		cur = cur->next;
	}
	printf("\n");
}
bool LTEmpty(LTNode* phead)
{
	assert(phead);
	/*if (phead->next == phead)
		return true;
	else
		return false;*/

	return phead->next == phead;
}
void LTPushBack(LTNode* phead, LTDataType x)
{
	LTInsert(phead,x);
	/*assert(phead);
	LTNode* newnode = BuyListNode(x);
	LTNode* tail = phead->prev;
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;	
	phead->prev = newnode;*/
}
void LTPopBack(LTNode* phead)
{
	LTErase(phead->prev);
	/*assert(phead);
	assert(!LTEmpty(phead));
	LTNode* tail = phead->prev;
	LTNode* tailPrev = tail->prev;
	tailPrev->next = phead;
	phead->prev = tailPrev;
	free(tail);
	tail = NULL;*/
}
void LTPushFront(LTNode* phead, LTDataType x)
{
	LTInsert(phead->next,x);
	/*assert(phead);
	LTNode* first = phead->next;
	LTNode* newnode = BuyListNode(x);
	
	//改链表指向的时候,可以先存储前驱或后继结点的位置,这样不需要过多考虑改指向的顺序问题
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = first;
	first->prev = newnode;*/

	//改结点间指向,也可以直接改,但要注意顺序,因为如果改的过程中,链表断开,找不到原来的前驱或后继结点就麻烦了
	//注意顺序
	/*newnode->next = phead->next;
	phead->next->prev = newnode;

	phead->next = newnode;
	newnode->prev = phead;*/
}
void LTPopFront(LTNode* phead)
{
	LTErase(phead->next);
	/*assert(phead);
	assert(!LTEmpty(phead));
	LTNode* first = phead->next;
	LTNode* firstNext = first->next;
	phead->next = firstNext;
	firstNext->prev = phead;
	free(first);
	first = NULL;*/
}
void LTInsert(LTNode* pos, LTDataType x) 
{
	assert(pos);
	LTNode* newnode = BuyListNode(x);
	LTNode* prev = pos->prev;
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}
void LTErase(LTNode* pos)
{
	assert(pos);
	LTNode* p = pos->prev;
	LTNode* n = pos->next;
	p->next = n;
	n->prev = p;
	free(pos);
}
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}

总结

在实现插入、删除函数后,头插、尾插、头删、尾删直接复用即可,这种复用使得20分钟写一个链表成为可能。而且插入函数不需要像单链表插入那样遍历找尾结点,直接更改链表结点间的指向即可,如下图所示。
在这里插入图片描述
删除函数一般搭配查找函数,查找特定值结点的位置并删除。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

传统图像处理Opencv分割不同颜色的夹子

任务要求&#x1f349; 1. 计算图像中夹子的总数。 2. 分别计算不同颜色夹子的个数。 3. 使用以下方法适应三张图片&#xff0c;并在每张图像上显示结果&#xff1a; - 阈值方法 - HSV颜色空间 - 连通域分析 - 形态学图像处理 - Canny边缘检测 4. 在结果中显示计…

北交大研究突破:塑料光纤赋能低成本无摄像头AR/VR眼动追踪技术

北交大研究&#xff1a;探索无摄像头低成本AR/VR眼动追踪新路径 在AR/VR技术领域&#xff0c;眼动追踪作为一项关键技术&#xff0c;对于提升用户体验、优化渲染效率具有重要意义。然而&#xff0c;传统的眼动追踪方案多依赖于高成本的摄像头&#xff0c;这不仅增加了设备的制造…

学习资料库系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;观看记录管理&#xff0c;基础数据管理&#xff0c;论坛信息管理&#xff0c;公告信息管理&#xff0c;轮播图信息 微信端账号功能包括&#xff1a;系统首页&#xff0c;阅读资…

性能学习5:性能测试的流程

一.需求分析 二.性能测试计划 1&#xff09;测什么&#xff1f; - 项目背景 - 测试目的 - 测试范围 - ... 2&#xff09;谁来测试 - 时间进度与分工 - 交付清单 - ... 3&#xff09;怎么测 - 测试策略 - ... 三.性能测试用例 四.性能测试执行 五.性能分析和调优 六…

【cpp/c++ summary 工具】 conan包管理器安装与cmake项目配置

发现有些包cvpkg中没有&#xff08;比如字典树&#xff09;&#xff0c;conan里有。 安装conan https://conan.io/downloads 配置conan PS C:\Users\multisim> conan config home C:\Users\multisim\.conan2 # Conan 主文件夹的路径,https://docs.conan.io/2/referen…

【梯级水电站调度优化】基于自适应权值优化粒子群算法

课题名称&#xff1a; 基于改进粒子群算法的梯级水电站调度优化 改进方向&#xff1a;自适应权值优化 代码获取方式&#xff08;付费&#xff09;&#xff1a; 相关资料&#xff1a; 1. 粒子群算法的基本原理 2. 梯级水电站调度优化模型 3. 代码注释 4. 代码讲解视频&am…

Python编程常用的35个经典案例

Python 的简洁和强大使其成为许多开发者的首选语言。本文将介绍35个常用的Python经典代码案例。这些示例覆盖了基础语法、常见任务、以及一些高级功能。 1.列表推导式 这个例子展示了列表推导式&#xff0c;用于生成FizzBuzz序列。 fizz_buzz_list ["FizzBuzz" i…

互联网Java工程师面试题及答案整理(2024年最新版)

前言 作为一个 Java 程序员&#xff0c;你平时总是陷在业务开发里&#xff0c;每天噼里啪啦忙敲着代码&#xff0c;上到系统开发&#xff0c;下到 Bug 修改&#xff0c;你感觉自己无所不能。然而偶尔的一次聚会&#xff0c;你听说和自己一起出道的同学早已经年薪 50 万&#x…

拥抱大模型时代,从入门到精通,科技从业者的转型攻略与AI红利指南

前言 自2022年11月ChatGPT问世以来&#xff0c;大模型在人工智能领域的潜能得到了充分展示&#xff0c;其深远影响引发了研究热潮。在不到一年的时间里&#xff0c;国产大模型如雨后春笋般涌现&#xff0c;2023年成为了大模型时代的元年。对于IT圈的科技从业者来说&#xff0c…

NextViT实战:使用NextViT实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整策略设置混合精度&#xff0c;DP多卡&#xff0c;EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试完整的代码 在上…

从零开始讲PCIe(8)——PCIe拓扑与组件

一、拓扑结构 和PCI-X一样&#xff0c;PCIe链路是点对点的连接&#xff0c;而不是像传统PCI那样使用共享总线。这是由于其使用了非常高的传输速度。因此&#xff0c;链路只能连接两个接口&#xff0c;为了构建一个复杂的系统&#xff0c;必须采用某种方式来扩展连接。在PCIe中&…

《Linux从小白到高手》理论篇:一文概览常用Linux重要配置文件

List item 今天继续宅家&#xff0c;闲来无事接着写。本篇是《Linux从小白到高手》理论篇的最后一篇了。本篇集中介绍所有常用的Linux重要配置文件。 用这个命令可以查看配置文件所在的位置&#xff1a;如上图 locate "*.conf" "*.ini" "*.cfg&quo…

论文 | Model-tuning Via Prompts Makes NLP Models Adversarially Robust

这篇论文研究了使用提示 (Prompting) 方法微调预训练语言模型&#xff0c;以提高其在对抗样本攻击下的鲁棒性。论文的主要贡献如下&#xff1a; 1.MVP 比 MLP-FT 更鲁棒&#xff1a; 论文比较了 MVP (Model-tuning Via Prompts) 和传统的 MLP-FT (Fine-tuning with an MLP head…

热门:AI变现,看看谁在默默赚大钱?

在这个愈发依赖AI的时代&#xff0c;找到属于自己的盈利方式愈发重要。 更多实操教程和AI绘画工具,可以扫描下方,免费获取 总的来说&#xff0c;利用AI进行盈利的方式主要有三种&#xff1a;技术型、流量型和内容型。 每种方式都根植于AI的特性&#xff0c;但同时也需要特定…

【英语】2. 英语的表达习惯

文章目录 前言less v. more n.解释e.g. less v. more prep.被动与中文的歧义总结参考文献 前言 进行英语前后缀的复习 less v. more n. 解释 外国的表达方式&#xff1a;更多地偏向静态&#xff0c;因此更多地使用名词 e.g. (rather Chinglish expression) She could not c…

Linux Cent7 已安装MySQL5.7.X,再安装MYSQL8.4.2

一、 下载安装 检查Linux系统的glibc版本rpm -qa | grep glibc结果&#xff1a;glibc-common-2.17-260.el7_6.6.x86_64 glibc-2.17-260.el7_6.6.x86_64 glibc-headers-2.17-260.el7_6.6.x86_64 glibc-devel-2.17-260.el7_6.6.x86_64访问MySQL官网&#xff0c;下载对应版本数据…

【智能算法应用】侏儒猫鼬优化算法求解二维路径规划问题

摘要 在复杂环境下的移动机器人路径规划问题中&#xff0c;最优路径的求解具有重要的应用价值。本文基于一种新型智能优化算法——侏儒猫鼬优化算法&#xff08;DMOA&#xff09;&#xff0c;提出了一种二维路径规划的有效求解方法。该算法通过模拟侏儒猫鼬的觅食、社会合作与…

一行 Python 代码能实现什么丧心病狂的功能?圣诞树源代码

手头有 109 张头部 CT 的断层扫描图片&#xff0c;我打算用这些图片尝试头部的三维重建。基础工作之一&#xff0c;就是要把这些图片数据读出来&#xff0c;组织成一个三维的数据结构&#xff08;实际上是四维的&#xff0c;因为每个像素有 RGBA 四个通道&#xff09;。 这个…

Github优质项目推荐-第四期

文章目录 Github优质项目推荐 - 第四期一、【Umi-OCR】&#xff0c;26.1k stars - 文字识别工具二、【AFFiNE】&#xff0c;41k stars - 知识库平台三、【NocoBase】&#xff0c;12k stars - 无代码/低代码平台四、【neovim】&#xff0c;82.3k stars - 改良版VIM五、【generat…

LLM大模型:开源RAG框架汇总

前言 本文搜集了一些开源的基于LLM的RAG&#xff08;Retrieval-Augmented Generation&#xff09;框架&#xff0c;旨在吸纳业界最新的RAG应用方法与思路。如有错误或者意见可以提出&#xff0c;同时也欢迎大家把自己常用而这里未列出的框架贡献出来&#xff0c;感谢~ RAG应用…