链表数据结构图解 和 代码实现

链表数据结构图解 项目中经常会用到LinkedList集合来存储数据,打算写一篇LinkedList的源码解析,而LinkedList是基于链表结构存储数据的,这篇博文将解析链表数据结构,包括单向链表和双向链表; 1:单向链表: 单向链表的链表对象维护了一个 first 引用,该引用指向节点链表中的第一个节点对象,每个节点对象维护一个 next 引用,next引用指向下一个节点对象;(这里注意:是引用指向的是节点对... 阅读详情

项目中经常会用到LinkedList集合来存储数据,打算写一篇LinkedList的源码解析,而LinkedList是基于链表结构存储数据的,这篇博文将解析链表数据结构,包括单向链表和双向链表;

1:单向链表:

单向链表的链表对象维护了一个 first 引用,该引用指向节点链表中的第一个节点对象,每个节点对象维护一个 next 引用,next引用指向下一个节点对象;(这里注意:是引用指向的是节点对象:节点对象包含存储的数据和next引用)

以下是单向链表的图解:

java代码实现如下:

public class LinkedListDemo1 { //表示整个链表对象

    private Node first;        //链表对象的第一个引用
    
    public LinkedListDemo1(){
        
    }
    
    
    public Node getFirst() {
        return first;
    }


    public void setFirst(Node first) {
        this.first = first;
    }


    class Node{              //节点对象
         Item item;          //存储的数据对象
         Node next;          //下一个节点对象的引用
        public Item getItem() {
            return item;
        }
        public void setItem(Item item) {
            this.item = item;
        }
        public Node getNext() {
            return next;
        }
        public void setNext(Node next) {
            this.next = next;
        }
        
        
    }
}

当需要在首位置插入元素时,图解如下:first 引用指向需要插入到链表中的节点对象,新的节点对象的next引用指向原先的首节点对象;

java代码实现如下:

//插入对象到链表首位置
	public void insertFirst(Item item){
		//创建链表对象
		LinkedListDemo1 list=new LinkedListDemo1();
		//原来的首个节点暂存在:用oldFirst引用指向
		Node oldFirst=first;
		//创建需要插入的节点对象
		Node newNode=new Node();
		newNode.item=item;
		//新节点对象的next引用指向原先的首节点对象
		newNode.next=oldFirst;
		
	}

 当然这里的插入没有考虑首位置的节点对象为null的情况,插入到其他位置的节点实现原理和插入到首位置的基本差不多;

下面接收双向链表的实现原理:

链表对象中维护一个first 引用和 last引用,分别指向链表中的首末节点对象;每个节点对象维护 存储的数据对象引用,prev和next引用,用来指向前后节点对象;

双向链表的图解:

java代码实现链表对象如下:

public class LinkedListDemo2 {

	private Node first;
	private Node last;
	
	
	public LinkedListDemo2(){
		
	}
	
	
	public Node getFirst() {
		return first;
	}


	public void setFirst(Node first) {
		this.first = first;
	}
	
	


	public Node getLast() {
		return last;
	}


	public void setLast(Node last) {
		this.last = last;
	}




	class Node{
		 Item item;
		 Node prev;
		 Node next;
		public Item getItem() {
			return item;
		}
		public void setItem(Item item) {
			this.item = item;
		}
		
		public Node getPrev() {
			return prev;
		}
		public void setPrev(Node prev) {
			this.prev = prev;
		}
		public Node getNext() {
			return next;
		}
		public void setNext(Node next) {
			this.next = next;
		}

}
}

双向链表插入元素到首位:

图解:

java代码实现:

public void insertFirst(Item item){
		//暂存原先首节点对象
		Node oldFirst=first;
		//创建新的节点对象
		Node newNode=new Node();
		newNode.item=item;
		newNode.next=first;
		//first引用指向新节点对象
		first=newNode;
		//原先的节点对象的prev引用指向新节点对象
		oldFirst.prev=newNode;
	}

到此,单向链表结构和双向链表结构就解析完了,下面将解析 LinkedList 的源码:

LinkedList是基于双向链表数据结构来存储数据的,以下是对LinkedList  的 属性,构造器 ,add(E e),remove(index),get(Index),set(inde,e)进行源码分析:

属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
transient int size = 0;    //记录集合的大小
 
    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;  //指向首节点对象
 
    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;    //指向末节点对象

2构造器:

1
public LinkedList() {   //构造空的LinkedList对象<br>}
1
2
3
public LinkedList(Collection<? extends E> c) {   //构造对象,将集合元素添加到新集合中<br>       this();
       addAll(c);
   }

3:方法:add(E e)

1
2
3
4
public boolean add(E e) {
        linkLast(e);
        return true;
    }

linkedLast(e) 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
     * Links e as last element.
     */
    void linkLast(E e) {
        final Node<E> l = last;      //将原来的最末节点对象暂存 l 引用
        final Node<E> newNode = new Node<>(l, e, null);  /构建新的Node对象
        last = newNode;              //将链表对象的last引用指向新增的节点元素
        if (l == null)              
            first = newNode;         //如果不存在之前指向的节点,则first引用指向新创建的节点对象
        else
            l.next = newNode;        //存在前一个节点,之前最后节点对象的next指向新建的节点对象
        size++;                      //结合的长度加1
        modCount++;
    }

Node对象的构造器如下:

1
2
3
4
5
6
7
8
9
10
11
private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;
 
        Node(Node<E> prev, E element, Node<E> next) {  //参数为 l:之前的最后一个节点, element:需要新增的元素, next null
            this.item = element;   //要增加的元素
            this.next = next;      //新增节点的next指向为null
            this.prev = prev;      //新增节点的prev指向之前的节点
        }
    }

remove方法:

1
2
3
4
public E remove(int index) {    //删除指定索引的元素
       checkElementIndex(index);   //检查是否索引越界
       return unlink(node(index)); 
   }
1
node(index) 的源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Node<E> node(int index) {
        // assert isElementIndex(index);
 
        if (index < (size >> 1)) {   //获取到一般长度的集合索引值 
            Node<E> x = first;        //暂存链表中首节点对象
            for (int i = 0; i < index; i++)  //遍历前半段集合节点
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

这里有点繁琐,举个具体的实例说明:比如需要删除index=5;的节点对象,假设结合的长度为20

则调用 node(5) 方法后返回的是什么呢?假设Node(0) 为起始位置  

此时:初始:x=Node(0),当i=0   x=Node(1)    i=1   x=Node(2)…… 当i=5-1  x=Node(5)   此时就定位到了需要删除的节点对象 即 Node(index)

接下来调用:   unlink(node(index))  继续以index=5为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;     //Node(5).data
        final Node<E> next = x.next;  //next=Node(6)
        final Node<E> prev = x.prev;  //prev=Node(4)
 
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;       //Node(4).next=Node(6)
            x.prev = null;          //Node(5).prev=null
        }
 
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;       // Node(6).prev=Node(4)
            x.next = null;          //Node(5).next=null  回收
        }
 
        x.item = null;             //Node(5)=null
        size--;
        modCount++;
        return element;
    }

这样就完成了  Node(index-1).next=Node(index+1)   Node(index+1).prev=Node(index-1)   Node(index).data=null  Node(index).prev=null  Node(index).next=null  完成了删除动作  删除相应的索引的节点

删除第一个节点和删除最后一个节点的原理类似;

Get(int index) 方法:

1
2
3
public E get(int index) {
        checkElementIndex(index);   //检查索引是否越界
        return node(index).item;    //node(index) 在删除的方法中分析过,返回索引为index的节点对象, 所以get方法 返回的是该索引节点的存储数据对象
1
}

set(index,e) 方法:

1
2
3
4
5
public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);     //调用node(index)放回Node(index)
        E oldVal = x.item;          
        x.item = element;            //将 Node(index)的引用指向新的对象
1
return oldVal; }

 到此LinkedList的源码分析结束了:

mark:使用LinkedList 时,使用的是链表结构,当调用add()方法时,默认添加到最后一个,集合不需要扩充,减少内存消耗;

但是当LinkedList 进行指定索引的查询,元素替换,删除,需要对集合从first指向开始进行遍历一遍才能进行,有相应的计算复杂度;使用时应当考虑到这一点 


c语言实现链表的基本操作之插入(头插法,尾插法),删除,打印 文章目录1.头插法2.尾插法3.删除元素4.打印元素总结 链表是一种常见的基础数据结构,结构体指针,下面用c语言实现单链表插入,删除,打印等基本操作 1.头插法 头插法:从一个空表开始,重复读入数据,生成新节点,将读入的数据域存放到新结点的数据域中,然后将新结点插入到当前链表的表头结点之后,直至读入结束为止 头插法图解 头插法代码 //插入元素(头插法) int insertheadList... 阅读详情

相关推荐

在这里,我分享一篇关于链表的文章,带你深入了解链表的基本概念和原理,并通过源代码实现对链表的操作。

链表是数据结构中常见的一种,它采用了链式存储结构,不同于数组的顺序存储结构,链表内部节点不需连续分配内存,只要内存空间足够即可插入新节点。下面我们来看链表的实现过程。一个链表由头节点和若干个数据节点组成,头节点并不存储数据,仅仅作为链表的入口。链表操作最重要的就是遍历链表,我们可以通过遍历来获取链表中每个节点的数据。节点包含两个成员变量,一个是 data 用来存储节点数据,另一个则是指向下一个节点的指针。在这里,我分享一篇关于链表的文章,带你深入了解链表的基本概念和原理,并通过源代码实现对链表的操作。

TechCraze的博客 113

36张图带你深刻理解链表

欢迎来到算法小课堂,今天分享的内容是链表这种数据结构。在浅谈数组这篇文章中,我们说到数组在内存中是用一块连续的内存空间存储的。由于是用连续内存空间存储的,那么就会出现,明明还剩50M内存...

简一未 2119

Windows 24H2下eNSP AR_40启动失败的终极修复指南

本文针对Windows 11 24H2更新后eNSP AR_40路由器启动失败的问题,提供了终极修复方案。核心在于解决系统强制虚拟化安全(VBS/Hyper-V)与老版模拟软件的兼容性冲突。指南详细介绍了安装关键系统更新KB5053656、精细调整Windows虚拟化功能以及后续排错步骤,帮助用户在24H2系统上稳定运行eNSP进行网络实验。

weixin_29233333的博客 507

数据结构与算法:1.链表结构

1 Python链表 1.1基本概念 概念:链表是通过一个个节点组成的,每个节点都包含了称为cargo的基本单元,它也是一种**递归**的数据结构。 图示:能保持数据之间的逻辑顺序,但不必按照顺序存储。 和C不一样的是python没有专门的指针概念,在python中每个变量都是指针 链表的删除可以通过修改指针来实现 Python实现链表的方法: class Node: ''' data:节点保存的数据 _next:保存下一个节点对象 ''' def __init

绝世这天下的博客 3885

Plant Simulation 实战指南:从二次分配问题到优化布局设计

本文是一份Plant Simulation实战指南,详细讲解了如何利用该仿真软件解决工厂布局中的二次分配问题。通过从数据准备、模型搭建到集成遗传算法进行自动化优化的完整流程,帮助工程师在数字孪生环境中高效寻找最优设备布局方案,从而显著降低物流成本,提升生产效率。

i1j2k的博客 564

AutoDock Vina多配体对接 (Simultaneous Multiple Ligand Docking)

多配体分子对接(Simultaneous Multiple Ligand Docking, SMLD)或(Multiple Ligand Simultaneous Docking, MLSD)是一种分子对接技术,用于将多个配体(小分子药物候选物)同时对接到一个蛋白质分子的结合位点。与传统的分子对接技术不同,传统方法通常只考虑一个配体与一个蛋白质的相互作用。SMLD的目标是更高效地研究多个配体如何与同一个蛋白质相互作用,以寻找潜在的药物候选物。

MurphyStar的博客 7143

[数据结构] 链表(图文超详解讲解)

根据本文对链表的介绍后大家能对链表有更深的理解相信大家在过后时间能够保持求学的态度,我们一起加油!offer拿到手软!对博主感兴趣可以关注博主,我会继续更新博客,努力完善我的文章内容不会辜负大家的期待!

积跬步以至千里 3660

Keil AC6编译器独立安装与配置指南:不升级Keil MDK也能用上新工具链

本文详细介绍了如何在不升级Keil MDK的情况下独立安装和配置Arm Compiler 6(AC6),帮助嵌入式开发者充分利用AC6的编译速度、代码密度和现代语言支持优势。通过零侵入式解决方案,开发者可以在现有环境中灵活切换编译器版本,提升开发效率。

weixin_30872157的博客 552

怎样用c语言把不同的图片存进链表,C语言的那些小秘密之链表(四)

大多数的读者在学习编程语言的时候都不喜欢那些枯燥的文字描述,包括我自己在开始学习编程的时候也是这样,对于代码的热情远远高于文字,所以我在我写东西的时候也不喜欢用枯燥的文字描述来向读者讲解,更喜欢用代码加上适当的文字描述的方式进行讲解,因为有些东西可能用枯燥的文字描述半天还不如实实在在的给读者呈现出一段简单的代码,让读者理解得更加的透彻些。但是并不是说文字描述就没用,文字描述也很重要,只是绝大部分读...

weixin_29421565的博客 570

Happycapy深度解析:AI智能体的“iPhone时刻“来了?

从理论到实践深度解析了融合OpenClaw和ClaudeCode优点的超强智能体工具Happycapy

program哲学 457

机器人常用坐标系详解:原理、获取方法及实战实例

本文系统阐述了机器人四大核心坐标系(世界、基、工具、工件)的原理与标定方法。世界坐标系提供全局参考基准,基坐标系绑定机器人本体,工具坐标系定义末端执行器位置,工件坐标系则锁定作业对象位置。通过齐次变换矩阵实现各坐标系间的关联转换。重点介绍了三类标定技术:手动设定法(世界坐标系)、三点/六点法(工具TCP标定)和视觉识别法(工件标定),并以工业机械臂上下料为例演示了完整应用流程。随着技术发展,自动化标定、动态坐标系等新趋势将进一步提升机器人作业精度与效率。

start_up_go的博客 512

C++ class实现十字链表存储的图(完整代码)

代码如下: #include <iostream> #include <queue> using namespace std; typedef int InfoType; typedef int VertexType; const int MaxVertexNum = 30; class ArcNode { friend class OLGraph; friend class VertexNode; private: int tailvertex, headvertex;

m0_51955470的博客 412

无人机——电机篇(一)

1. 电机的定义 电机俗称“马达”,是无人机的动力来源,无人机通过改变电机的转速来改变无人机的飞行状态。即改变每个电机的速度,使得无人机能够盘旋空中,上升或下降,或向各个方向移动。2. 电机的分类 电机分为:“无刷电机”和“有刷电机” - 无刷电机:是采用半导体开关器件来实现电子换向,具有可靠性高、低干扰、低噪声低、寿命长等优点。一般应用在稍大型的飞行器,载重大,可以有更广泛的用途。 - 有刷电机:是内含电刷装置的将电能转换成机械能(电动机)或将机械能转换成电能(发电机)的旋转电机。有刷电机是所有电机的基

weixin_46066938的博客 1万+

Java数据结构 双向链表增删改查 图解和代码实现

目录 * 双向链表 * 节点结构: * 操作:(前后节点一定是双向奔赴的,箭头指向节点) 图解: 主函数 //创建节点 //创建链表 //添加节点到链表 // 节点类 // 节点信息 //链表类 * 显示 //链表是否为空 * 添加 * 通过编号修改节点信息 * 删除某节点 /** * 双向链表 * 节点结构: * node * 【pr...

cbdA_ZAI的博客 714

不会精准对比前人研究写“讨论与对比”段落?教你用ChatGPT辅助,直接让这部分提升一个档次(附提示词指令)

一个我觉得很有效的做法是,选取几篇具有代表性的文献,从研究方法、变量设置或结论方面进行对照分析,并指出本研究在哪些方面进行了补充或修正。除了简单说“提出了新的方法”或“首次应用某模型”,更需要说明这一做法在结果解读、研究设计或理论拓展上的具体意义,最好能够与现有研究进行比较,形成明确的差异化描述。段落应包含:研究结果解释、与前人研究对比、研究贡献、局限性反思,并在结尾提出未来研究的可能方向。讨论与对比部分一般位于文献综述、研究讨论或结果分析的后面,目的是对比已有研究,突出自己研究的价值与新意。

智能AI让学术科研更容易 170

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

数据结构:单链表原地逆序(图解原理和代码实现)

单链表原地逆序(图解原理和代码实现)

Zyzzzyz_的博客 859

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

【数据结构(一)】用C语言实现(双向、带头、循环)链表、栈、队列、递归、二叉树等基本数据结构

老师,我真的很想搞懂数据结构!😭

Dusong_的博客 3050

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

b - 数据结构实验之串三:kmp应用_图解Java数据结构和算法

点击上方蓝字,关注公众号 今天分享一套韩顺平图解Java数据结构和算法视频给大家。本教程是使用Java来讲解数据结构和算法,考虑到数据结构和算法较难,授课采用图解加算法游戏的方式。内容包括: 稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡...

weixin_39872123的博客 109

linked structures链表的思路和具体实现(C语言)

学习了有一段时间,本来想查找链表(linked structures)的具体实现,可是发现网络上大多的实现方法不太简单便捷,所以自己设计了一套代码如下, 并且我运用了我20年的画画技术,为大家手绘一个思路图方便理解,(PS:因为国外待太久,字丑不要介意哈) #include <stdio.h> #include <assert.h> #include <stdlib.h> /* 定义了一个struct 分为数据地址和一个指向下个struct的指针*/ typedef s

asdfxp098的博客 222

【Code Pratice】—— 《图解算法数据结构 ~ 第一章》

本文主要记录了学习《图解算法数据结构》一书中“数据结构”章节所做练习题的笔记,记录其中的思路以及碰到的问题等。因为学习的这本书是在`leetcode`上的,但是感觉`leetcode`上只要求做函数本身的实现,而无法做到对总体的把控,总感觉缺少了一部分,所以所有的题目中,为了追求编程的完整性(头文件、数据结构、函数定义、函数实现),练习的时候不局限于题目的要求,增加一点适应性,总的代码文件会放到文章末尾以供后续回看。

再努力一点点就好了 454

【考研408数据结构-03】 线性表详解(下):链表的七种形态

本文系统讲解了考研408数据结构中链表的七种形态及其核心实现。重点内容包括: 链表基本概念与七种形态对比(单链表、双链表、循环链表等) 链表与顺序表的性能差异分析 核心代码实现(头插/尾插、反转、快慢指针等) 链表操作的时间/空间复杂度分析 链表是考研高频考点,近5年有超过15道题涉及。掌握链表的实现原理和经典算法(如反转、环检测等)对解题至关重要。文中详细解析了头指针与头结点的区别、双链表插入顺序等易错点,并提供了清晰的代码示例和图解说明。 【核心要点】 链表通过指针实现非连续存储 七种链表各有特点,需掌

专注后端技术架构,AI技术分享 1367

c语言链表详解

初步发表c语言链表,图解部分还没有进行编辑,后面会发出来,主要内容包括链表的创建以及链表的以及链表的基本操作

weixin_50641310的博客 2759
上一篇: Linux下远程桌面连接windows
下一篇: C语言链表实现
csshuke
博客等级 码龄16年 1517粉丝 8原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值