用Java描述数据结构之线性表的链式存储(链表),模拟LinkedList实现
上一篇介绍了顺序表:用Java描述数据结构之线性表的顺序存储(顺序表),ArrayList及其方法的介绍
上一篇博客中说明了什么是线性表——线性表就是一个个数据元素逻辑上以一对一的相邻关系(但是在物理结构上并不一定是连续得到)组织起来的有限序列。
根据物理存储方式的不同可以将线性表分为两种,一种叫顺序表,一种叫链表,这篇博客主要介绍链表。
链表和顺序表不同,链表在存储时并不是连续的,大家可以这么理解:内存上被使用的一段段空间之间会有小段的未被使用的,链表在使用时就可以使用这些小段的未被使用的空间去存储数据,而顺序表需要相比于链表则是一段较长的连续的存储空间。因为顺序表底层其实是使用数组实现的,而数组在声明之后长度是不可变的,所以顺序表在使用之初就会申请一段相对较长的数组,随着顺序表内增加的元素越来越多,数组可用的空间就越来越少,直到顺序表检测到原数组空间不够用时会申请一个比原数组长的新数组,然后将原数组中的数据依次复制到新数组,顺序表就一直重复着这样的操作来满足增加新元素的可持续性,这导致了多数时候顺序表底层的数组有大量未使用的空间,但这些空间又不能被其他程序使用,所以顺序表在空间利用上存在明显的弊端。
链表的出现就完美的解决了顺序表在空间利用的不足之处(虽然链表一个节点存储了除了数据元素外相比于顺序表的一个节点还多了指向当前节点的前后节点的引用,但是多出来的这点空间相比于顺序表底层数组时常存在的未被使用的空间相比,还是小了很多),链表的一个节点除了储存要储存的数据外,还需要存指向在逻辑上该节点的前后两个节点的引用,这因为这多出来可以指向前后节点的引用,才可以使链表在物理存储上可以不连续,因为可以通过当前节点中的引用去找它的前后节点。
为了方便理解节点,我们定义一个节点类:
public class MyListNode {//该链表可以存储String类型数据public String val;//指向当前节点的前节点的引用public MyListNode prev;//指向当前节点的后节点的引用public MyListNode next;//构造方法public MyListNode(String val){ this.val = val; }
}
虽然Java有已经实现的LinkedList类,但是为了方便对链表更深入的理解,我建议大家可以自己实现以下LinkedList的常用方法,有些同学可能对节点和引用这一块不太理解,我推荐你去看我这篇博客,看完之你能更好的理解链表:通过链表深入理解Java的引用和对象,下面开始介绍LinkedList的常用方法,以及自己实现它们的思路(思路放在代码中,每个方法实现前),还是将常用方法分成增删查改四个大类:
增 :
方法名 | 作用 |
---|---|
boolean add(element) | 将element尾插在链表中,理论上一定会尾插成功,即一定会返回true。 |
void add(index, element) | 将element插在指定位置index处,index的范围应该在[0, size()]之间。 |
删 :
方法名 | 作用 |
---|---|
E remove(index) | 删除index位置的元素,并返回该元素的值。 |
boolean remove(element) | 删除指定元素,这里就有三种情况了: 1.如果该元素确实在链中,并且只有一个,则删除该元素,最后返回true;2.如果该元素在链表中有多个,则删除第一个,同时返回true;3.如果链表中没有该元素,则什么都不做,返回false。 |
void clear() | 无论链表中原来有多少元素,执行该方法之后, 清空这个顺序表。 |
查 :
方法名 | 作用 |
---|---|
E get(index) | 获取链表中的index位置的元素,并且返回,index的范围为[0, size()]。 |
boolean contains(element) | 判断链表中是否包含元素,包含返回true,否则返回false。 |
int indexOf(element) | 返回该元素从前往后找的首次遇到的下标,如果没有则返回 -1。 |
int lastIndexOf(element) | 返回该元素从后往前找的首次遇到的下标,如果没有则返回 -1。 |
boolean isEmpty() | 判断链表是否为空,为空返回true,否则返回false。 |
int size() | 返回链表中元素的个数。 |
改 :
方法名 | 作用 |
---|---|
E set(index, element) | 向链表的index位置放入元素element,并且返回该下标位置原来的元素。 |
下面是实现代码:
public class MyLinkedList implements MyList {public MyListNode head;public MyListNode last;public int size;public MyLinkedList(){head = null;last = null;size = 0;}//尾插//当链表为空时,要尾插一个节点时//需要同时让 head 和 last 两个引用指向新节点;//当链表不为空时,只需要用 last(last指向最后一个节点)来操作//最后让last指向新的尾节点public boolean add(String e) {MyListNode node = new MyListNode(e);if(head == null){head = node;}else {last.next = node;node.prev = last;}last = node;size++;return true;}//指定位置插入节点//先判断下标的合法性//分为三种情况,头插,尾插,从中间插//头插就是index == 0,如果原链表为空链表,index等于0也为合法下标//所以头插要分为原链表为空表,或者原链表不是空表两种情况//链表不为空时,需要判断是否是尾插,因为尾插需要改变last//最后一种情况就是正常插入public void add(int index, String e) {if(index < 0 || index > size()){throw new ArrayIndexOutOfBoundsException();}MyListNode node = new MyListNode(e);if(index == 0){if(head != null){node.next = head;head.prev = node;head = node;}else {head = node;last = node;}} else if(index == size()){add(e);}else {MyListNode preNode = head;for(int i = 1; i < index ; i++){preNode = preNode.next;}//先让node的prev 和 next 分别指向//它的前一个和后一个节点node.prev = preNode;node.next = preNode.next;//再让node的下一个节点的prev指向nodepreNode.next.prev = node;//最后让node前一个节点的next指向nodepreNode.next = node;}size++;}//删除指定下标节点//先判断下标合法性//分为三种情况:头删,删中间,尾删//头删又分为两种情况:表中只有一个节点的头删和表中节点数大于1//删中间正常删就可以//尾删同上public String remove(int index) {if(index < 0 || index >= size()){throw new ArrayIndexOutOfBoundsException();}String oldVal;if(index == 0){if(size() == 1){oldVal = head.val;head = null;}else {oldVal = head.val;head = head.next;head.prev = null;}}else if(index == size() - 1){oldVal = last.val;last = last.prev;last.next = null;}else {MyListNode prevNode = head;for(int i = 1; i < index; i++){prevNode = prevNode.next;}oldVal = prevNode.val;prevNode.next.next.prev = prevNode;prevNode.next = prevNode.next.next;}size--;return oldVal;}public boolean remove(String e) {int index = indexOf(e);if (index < 0) {return false;}remove(index);return true;}public String get(int index) {if (index < 0 || index >= size()) {throw new ArrayIndexOutOfBoundsException();}MyListNode node = head;for (int i = 0; i < index; i++) {node = node.next;}return node.val;}public String set(int index, String e) {if (index < 0 || index >= size()) {throw new ArrayIndexOutOfBoundsException();}MyListNode node = head;for (int i = 0; i < index; i++) {node = node.next;}String oldE = node.val;node.val = e;return oldE;}public int indexOf(String e) {// 遍历查找int i = 0;for ( MyListNode cur = head; cur != null; cur = cur.next) {if (cur.val.equals(e)) {return i;}i++;}return -1;}public int lastIndexOf(String e) {int i = size() - 1;for (MyListNode cur = last; cur != null; cur = cur.prev) {if (cur.val.equals(e)) {return i;}i--;}return -1;}public boolean contains(String e) {return indexOf(e) >= 0;}public int size() {return size;}public boolean isEmpty() {return size() == 0;}@Overridepublic String toString() {StringBuilder sb = new StringBuilder("[");for (Node cur = head; cur != null; cur = cur.next) {sb.append(cur.val);if (cur != last) {sb.append(", ");}}sb.append("]");return sb.toString();}}
测试`
public class Demo {public static void main(String[] args) {MyLinkedList linkedList = new MyLinkedList();linkedList.add("A");linkedList.add("B");linkedList.add("C");System.out.println(linkedList); // [A, B, C]linkedList.add(0, "A");linkedList.add(0, "B");linkedList.add(2, "C");System.out.println(linkedList); // [A, B, C]linkedList.add(2, "D");System.out.println(linkedList.size()); // 3System.out.println(linkedList.isEmpty()); // falseSystem.out.println("======================");linkedList.clear();System.out.println(linkedList); // []System.out.println(linkedList.size()); // 0System.out.println(linkedList.isEmpty()); // trueSystem.out.println("======================");System.out.println(linkedList); // [B, A, D, C]System.out.println(linkedList.size()); // 4System.out.println(linkedList.isEmpty()); // falseSystem.out.println("======================");linkedList.add("A");linkedList.add("B");linkedList.add("C");System.out.println(linkedList.get(1)); // BSystem.out.println(linkedList.set(1, "b")); // BSystem.out.println(linkedList); // [A, b, C]}
}
运行结果:
以上就是这篇博客所有内容,如果理解有偏差,还望看官在评论区指正,谢谢!
用Java描述数据结构之线性表的链式存储(链表),模拟LinkedList实现相关推荐
- Python 数据结构 之 线性表 的链式存储结构
用Python 来实现 C语言中 线性表的链式存储结构. 文章转载请注明: Python 数据结构 之 线性表 的链式存储结构 代码地址 https://github.com/WenkeZhou/P ...
- 【数据结构】线性表的链式存储-单链表
单链表的定义 线性表的链式存储又称为单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素. 为了建立起数据元素之间的线性关系,对每个链表结点,除了存放元素自身的信息之外,还需要存放一个指向其后 ...
- 数据结构之线性表的链式存储实现(附完整代码)
顺序表插入.删除时需要通过移动数据来实现,影响了执行效率. 而链表不要求逻辑上相邻的两个数据元素物理上也相邻,因此对线性表的插入.删除不需要移动数据元素,只需要修改链. 下面介绍带头结点的链式表: 数 ...
- 【数据结构】线性表的链式存储-双链表
引言 单链表结点中只有一个指向其后继的指针,这使得单链表只能从头结点依次顺序地向后遍历.若要访问某个结点的前驱结点(插入.删除操作时),只能从头开始遍历 ,访问后继结点的时间复杂度为 0(1),访问前 ...
- JAVA数据结构 线性表的链式存储及其实现
2019独角兽企业重金招聘Python工程师标准>>> 2线性表的链式存储及其实现 虽然顺序表具有随机存取的特点是一种有用的存储结构但是也有缺陷: (1) 若需要给顺序表增 ...
- 【数据结构 C描述】有两个整数集合 A 和 B 分别用两个线性表 LA 和 LB 表示,求:一个新的集合A=A∪B,A仍然为纯集合,线性表采用链式存储方式。【单链表】
这是我的作业题,作业写完后再附到博客中,可能代码写得很烂.虽然是C语言写的,但是我在其中加入了C++的写法,例如cout.我懒得写printf的格式控制符,所以用cout来输出内容,这样我感觉简便多了 ...
- 数据结构和算法:(3)3.2线性表的链式存储结构
线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素也就是说你这个可以放在A地点,这个可以放在E地点,A地点和E地点中间可以隔开一个C地点和D地点,这样是允许的),这组存储单元可以存在 ...
- 《数据结构》c语言版学习笔记——单链表结构(线性表的链式存储结构Part1)
线性表的链式存储结构 数据结构系列文章 第二章 单链表结构 文章目录 线性表的链式存储结构 前言 一.单链表的建立 代码 二.单链表的读取 代码 三.单链表的插入 代码 四.单链表的删除 代码 五.单 ...
- 【数据结构】CH2 线性表的链式存储结构
目录 一.链表概述 1.相关定义 二.单链表 1.插入和删除节点的操作 (1)插入结点 (2)删除结点 2.建立单链表 (1)头插法 (2)尾插法 3.线性表基本运算在单链表中的实现 (1)初始化线性 ...
最新文章
- R语言ggplot2可视化分组变量下的数据分布(线条、色彩配置)、WVPlots包的ShadowHist函数比较分组下的数据直方图、ggplot2分面图facet_wrap可视化分组变量下的数据分布
- python3官方文档 中文-Python 3.7 官方文档中文翻译召集
- Apache 2,4版本 编译与安装 RedHat enterprises 6.2
- 轻松 Flutter 入门,秒变大前端
- 架构部署002--城域网_骨干网
- SpringBoot-技术专区-详细打印启动时异常堆栈信息
- 字符串当id用 转换成json对象
- android 抓包 2
- 直播平台开发时关于生成邀请码的开发
- 自定义小型ajax框架-ajax讲义二
- 笨方法学python 习题31
- 计算机联锁静态数据表,计算机联锁功能.doc
- com组件调用regsvr32的时候调试DllRegisterServer时候遇到的问题
- f28335的c语言结构体,TMS320F28335程序SVPWM源程序
- CNN网络实现垃圾分类
- git rebase详解(图解+最简单示例,一次就懂)
- 如何计算图论中的模块化modularity指标,图论中的社区检测算法——Louvain community
- C Primer Plus 第6版 中文版 勘误表
- DOM常用属性【DOM】
- Real-Time Rendering 第六章 texture