链表---数据结构-黑马

链表

定义

链表是数据元素的线性集合,其每个元素都指向下一个元素,元素存储上是不连续的

分类

  • 单向链表,每个元素只知道自己的下一个元素是谁。
    在这里插入图片描述
  • 双向链表,每个元素知道自己的上一个元素和下一个元素。
    在这里插入图片描述
  • 循环链表,通常链表尾部节点 tail 指向的为null,而循环链表的tail指向链表的头部节点 head。
    在这里插入图片描述

链表中还有一种特殊的节点称之为,哨兵(Sentinel)节点,也称之为 哑元(Dummy)节点, 不存储数据,一般作为头尾,简化边界判断,如下如所示。
在这里插入图片描述

性能

随机访问

根据 i n d e x index index 查询,时间复杂度为 O ( n ) O(n) O(n)

插入或删除

  • 起始位置,时间复杂度为, O ( 1 ) O(1) O(1)
  • 结束位置,若已知尾部节点 tail ,时间复杂度为, O ( 1 ) O(1) O(1) ;否则为 O ( n ) O(n) O(n)
  • 中间位置,根据 index 查找的时间复杂度 + O ( 1 ) O(1) O(1)

单项链表实现

方法实现

节点类、头部添加、循环遍历、尾部添加

// 单项链表
public class SinglyLinkedList implements Iterable<Integer>() {private Node head = null;  // 头指针// 节点类private static class Node() {int value;  // 值Node node;  // 下一个节点public Node(int value, Node node) {this.value = value;this.node = node;}}@Overridepublic Iterator<Integer> iterator() {// 匿名内部类 -> 带名字的内部类return new NodeIterator();}// 1.链表头部添加元素public void addFirst(int value) {// 1.当链表为空时// head = new Node(value, null);  因为 head = new Node(value, head);这行代码可以处理链表为空的情况,则注释// 2.当链表非空时head = new Node(value, head);}// 寻找最后一个节点private Node findLast() {// 链表为空if (head == null) {return null;}Node p;for(p = head; p.next != null; p = p.next) {}return p;}// 3.尾部添加元素private void addLast(int value) {Node last = findLast();if (last == null) {// 头部添加addFirst(value);return;}last.next = new Node(value, null);}// 4.根据索引值查找结点public Node findNode(int index) {int i = 0;for(Node p = head; p != null; p = p.next, i++) {if (index == i) {return p;}}// 未找到return null;}// 5.根据index找到对应节点,并返回节点值public int get(int index) {Node p = findNode(index);if (p == null) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index))}return p.value;}// 6.向索引位置插入元素public void insert(int index, int value) {if (index == 0) {  // 索引为0,向链表头部添加addFirst(value);return;}// 1.找到插入索引位置的上一个节点Node p = findNode(index - 1);// 判断p节点是否为空if (p == null) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}Node n = p.next;// p存在的情况p.next = new Node(value, n);}// 7.删除头部节点public void removeFirst() {if (head == null) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}// 头节点的下一个节点赋给头head = head.next;}// 8.按索引位置删除节点public void remove (int index) {if (index == 0) {removeFirst();return;}Node p = findNode(index - 1);  // 找到待删除节点的上一个节点if (p == null) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}Node n = p.next;               // 待删除节点if (n == null) {// 未找到待删除节点threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}p.next = n.next;			   // 待删除节点的上一个节点p的next指向待删除结点的下一个节点}// 6.遍历链表,方法1public void loop1(Consumer<Integer> consumer) {Node p = head; // 头节点赋给pwhile (p != null) {consumer.accept(p.value);p = p.next;}}// 循环遍历,方法2public void loop2(Consumer<Integer> consumer) {for(Node p = head; p != null; p = p.next) {consumer.accept(p.value);}}// 遍历,方法3,使用迭代器,实现接口Iterablepublic class NodeIterator implements Iterator<Integer> {Node p = head;@Overridepublic boolean hasNext() {  //判断是否有下一个元素return p != null;}@Overridepublic Integer next() {     //返回当前节点值int value = p.value;p = p.next;return value;      }}
}

测试

public class TestSinglyLinkedList() {// 尾部添加, 根据索引查找对应节点值测试@Testpublic void test3() {SinglyLinkedList list = new SinglyLinkedList();list.addLast(1);list.addLast(2);list.addLast(3);list.addLast(4);// Assertions.assertIterableEquals(List.of(1, 2, 3, 4), list);int i = list.get(2);System.out.println(i);  //i = 3}@Testpublic void test4() {SinglyLinkedList list = new SinglyLinkedList();list.addLast(1);list.addLast(2);list.addLast(3);list.addLast(4);list.insert(2, 5); 向索引为2的位置插入元素5for(Integer value : list) {System.out.print(value);}}
}

带哨兵的单向链表

// 单项链表
public class SinglyLinkedList implements Iterable<Integer>() {private Node head = new Node(666, null);  // 头指针// 节点类private static class Node() {int value;  // 值Node node;  // 下一个节点public Node(int value, Node node) {this.value = value;this.node = node;}}@Overridepublic Iterator<Integer> iterator() {// 匿名内部类 -> 带名字的内部类return new NodeIterator();}// 1.链表头部添加元素public void addFirst(int value) {insert(0, value);}// 寻找最后一个节点private Node findLast() {Node p;for(p = head; p.next != null; p = p.next) {}return p;}// 3.尾部添加元素private void addLast(int value) {Node last = findLast();last.next = new Node(value, null);}// 4.根据索引值查找结点public Node findNode(int index) {int i = -1;for(Node p = head; p != null; p = p.next, i++) {if (index == i) {return p;}}// 未找到return null;}// 5.根据index找到对应节点,并返回节点值public int get(int index) {Node p = findNode(index);if (p == null) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}return p.value;}// 6.向索引位置插入元素public void insert(int index, int value) {// 1.找到插入索引位置的上一个节点Node p = findNode(index - 1);// 判断p节点是否为空if (p == null) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}Node n = p.next;// p存在的情况p.next = new Node(value, n);}// 7.删除头部节点public void removeFirst() {remove(0);}// 8.按索引位置删除节点public void remove (int index) {Node p = findNode(index - 1);  // 找到待删除节点的上一个节点if (p == null) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}Node n = p.next;               // 待删除节点if (n == null) {// 未找到待删除节点threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}p.next = n.next;			   // 待删除节点的上一个节点p的next指向待删除结点的下一个节点}// 6.遍历链表,方法1public void loop1(Consumer<Integer> consumer) {Node p = head.next; // 头节点赋给pwhile (p != null) {consumer.accept(p.value);p = p.next;}}// 循环遍历,方法2public void loop2(Consumer<Integer> consumer) {for(Node p = head.next; p != null; p = p.next) {consumer.accept(p.value);}}// 遍历,方法3,使用迭代器,实现接口Iterablepublic class NodeIterator implements Iterator<Integer> {Node p = head.next;@Overridepublic boolean hasNext() {  //判断是否有下一个元素return p != null;}@Overridepublic Integer next() {     //返回当前节点值int value = p.value;p = p.next;return value;      }}
}

双向链表–哨兵

public class DoublyLinkedListSentinel implements Iterator<Integer> {static class Node {Node prev;		// 上一节点int value;		// 值Node next;		// 下一节点public Node (Node prev, int value, Node next) {this.prev = prev;this.value = value;this.next = next;}}private Node head; //头哨兵节点private Node tail; //尾哨兵节点public DoublyLinkedListSentinel () {head = new Node(null, 666, null);tail = new Node(null, 888, null);head.next = tail;tail.prev = head;}// 根据索引位置找节点private Node findIndex(int index) {int i = -1;for(Node p = head; p != tail; p = p.next, i++) {if (index == i) {return p;}    }return null;}// 头部插入元素public void addFirst(int value) {insert(0, value);} // 1.对应索引位置插入元素public void insert(int index, int value) {Node prev = findIndex(index - 1);		// 插入索引位置前一个结点if (prev == null) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}Node next = prev.next;					// 成为待插入元素节点的下一个节点Node node = new Node(prev, value, next); prev.next = node;next.prev = node;}// 尾部添加元素public void addLast(int value) {Node last = tail.prev;Node added = new Node(last, value, tail);last.next = added;tail.prev = added; }// 删除最后一个节点public void removeLast() {Node removed = tail.prev;if (removed == head) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}Node prev = removed.prev;prev.next = tail;tail.prev = prev;}// 删除头节点public void removeFirst() {remove(0);}// 删除节点public void remove(int index) {Node prev = findIndex(index - 1);		// 删除索引位置前一个结点if (prev == null) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}Node next = prev.next.next;					// 待删除元素节点的下一个节点if (next == null) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}prev.next = next;next.prev = prev;}// 迭代器遍历@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {Node p = head.next;@Overridepublic boolean hasNext() {return p != tail;}@Override public Integer next() {int value = p.value;p = p.next;return value;}}}}

环形链表

双向环形链表带哨兵,哨兵既作为头哨兵,又作为尾哨兵
在这里插入图片描述

public class DoublyLinkedListSentinel {private static class Node {Node prev;		// 上一节点int value;		// 值Node next;		// 下一节点public Node (Node prev, int value, Node next) {this.prev = prev;this.value = value;this.next = next;}}private Node sentinel = new Node(null, -1, null);public DoublyLinkedListSentinel () {sentinel.prev = sentinel;sentinel.next = sentinel;}// 根据索引位置找节点private Node findIndex(int index) {int i = -1;for(Node p = sentinel; p != sentinel; p = p.next, i++) {if (index == i) {return p;}    }return null;}// 头部插入元素public void addFirst(int value) {Node a = sentinel;Node b = sentinel.next;Node added = new Node(a, value, b);a.next = added;b.prev = added;} // 1.对应索引位置插入元素public void insert(int index, int value) {Node prev = findIndex(index - 1);		// 插入索引位置前一个结点if (prev == null) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}Node next = prev.next;					// 成为待插入元素节点的下一个节点Node node = new Node(prev, value, next); prev.next = node;next.prev = node;}// 尾部添加元素public void addLast(int value) {Node b = sentinel;Node a = sentinel.prev;Node added = new Node(a, value, b);b.prev = added;a.next = added;}// 删除头节点public void removeFirst() {Node removed = sentinel.next;if (removed == sentinel) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}Node a = sentinel;Node b = removed.next;a.next = b;b.prev = a;}// 删除最后一个节点public void removeLast() {Node removed = sentinel.prev;if (removed == sentinel) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}Node a = sentinel;Node b = removed.prev;a.prev = b;b.next = a;}// 根据节点值查找该节点public Node findByValue(int value) {Node p = sentinel.next;whlie (p != sentinel) {if (p.value == value) {return p;}p = p.next;}return null;}// 根据节点值删除节点public void remove(int vlaue) {Node removed = findByValue(value);if (removed == null) {// 节点为空,抛出异常threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));}Node a = removed.prev;Node b = removed.next;a.next = b;b.prev = a;}// 迭代器遍历@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {Node p = sentinel.next;@Overridepublic boolean hasNext() {return p != sentinel;}@Override public Integer next() {int value = p.value;p = p.next;return value;}}}
}

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

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

相关文章

【深度学习】单层神经网络

单层神经网络 神经元感知机 1943年&#xff0c;心理学家McCulloch和数学家Pitts共同发表了神经网络的开山之作A Logical Calculus of the Ideas Immanent in Nervours Activity1&#xff0c;提出了神经网络的第一个数学模型——MP模型。该模型也成为了人工神经网络的基础。 神经…

代码随想录算法训练营第十四天| 226.翻转二叉树 101. 对称二叉树 104.二叉树的最大深度 111.二叉树的最小深度

目录 一、LeetCode 226.翻转二叉树思路&#xff1a;C代码 二、LeetCode 101. 对称二叉树思路C代码 二、LeetCode 104.二叉树的最大深度思路C代码 二、LeetCode 111.二叉树的最小深度思路C代码 总结 一、LeetCode 226.翻转二叉树 题目链接&#xff1a;LeetCode 226.翻转二叉树 …

springboot静态资源访问问题归纳

以下内容基于springboot 2.3.4.RELEASE 1、默认配置的springboot项目&#xff0c;有四个静态资源文件夹&#xff0c;它们是有优先级的&#xff0c;如下&#xff1a; "classpath:/META-INF/resources/", &#xff08;优先级最高&#xff09; "classpath:/reso…

Unified 阻抗控制 architecture、framework、approach

Unified 阻抗控制&#xff08;Unified Impedance Control&#xff09;作为一种控制策略&#xff0c;其architecture&#xff08;架构&#xff09;、framework&#xff08;框架&#xff09;和approach&#xff08;方法&#xff09;为&#xff1a; 一、Unified 阻抗控制 Archite…

骨板和螺钉市场分析:前五大厂商占有大约78.0%的市场份额

据QYResearch调研团队最新报告“全球骨板和螺钉市场报告2024-2030”显示&#xff0c;预计2030年全球骨板和螺钉市场规模将达到76.2亿美元&#xff0c;未来几年年复合增长率CAGR为3.7%。 根据QYResearch头部企业研究中心调研&#xff0c;全球范围内骨板和螺钉生产商主要包括J &a…

视频美颜SDK与直播美颜工具的开发详解与技术优化

本篇文章&#xff0c;小编将为开发者提供一份详细的美颜指南。 一、视频美颜SDK的核心功能 视频美颜SDK是一种嵌入式软件开发工具包&#xff0c;允许开发者在应用中轻松实现实时美颜效果。其核心功能主要包括&#xff1a; 1.实时磨皮 2.美白功能 3.瘦脸与大眼 4.智能滤镜…

AWS 消息队列服务 SQS

AWS 消息队列服务 SQS 引言什么是 SQSSQS 访问策略 Access Policy示例&#xff1a;如何为 DataLake Subscription 配置 SQS 引言 应用系统需要处理海量数据&#xff0c;数据发送方和数据消费方是通过什么方式来无缝集成消费数据的&#xff0c;AWS 提供 SQS 消息队列服务来解决…

设计模式21-组合模式

设计模式21-组合模式&#xff08;Composite Pattern&#xff09; 写在前面 动机定义与结构定义结构主要类及其关系 C代码推导优缺点应用场景总结补充叶子节点不重载这三个方法叶子节点重载这三个方法结论 写在前面 数据结构模式 常常有一些组件在内部具有特定的数据结构。如何…

牛客网SQL进阶135 :每个6/7级用户活跃情况

每个67级用户活跃情况_牛客题霸_牛客网 0 问题描述 基于用户信息表user_info、、试卷作答记录表exam_record、题目练习记录表practice_record&#xff0c;统计 每个6/7级用户总活跃月份数、2021年活跃天数、2021年试卷作答活跃天数、2021年答题活跃天数&#xff0c;结果 按照总…

在linux上架设Web服务器Apache(Ubuntu)

欢迎诸位来阅读在下的博文~ 在这里&#xff0c;在下会不定期发表一些浅薄的知识和经验&#xff0c;望诸位能与在下多多交流&#xff0c;共同努力! 江山如画&#xff0c;客心如若&#xff0c;欢迎到访&#xff0c;一展风采 文章目录 背景1. 安装 Apache2. 启动和检查 Apache 服务…

强烈推荐这三款IOS应用,让你的生活更美好

Dino记账 Dino记账是一款结合了简洁设计和强大功能的记账应用&#xff0c;它通过多维度图表帮助用户轻松掌握金钱流向。应用界面明亮且配色突出&#xff0c;使得记录内容易于阅读&#xff0c;让记账和管理账目变得更加简单。 主要特性&#xff1a; 极简风格与易用性&#xff1…

掌握 Spring Boot + MyBatis-Plus 动态数据源切换,只要5分钟!

数据量猛增&#xff0c;通过动态数据源切换&#xff0c;我们不仅能提高查询效率&#xff0c;还能保证系统的高可用性。 通过将写操作集中在主库&#xff0c;读操作分散到多个从库&#xff0c;可以有效减轻数据库的压力。 在pom.xml中添加以下依赖&#xff1a; xml <depend…

Qt系统机制

Qt系统 Qt文件概述输入输出设备类QFileQFileInfoQt多线程Qt多线程常用API使用Qt多线程 线程安全互斥锁读写锁条件变量信号量 Qt网络QUdpSocketQNetworkDatagram设计一个UDP回显服务器QTcpServerQTcpSocketTcp版本的回显服务器HttpClient核心API Qt 音频Qt视频 Qt文件概述 ⽂件操…

入门Pandas必练习100题基础到进阶|阶级教程2

作者:郭震 51. How to get the row number of the nth largest value in a column? Find the row position of the 5th largest value of column a in df. # input df pd.DataFrame(np.random.randint(1, 30, 30).reshape(10,-1), columnslist(abc)) df# Solution 1# argsort…

HEML+CSS超详细基础知识

一些快捷键 ctrl/ 是注释 ctrld 是选中多个相同字 ctrls保存 altZ自动换行 altshift选中多行 HTML认知 基础认知 html初尝试 HTML页面结构介绍 初次尝试 开始动手写一个网页 先新建一个文件&#xff0c;记得后缀要命名成html 然后shift&#xff01;&#xff0c;就会自动…

《系统架构设计师教程(第2版)》第13章-层次式架构设计理论与实践-01-层次式体系结构概述

文章目录 1. 常用层次是架构2. 层次式架构设计的注意点2.1 污水池反模式2.2 应用变得庞大 本章教材又赘述了一遍架构的定义和层次架构风格的概述&#xff0c;我之前的笔记都写了 架构的定义回看《第7章-系统架构设计基础知识-01-软件架构&#xff08;Software Architecture&…

学习测试15-实战6-根据说明书建工程

CAN协议说明书&#xff1a;含义 一&#xff0c;得到表 1&#xff0c;先建信号 2&#xff0c;建报文&#xff0c;将对应信号拖入其中 3&#xff0c;建节点&#xff0c;将报文添加进TX msg里 调整起始位 数据库建立完成 二&#xff0c;不需要面板&#xff0c;直接导入数据库&…

HTTPS证书价格一年多少钱?如何购买?

目前市面上所有免费一年期HTTPS已经全部下架&#xff0c;付费证书已经成为主流。HTTPS证书的价格受多种因素影响&#xff0c;具体有以下几种&#xff1a; 一、证书类型 单域名证书价格一般在几百元左右&#xff0c;通配符价格高一些&#xff0c;千元以上&#xff0c;多域名价…

《知识点扫盲 · Redis 序列化器》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…