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

链表的概念和单向链表的实现

文章目录

  • 一:链表的概念及结构
  • 二:单向链表
    • (1)基础结构定义
    • (2)手动创建链表
    • (3)打印链表
    • (4)得到单链表的⻓度
    • (5)查找是否包含关键字key是否在单链表当中
    • (6)头插法
    • (7)尾插法
    • (8)下标不合法异常
    • (9)任意位置前面插⼊,第一个数据节点为0号下标
    • (10)删除第⼀次出现关键字为key的节点
    • (11)删除所有值为key的结点
    • (12)清空链表中所有元素:

一:链表的概念及结构

1.1链表是⼀种物理存储结构上⾮连续存储结构,数据元素的逻辑顺序是通过链表中的引⽤链接次序实现
的。
(1))物理非连续:链表的节点在内存中不一定连续,每个节点包含数据域(val)和引用域(next)(指向其他节点)。
(2)逻辑连续:通过引用域,节点之间形成链式关系,保证了数据在逻辑上的连续。
(3))节点来源:现实中,链表的节点一般从堆内存中申请,两次申请的空间可能连续也可能不连续。
1.2 链表的常见结构
链表的结构多样,通过以下三个维度组合,可形成 8 种不同的链表结构:
(1))单向 / 双向:单向链表节点只有一个引用域,指向后继节点;双向链表节点有两个引用域,分别指向前驱和后继节点。
(2))带头 / 不带头:带头链表有一个头节点(不存储实际数据),用于简化操作;不带头链表直接从存储数据的节点开始。
(3))循环 / 非循环:循环链表的尾节点引用指向头节点(或头节点相关节点),形成闭环;非循环链表的尾节点引用为 null。
示例:
(1)单向不带头非循环
在这里插入图片描述
(2)单向带头非循环
在这里插入图片描述(3)单向不带头循环
在这里插入图片描述
(4)单向带头循环
在这里插入图片描述
在实际应用中,我们重点掌握两种核心结构:

无头单向非循环链表:结构简单,一般不单独用于存储数据,常作为其他数据结构(如哈希桶、图的邻接表)的子结构,也是笔试面试中的高频考点。
无头双向非循环链表:Java 中 LinkedList 的底层实现就是这种结构,能兼顾插入、删除和查询操作的性能。

二:单向链表

                         我们自己实现一个⽆头单向⾮循环链表

(1)基础结构定义

public class MySingleList {// 内部静态类:链表节点static class ListNode{public int val;    // 数据域public ListNode next; // 引用域(下一个节点引用)// 初始化数据域public ListNode(int val) {this.val = val;}}ListNode head; // 链表的头引用,是访问链表的入口
}

(2)手动创建链表

public void createList(){ListNode node1 = new ListNode(12);ListNode node2 = new ListNode(23);ListNode node3 = new ListNode(34);ListNode node4 = new ListNode(45);ListNode node5 = new ListNode(56);node1.next = node2;node2.next = node3;node3.next = node4;node4.next = node5;this.head = node1;}

在这里插入图片描述

(3)打印链表

public void display() {//不要让head动ListNode cur = this.head;while (cur != null) {System.out.print(cur.val + " ");cur = cur.next;}System.out.println();
}

测试:

public class Test {public static void main(String[] args) {MySingleList mySingleList = new MySingleList();mySingleList.createList();mySingleList.display();}
}

在这里插入图片描述

在这里我们会思考如果我们把循环条件改为cur.next !=null会怎么样

根据上面的链表图,我们可以推出不会打印最后一个val;

我们来修改一下代码来看结果
在这里插入图片描述
确实少打印了56,说明我们的推断是正确的
所以我们可以总结出一个结论:
如果要停在最后一个节点,那么cur.next !=null,如果要遍历完所有节点,那么cur !=null

(4)得到单链表的⻓度

public int size(){int count = 0;ListNode cur = this.head;while(cur !=null){count++;cur = cur.next;}return count;
}

(5)查找是否包含关键字key是否在单链表当中

 public boolean contains(int key){ListNode cur = this.head;while(cur != null){if(cur.val == key){return true;}cur = cur.next;}return false;}

(6)头插法

1实例化一个节点对象node
2.修改指向

//时间复杂度为O(1)
public void addFirst(int data){ListNode node = new ListNode(data);//实例化一个节点对象nodenode.next = this.head;//修改指向this.head = node;//修改指向}

在这里插入图片描述
注意:修改指向这两步不能调换,因为调换之后相当于自己指向自己
结论:所有插入的时候先绑定后面

这时我们会有一个疑问,如果我们在一个空链表里使用头插法要不要特殊处理?

答案是不用!

因为你一开始节点的引用域本来就是null,你的head也是null,第一步修改指向没什么变化,第二步修改指向,head就存node的地址了,也就是正常插入了!

(7)尾插法

1看一下链表是不是空链表(一个节点都没有)head==null
2.找尾巴

//时间复杂度O(N)
public void addLast(int data){ListNode node = new ListNode(data);if(this.head == null){this.head = node;return;}//找尾巴ListNode cur = this.head;while(cur.next != null){cur = cur.next;}cur.next = node;
}

(8)下标不合法异常

public class illegalIndexException extends RuntimeException {public illegalIndexException() {}public illegalIndexException(String message) {super(message);}
}

(9)任意位置前面插⼊,第一个数据节点为0号下标

假如插入2位置,那就是插入2之前,所以要找到index位置的前一个节点

在这里插入图片描述

//任意位置前面插⼊,第一个数据节点为0号下标
public void addIndex(int index,int data){int len = size();if(index < 0 || index > len){throw new illegalIndexException("下标不合法");}//头插法if(index == 0){addFirst(data);return;}//尾插法if(index == len){addLast(data);return;}//中间位置ListNode cur = searchIndex(index);if(cur == null){return;}ListNode node = new ListNode(data);node.next = cur.next;cur.next = node;}/*** 找到index位置的前一个节点* @param index* @return*/private ListNode searchIndex(int index){int len = size();if(index < 0 || index >len){return null;}ListNode cur = this.head;int count = 0;while(count !=index-1){cur = cur.next;count++;}return cur;}

在这里插入图片描述

(10)删除第⼀次出现关键字为key的节点

/*** 查找关键字key的前一个节点,找到返回地址* 找不到返回null* @param key* @return*/
private ListNode findNode(int key){if(this.head == null){return null;}ListNode prev = this.head;while(prev.next != null){if(prev.next.val == key){return prev;}prev = prev.next;}return null;}
//删除第⼀次出现关键字为key的节点
public void remove(int key){if(this.head == null){return;}if(this.head.val == key){this.head = this.head.next;return;}//走到这里 第一个节点如果是要删除的节点 此时已经删除完毕ListNode prev = findNode(key);if(prev == null){return;}ListNode del = prev.next;prev.next = del.next;
}

(11)删除所有值为key的结点

public ListNode removeAllKey(int key) {if(this.head == null) {return null;}ListNode prev = this.head;ListNode cur = this.head.next;while(cur != null) {if(cur.val == key) {prev.next = cur.next;cur = cur.next;} else {prev = cur;cur = cur.next;}}//最后处理头if(this.head.val == key) {this.head = this.head.next;}return this.head;
}

(12)清空链表中所有元素:

public void clear() {while (this.head != null) {ListNode curNext = head.next;head.next = null;head.prev = null;head = curNext;}last = null;
}
http://www.dtcms.com/a/516166.html

相关文章:

  • 2013年下半年试题二:论企业应用系统的分层架构风格
  • U 盘写写保护解决方法
  • 简约手机网站源码兴宁电子商务网站建设
  • 教程网站搭建wordpress二次元风格
  • 02-Vue 插值
  • 【NebulaGraph】Nebula Importer使用
  • 不同形态组织镊在口腔临床的适配性选择
  • 深入理解进程、线程与协程
  • 用IIS自带FTP功能搭一个FTP!
  • 一种简单的Yolov8 onnx模型类别标签获取的方法
  • 用哪个网站做首页好做网站哪里最便宜
  • ROS1+Vscode
  • Ubuntu22.04 中搭建基于 Qemu 的内核(驱动)开发环境
  • JMETER+ANT接口自动化测试环境搭建实战讲解
  • 告别“大力金刚指”:晶尊微触摸芯片让电梯按键一触即灵
  • HTML教程
  • 基于Qt Quick的图像标注与标注数据管理工具
  • vscode搭建python项目隔离的虚拟环境
  • 模版网站有源代码吗wordpress栏目对应模板
  • 海阳市城建设局网站网页价格表
  • 网站建设客户分析调查表wordpress打不开页面
  • JAVA算法练习题day50
  • xss-labs pass-10
  • ArcMap批量修改字段的属性值
  • 龙虎榜——20251022
  • 03-RAG Agent-集成百炼知识库(Spring AI Alibaba)
  • 基于DEIM模型的声纳图像目标检测系统设计与实现
  • 如何让新网站快速收录上海城乡建设管理局网站
  • SpringMVC—请求映射路径 get请求与Post请求发送请求参数 5种类型参数传递 json数据传递参数 日期型参数的传递 响应
  • 下一代医疗机器人的标配:六维力传感器破解远程医疗核心瓶颈