Java数据结构和算法(四)--链表
日常开发中,数组和集合使用的很多,而数组的无序插入和删除效率都是偏低的,这点在学习ArrayList源码的时候就知道了,因为需要把要
插入索引后面的所以元素全部后移一位。
而本文会详细讲解链表,可以解决数组的部分问题,相比数组的大小不可更改,链表更加灵活,在学习LinkedList源码对链表有了一个大致的
了解。
ArrayList和LinkedList源码请参考:
Java集合(四)--基于JDK1.8的ArrayList源码解读
Java集合(五)--LinkedList源码解读
本文我们会学习:单链表、双端链表、有序链表、双向链表和有迭代器的链表,并且会讲解一下抽象数据类型(ADT)的思想,如何用 ADT 描述
栈和队列,如何用链表代替数组来实现栈和队列。
链节点:
在链表中,每个元素都被包含在链节点Link中。一个链节点是某个类的对象,这个类可以叫做Link。每个Link对象都包含对下一个Link引用的
字段(通常叫next)。但是链表本身有个字段指向对第一个Link的引用。
代码示例:
public class Link {private Object data;private Link next;
}
单链表:
单链表的机构比较简单,每个Node包含data和next(指向下个Node),最后一个Node的next指向null
图例:
代码实现:
public class SingleLinkList<E> {private int size; //链表长度大小private Node head; //头结点public SingleLinkList() {size = 0;head = null;}//添加元素到headpublic void addFirst(E data) {Node newNode = new Node(data);if (size == 0) {head = newNode;} else {newNode.next = head;head = newNode;}size++;}//删除头结点public E deleteFirst() {final E data = (E)head.data;head = head.next;size--;return data;}//查询某个元素是否存在public E find(E object) {Node current = head;int tempSize = size;while (tempSize > 0) {if (object == current.data) {return (E)current.data;} else {current = current.next;}tempSize--;}return null;}//删除链表中某个元素public boolean delete(E object) {if (null != object) {Node current = head;Node previous = head;while (!object.equals(current.data)) {if (current.next == null) {return false;} else {previous = current;current = current.next;}}if (current == head) {head = current.next;} else {previous.next = current.next;}size--;}return true;}//遍历打印链表public void displayList() {Node current = head;int tempSize = size;if (tempSize == 0) {System.out.print("[]");} else {System.out.print("[");while (current != null) {if (current.next == null) {System.out.print(current.data);;} else {System.out.print(current.data + "-->");;}current = current.next;}System.out.println("]");}}public boolean isEmpty() {return size == 0;}private static class Node<E>{E data;Node<E> next;Node(E data) {this.data = data;}}
}
public static void main(String[] args) { SingleLinkList<Integer> singleList = new SingleLinkList<Integer>(); singleList.addFirst(22); //添加节点 singleList.addFirst(44); singleList.addFirst(66); singleList.addFirst(88); singleList.displayList(); //打印链表结构 singleList.delete(44); //删除某个节点 singleList.displayList(); System.out.println(singleList.find(66)); //查询某个节点}
打印结果:
[88-->66-->44-->22]
[88-->66-->22]
66
双端链表:
双端链表和单向链表很相似,但是增加了一个新特性:就是对最后一个节点的引用,最后一个节点定义为tail
PS:双端链表不是双向链表,只能单向遍历,只是可以在双端添加/删除数据
图例:
代码实现:
public class DoubleLinkList<E> {private int size; //链表长度大小private Node head; //头结点private Node tail; //尾结点public DoubleLinkList() {size = 0;head = null;tail = null;}//添加元素到headpublic void addFirst(E data) {Node newNode = new Node(data);if (size == 0) {head = newNode;tail = newNode;} else {newNode.next = head;head = newNode;}size++;}//添加元素到tailpublic void addLast(E data) {Node newNode = new Node(data);if (size == 0) {head = newNode;tail = newNode;} else {tail.next = newNode;tail = newNode;}size++;}//删除头结点public E deleteFirst() {if (isEmpty()) {return null;}final E data = (E)head.data;if (head.next == null) {tail = null;}head = head.next;size--;return data;}//删除尾结点public E deleteLast() {if (isEmpty()) {return null;}final E data = (E)tail.data;if (head.next == null) {head = null;}int tempSize = size;Node current = head;Node previous = head;while (tempSize > 0) {if (current.next == null) {previous.next = null;break;}previous = current;current = current.next;tempSize--;}tail = previous;size--;return data;}//查询某个元素是否存在public E find(E object) {Node current = head;int tempSize = size;while (tempSize > 0) {if (object == current.data) {return (E)current.data;} else {current = current.next;}tempSize--;}return null;}//删除链表中某个元素public boolean delete(E object) {if (null != object) {Node current = head;Node previous = head;while (!object.equals(current.data)) {if (current.next == null) {return false;} else {previous = current;current = current.next;}}if (current == head) {head = current.next;}else {previous.next = current.next;}size--;}return true;}//遍历打印链表public void displayList() {Node current = head;int tempSize = size;if (tempSize == 0) {System.out.print("[]");} else {System.out.print("[");while (current != null) {if (current == tail) {System.out.print(current.data);break;} else {System.out.print(current.data + "-->");}current = current.next;}System.out.println("]");}}public boolean isEmpty() {return size == 0;}private static class Node<E>{E data;Node<E> next;Node(E data) {this.data = data;}}
}
public static void main(String[] args) {DoubleLinkList<Integer> doubleLinkList = new DoubleLinkList<Integer>();doubleLinkList.addFirst(22); //添加节点doubleLinkList.addFirst(44);doubleLinkList.addLast(66);doubleLinkList.addLast(88);doubleLinkList.addFirst(101);doubleLinkList.displayList(); //打印链表结构doubleLinkList.delete(44); //删除某个节点doubleLinkList.displayList();doubleLinkList.deleteLast(); //删除尾节点doubleLinkList.displayList();doubleLinkList.deleteFirst(); //删除头结点doubleLinkList.displayList();System.out.println(doubleLinkList.find(66)); //查询某个节点
}
输出结果:
[101-->44-->22-->66-->88]
[101-->22-->66-->88]
[101-->22-->66]
[22-->66]
66
链表的效率:
表头插入和删除的速度很快,时间复杂度O(1)
平均下来,定点插入、删除、查询都需要搜索链表中一半的节点,需要O(N)次比较,相比而言,数组执行这些操作也需要O(N)次比较,但是
链表不需要移动数据,只需要改变前后引用,而数组只能整体复制,效率会好很多
链表的另一个优点体现在内存使用上,需要多少内存就使用多少内存,而数组一开始的内存空间都是确定的
有序链表:
对于某些应用来说,在链表中保持数据的有序很很有用的。有序链表中,数据都是按照关键值有序排列的。
在大多数使用有序数组的场景也可以使用有序链表,在插入速度方面有很大优势
有序链表和一般单向链表只是添加方法有区别,其余方法都是相同的
代码示例:
public class SortedLinkList {private int size; //链表长度大小private Node head; //头结点public SortedLinkList() {size = 0;head = null;}//添加元素public void add(int data) {Node newNode = new Node(data);Node previoue = null;Node current = head;while (current != null && data > current.data) {previoue = current;current = current.next;}if (previoue == null) {head = newNode;head.next = current;} else {previoue.next = newNode;newNode.next = current;}size++;}//删除头结点public int deleteFirst() {final int data = head.data;head = head.next;size--;return data;}//查询某个元素是否存在public int find(int object) {Node current = head;int tempSize = size;while (tempSize > 0) {if (object == current.data) {return current.data;} else {current = current.next;}tempSize--;}return -1;}//删除链表中某个元素public boolean delete(int object) {Node current = head;Node previous = head;while (object != current.data) {if (current.next == null) {return false;} else {previous = current;current = current.next;}}if (current == head) {head = current.next;} else {previous.next = current.next;}size--;return true;}//遍历打印链表public void displayList() {Node current = head;int tempSize = size;if (tempSize == 0) {System.out.print("[]");} else {System.out.print("[");while (current != null) {if (current.next == null) {System.out.print(current.data);;} else {System.out.print(current.data + "-->");;}current = current.next;}System.out.println("]");}}public boolean isEmpty() {return size == 0;}private static class Node{int data;Node next;Node(int data) {this.data = data;}}
}
public static void main(String[] args) {SortedLinkList sortedLinkList = new SortedLinkList();sortedLinkList.add(5); //添加节点sortedLinkList.add(1);sortedLinkList.add(8);sortedLinkList.add(2);sortedLinkList.add(101);sortedLinkList.displayList(); //打印链表结构sortedLinkList.delete(8); //删除某个节点sortedLinkList.displayList();sortedLinkList.deleteFirst(); //删除头结点sortedLinkList.displayList();System.out.println(sortedLinkList.find(101)); //查询某个节点
}
输出结果:
[1-->2-->5-->8-->101]
[1-->2-->5-->101]
[2-->5-->101]
101
双向链表:
双向链表就是为了解决单向链表只能单向遍历而效率慢的问题,因为只能current=current.next进行遍历,只能获得下一个节点,而不能
获取上一个节点
在学习LinkedList源码的时候,我们已经详细了解过双向链表了,Java集合(五)--LinkedList源码解读
图例:
代码示例:
public class DoubleLinkedList<E> {private int size; //链表长度大小private Node head; //头结点private Node tail; //尾结点public DoubleLinkedList() {size = 0;head = null;tail = null;}//添加元素到headpublic void addFirst(E data) {Node newNode = new Node(data);if (size == 0) {tail = newNode;} else {head.previous = newNode;newNode.next = head;}head = newNode;size++;}//添加元素到tailpublic void addLast(E data) {Node newNode = new Node(data);if (size == 0) {head = newNode;} else {tail.next = newNode;newNode.previous = tail;}tail = newNode;size++;}//删除头结点public E deleteFirst() {if (isEmpty()) {return null;}final E data = (E)head.data;if (head.next == null) {tail = null;}head = head.next;head.previous = null;size--;return data;}//删除尾结点public E deleteLast() {if (isEmpty()) {return null;}final E data = (E)tail.data;if (head.next == null) {head = null;}tail = tail.previous;tail.next = null;size--;return data;}//查询某个元素是否存在/*public E find(E object) {if (object == null) {for (Node<E> x = head; x != null; x = x.next) {if (x.data == null) {return x.data;}}} else {for (Node<E> x = head; x != null; x = x.next) {if (object.equals(x.data)) {return x.data;}}}return null;}*///查询某个索引下标的数据public E find(int index) {return (E)node(index).data;}//删除链表中某个元素public boolean delete(E object) {if (object == null) {for (Node<E> x = head; x != null; x = x.next) {if (x.data == null) {unlink(x);return true;}}} else {for (Node<E> x = head; x != null; x = x.next) {if (object.equals(x.data)) {unlink(x);return true;}}}return false;}E unlink(Node<E> x) {final E element = x.data;final Node<E> next = x.next;final Node<E> prev = x.previous;if (prev == null) {head = next;} else {prev.next = next;x.previous = null;}if (next == null) {tail = prev;} else {next.previous = prev;x.next = null;}x.data = null;size--;return element;}Node node(int index) {if (index < (size >> 1)) {Node x = head;for (int i = 0; i < index; i++)x = x.next;return x;} else {Node<E> x = tail;for (int i = size - 1; i > index; i--)x = x.previous;return x;}}//遍历打印链表public void displayList() {Node current = head;int tempSize = size;if (tempSize == 0) {System.out.print("[]");} else {System.out.print("[");while (current != null) {if (current == tail) {System.out.print(current.data);break;} else {System.out.print(current.data + "-->");}current = current.next;}System.out.println("]");}}public boolean isEmpty() {return size == 0;}private static class Node<E>{E data;Node<E> previous;Node<E> next;Node(E data) {this.data = data;}}
}
public static void main(String[] args) {DoubleLinkedList<Integer> doubleLinkList = new DoubleLinkedList<Integer>();doubleLinkList.addFirst(22); //添加节点doubleLinkList.addFirst(44);doubleLinkList.addLast(66);doubleLinkList.addLast(88);doubleLinkList.addFirst(101);doubleLinkList.displayList(); //打印链表结构doubleLinkList.delete(44); //删除某个节点doubleLinkList.displayList();doubleLinkList.deleteLast(); //删除尾节点doubleLinkList.displayList();doubleLinkList.deleteFirst(); //删除头结点doubleLinkList.displayList();System.out.println(doubleLinkList.find(66)); //查询某个节点System.out.println(doubleLinkList.find(33)); //查询某个节点
}
输出结果:
[101-->44-->22-->66-->88]
[101-->22-->66-->88]
[101-->22-->66]
[22-->66]
66
拓展1、用链表实现:
public class MyStackByLinkedList<E> {private SingleLinkList linkList;public MyStackByLinkedList() {linkList = new SingleLinkList();}public void push(E e) {linkList.addFirst(e);}public E pop(){E e = (E)linkList.deleteFirst();return e;}}
拓展2:用双端链表实现队列
public class MyQueueByLinkedList<E> {private DoubleLinkedList linkedList;public MyQueueByLinkedList() {linkedList = new DoubleLinkedList();}public void add(E e) {linkedList.addFirst(e);}//移除数据public E remove(){return (E)linkedList.deleteFirst();}}
内容参考:<Java数据结构和算法>
转载于:https://www.cnblogs.com/huigelaile/p/11078109.html
Java数据结构和算法(四)--链表相关推荐
- Java数据结构与算法 day02 链表
文章目录 第三章 链表 单链表介绍和内存布局 单链表创建和遍历的分析实现 添加(创建)过程 遍历过程 代码实现 单链表按顺序插入节点 单链表节点的修改 单链表节点的删除和小结 单链表面试题 新浪面试题 ...
- Java 数据结构与算法面试 链表
手写链表的Java实现 package test;class Node{ // 数据域private Object ele; // 指针域private Node node; // 默认构造器,标准构 ...
- 尚硅谷图解Java数据结构和算法四
下载地址:https://pan.baidu.com/s/1aiMur9dTCUvnDRD32LH3Tg 提取密码:vv3k
- 一文通数据结构与算法之——链表+常见题型与解题策略+Leetcode经典题
文章目录 1 链表 1.1 常见题型及解题策略 1.1.1 LeetCode中关于链表的题目有以下五种类型题: 1.1.2 解题策略 1.2 链表的基本内容 1.2.1 链表的基本结构: 1.2.2 ...
- java环形链表_数据结构和算法(四)Java实现环形链表
1. 数据结构和算法(四)Java实现环形链表 1.1 约瑟夫问题 约瑟夫问题:公元66年,约瑟夫不情愿地参与领导了犹太同胞反抗罗马统治的起义,后来起义失败,他和一些宁死不降的起义者被困于一个山洞之中 ...
- java数据结构与算法之顺序表与链表深入分析
转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/52953190 出自[zejian的博客] 关联文章: java数据结 ...
- java数据结构与算法之双链表设计与实现
转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/53047590 出自[zejian的博客] 关联文章: java数据结 ...
- 【Java数据结构及算法实战】系列002:算法的四种描述方式
本节是<Java数据结构及算法实战>系列的第2节,主要介绍描述算法的常用的4种方式. 要定义一个算法,我们可以用自然语言.流程图.伪代码的方式描述解决某个问题的过程或是编写一段程序来实现这 ...
- Java数据结构和算法(一)——简介
本系列博客我们将学习数据结构和算法,为什么要学习数据结构和算法,这里我举个简单的例子. 编程好比是一辆汽车,而数据结构和算法是汽车内部的变速箱.一个开车的人不懂变速箱的原理也是能开车的,同理一个不懂数 ...
最新文章
- “万人迷”小冰背后的AI故事
- 2016电大计算机网考,2016年电大-电大计算机网考题库[].doc
- 潮流设计师创作灵感|是时候设计一下蒸汽波海报了!
- java与android https,Java-Android SSL https发布
- add php support,WordPress add_theme_support() 函数详解
- 应用系统适配迁移方案
- android 图片可以滚动条,Android仿即刻首页垂直滚动图,炫酷到底!
- 网易云课堂 oracle,网易云课堂DBA学习笔记 (一) 数据库基础
- 面试官问“你还有什么需要了解的吗”——应该这样回答
- diskpart建立新卷
- DCO-OFDM可见光通信matlab
- C#,入门教程——列表(List)的基础知识
- Nginx反向代理实现负载均衡配置图解
- t430服务器安装系统,Dell PowerEdge T430
- 中国信通院:2017年Q3共享单车行业发展指数报告(附下载)
- CTA-敏感行为-AppOps方案
- 使用Dockerfile创建包含nginx-fair和nginx-check模块的nginx镜像
- [置顶]类的加载连接初始化
- National day present
- 计算机监控系统软件设计,计算机监控软件设计.doc
热门文章
- messagehub讲解,如何获取MessageHub的其余管理API的API密钥?
- TYVJ1467 通往聚会的道路
- PhoneGap在Microsoft Visual Studio Express For Wi...
- java导出excel 客户端_java如何将导出的excel下载到客户端
- 《假设的世界-一切不能想当然》笔记
- TCP/IP协议简介
- java中有效关键字_java中有效的关键字
- android shape 绘制气泡图,气泡图-自定义 shape
- 设置和使用地图的范围—ArcGIS API for JavaScript
- asp.net数据库连接web.config配置