日常开发中,数组和集合使用的很多,而数组的无序插入和删除效率都是偏低的,这点在学习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;
} //添加元素到head
public 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;
} //添加元素到head
public void addFirst(E data) {
Node newNode = new Node(data);
if (size == 0) {
head = newNode;
tail = newNode;
} else {
newNode.next = head;
head = newNode;
}
size++;
} //添加元素到tail
public 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;
} //添加元素到head
public void addFirst(E data) {
Node newNode = new Node(data);
if (size == 0) {
tail = newNode;
} else {
head.previous = newNode;
newNode.next = head;
}
head = newNode;
size++;
} //添加元素到tail
public 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数据结构和算法>

Java数据结构和算法(四)--链表的更多相关文章

  1. Java数据结构和算法之链表

    三.链表 链结点 在链表中,每个数据项都被包含在‘点“中,一个点是某个类的对象,这个类可认叫做LINK.因为一个链表中有许多类似的链结点,所以有必要用一个不同于链表的类来表达链结点.每个LINK对象中 ...

  2. Java数据结构和算法(四)——栈

    stack,中文翻译为堆栈,事实上指的是栈,heap,堆. 这里讲的是数据结构的栈,不是内存分配里面的堆和栈. 栈是先进后出的数据的结构,好比你碟子一个一个堆起来.最后放的那个是堆在最上面的. 队列就 ...

  3. Java数据结构和算法(六)--二叉树

    什么是树? 上面图例就是一个树,用圆代表节点,连接圆的直线代表边.树的顶端总有一个节点,通过它连接第二层的节点,然后第二层连向更下一层的节点,以此递推 ,所以树的顶端小,底部大.和现实中的树是相反的, ...

  4. Java数据结构和算法(五)——队列

    队列.queue,就是现实生活中的排队. 1.简单队列: public class Queqe { private int array[]; private int front; private in ...

  5. Java数据结构和算法(一)线性结构之单链表

    Java数据结构和算法(一)线性结构之单链表 prev current next -------------- -------------- -------------- | value | next ...

  6. Java数据结构和算法(四)赫夫曼树

    Java数据结构和算法(四)赫夫曼树 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 赫夫曼树又称为最优二叉树,赫夫曼树的一个 ...

  7. Java数据结构和算法(十四)——堆

    在Java数据结构和算法(五)——队列中我们介绍了优先级队列,优先级队列是一种抽象数据类型(ADT),它提供了删除最大(或最小)关键字值的数据项的方法,插入数据项的方法,优先级队列可以用有序数组来实现 ...

  8. Java数据结构和算法 - 二叉树

    前言 数据结构可划分为线性结构.树型结构和图型结构三大类.前面几篇讨论了数组.栈和队列.链表都是线性结构.树型结构中每个结点只允许有一个直接前驱结点,但允许有一个以上直接后驱结点.树型结构有树和二叉树 ...

  9. Java数据结构和算法 - 什么是2-3-4树

    Q1: 什么是2-3-4树? A1: 在介绍2-3-4树之前,我们先说明二叉树和多叉树的概念. 二叉树:每个节点有一个数据项,最多有两个子节点. 多叉树:(multiway tree)允许每个节点有更 ...

随机推荐

  1. pkill详解

    pkill详解 一:含义: 是ps命令和kill命令的结合,按照进程名来杀死指定进程,pkill和killall应用方法差不多,也是直接杀死运行中的程序:如果您想杀掉单个进程,请用kill来杀掉. 二 ...

  2. 笔记本电脑处理器(CPU)性能排行榜

    笔记本电脑处理器(CPU)性能排行榜 本排行榜随新款处理器(CPU)的发布而随时更新.更新日期:2012年7月15日   排名 型号 二级+三级缓存 前端总线(MHz) 功率(瓦) 主频(MHz) 核 ...

  3. 51nod 1119【杨辉三角】

    思路: = =杨辉三角的应用,组合数的应用: C(N+M,N); 逆元一发,费马小定理,OK. #include <stdio.h> #include <string.h> # ...

  4. 洛谷 P4125 [WC2012]记忆中的水杉树【扫描线+set+线段树】

    我没有找到能在bzojAC的代码--当然我也WA了--但是我在洛谷过了,那就假装过了吧 minmax线段树一开始写的只能用min更新min,max更新max,实际上是可以互相更新的-- 首先看第二问, ...

  5. Luogu P2114[NOI2014]起床困难综合症 【贪心/位运算】By cellur925

    题目传送门 所以NOI的题现在简单惹? 30分做法:枚举开始的权值,n²过掉. 100分做法:竟然是贪心qwq.因为我们的计算背景是二进制下,所以我们贪心地想让每一位都是1.我们现在需要解决的问题,就 ...

  6. iOS WKWebView 加载进度条、导航栏返回&关闭 (Swift 4)

    导航: 1.加载进度条 2.导航栏增加返回.关闭按钮 加载进度条 效果图 代码如下: self.progressView.trackTintColor = UIColor.white self.pro ...

  7. iOS 监测电话呼入

    1.首先引入CoreTelephony框架,代码里: @import CoreTelephony; 项目设置里: 2.定义属性,建立强引用: @property (nonatomic, strong) ...

  8. Educational Codeforces Round 46 (Rated for Div. 2) A. Codehorses T-shirts

    Bryce1010模板 http://codeforces.com/problemset/problem/1000/A 题意: 问你将一种类型的衣服转换成另一种的最小次数. #include<b ...

  9. Music in Car CodeForces - 746F

    Music in Car CodeForces - 746F 题意很难懂啊... 题意:http://blog.csdn.net/a838502647/article/details/74831793 ...

  10. 7.1 Java集合概述

    List 有序.重复的集合 Set 无序.不可重复的集合 Map 具有映射关系的集合 jdk1.5之后.Java增加了Queue体系集合,代表一种队列集合实现