日常开发中,数组和集合使用的很多,而数组的无序插入和删除效率都是偏低的,这点在学习ArrayList源码的时候就知道了,因为需要把要
插入索引后面的所以元素全部后移一位。
而本文会详细讲解链表,可以解决数组的部分问题,相比数组的大小不可更改,链表更加灵活,在学习LinkedList源码对链表有了一个大致的
了解。
ArrayList和LinkedList源码请参考:
Java集合(四)--基于JDK1.8的ArrayList源码解读
本文我们会学习:单链表、双端链表、有序链表、双向链表和有迭代器的链表,并且会讲解一下抽象数据类型(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数据结构和算法>