- 线性数据结构,和数组不同,数组要求元素类型一致,大小固定(满了需要扩容),内存地址连续
- 链表,每一个结点都会存储下一个结点的地址,双向链表还会存储上一个结点的地址。结点类型不固定,大小不固定,内存地址无需连续。只要我们知道固定某一结点的位置(例如单链表头结点,双链表头尾均可,中间位置一般没人那么干),就可以找到所有元素。

1. 单链表实现(Single linked list implementation)

- 每个结点,拥有存储数据的数据域data和指向它后继(下一个结点)的指针域next
- head变量保存头结点,最后一个结点的next指向空
- 查询:对比数组效率低,需要从头结点依次遍历

- 修改:对比数组效率低,从头结点依次遍历,找到目标结点后,修改结点数据域

- 插入:对比数组效率高,按照最复杂的情况,从头结点遍历到需要插入位置,让自己的next=原来这个位置的结点,让前面的结点的next指向自己
- 尾部插入,直接让原来的尾结点的next指向自己,自己的next指向null

- 头部插入,让自己的next指向原来的head头结点。然后head指向自己,自己成为新的头结点

- 中间插入,假设插入3这个位置,让自己next指向3这个位置当前的结点,然后2位置的next指向自己

- 删除:对比数组效率高
- 尾部删除,Java中,直接让倒数第二个next指向null,那么尾结点因为没有结点指向,会被垃圾回收器回收

- 头部删除,Java中,让head指向next,也就是头结点后面的元素,此时原来的头结点没有引用指向,会被垃圾回收器回收

- 中间删除,找到要删除结点,直接让前面结点的next越过它指向它的next,此时没有引用指向它,被垃圾回收器回收

- 效果演示

- 代码
import java.util.Collection;
import java.util.NoSuchElementException;
public class SingleLinkedList<E> {
private static class Node<E> {
E data;
Node<E> next;
Node(E element, Node<E> next) {
this.data = element;
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
", next=" + next +
'}';
}
}
private Node<E> first;
private int size = 0;
public SingleLinkedList(){}
public SingleLinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
private Node<E> node(int index){
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
}
public E get(int index) {
checkElementIndex(index);
return node(index).data;
}
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.data;
}
public E getLast() {
final Node<E> l = node(size-1);
if (l == null)
throw new NoSuchElementException();
return l.data;
}
public boolean add(E e) {
linkFirst(e);
return true;
}
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else if(index == 0)
linkFirst(element);
else
linkBefore(element, node(index-1));
}
private void linkBefore(E e, Node<E> pred) {
final Node<E> newNode = new Node<>(e,pred.next);
pred.next = newNode;
size++;
}
private void linkLast(E e) {
Node<E> last = node(size);
final Node<E> newNode = new Node<>(e, null);
last.next = newNode;
size++;
}
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(e,f);
first = newNode;
size ++;
}
public boolean addAll(Collection<? extends E> c) {
return this.addAll(size,c);
}
public boolean addAll(int index,Collection<? extends E> c){
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0) return false;
Node<E> pred,succ;
if(size == index){
succ = null;
pred = node(size);
}else{
pred = node(index-1);
succ = pred.next;
}
for (Object o : a){
E e = (E) o;
Node<E> newNode = new Node<>(e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ != null) {
pred.next = succ;
}
size += numNew;
return true;
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
public E removeLast() {
final Node<E> l = node(size-2);
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
public boolean remove(Object o) {
if (o == null) {
if(first.data == o){
removeFirst();
return true;
}
for (Node<E> x = first; x != null; x = x.next) {
if (x.next!=null&&x.next.data == null) {
unlink(x);
return true;
}
}
} else {
if(first.data.equals(o)){
removeFirst();
return true;
}
for (Node<E> x = first; x != null; x = x.next) {
if (x.next!=null&&o.equals(x.next.data)) {
unlink(x);
return true;
}
}
}
return false;
}
private E unlinkFirst(Node<E> f) {
final E element = f.data;
final Node<E> next = f.next;
f.data = null;
f.next = null;
first = next;
size--;
return element;
}
private E unlinkLast(Node<E> l) {
final Node<E> next = l.next;
final E element = next.data;
l.next = null;
next.data = null;
next.next = null;
size--;
return element;
}
private E unlink(Node<E> x) {
final Node<E> next = x.next.next;
final E element = x.next.data;
if (next != null) {
x.next = next;
}else{
x.next = null;
}
size--;
return element;
}
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
@Override
public String toString() {
return "SingleLinkedList{" +
"first=" + first +
", size=" + size +
'}';
}
public static void main(String[] args) {
SingleLinkedList<Integer> list = new SingleLinkedList<>();
list.add(3);
list.add(2);
list.add(0,0);
list.add(1,1);
System.out.println("添加&插入元素完成:"+list);
System.out.println("获取头元素"+list.getFirst());
System.out.println("获取尾元素"+list.getLast());
System.out.println("获取下标为1元素"+list.get(1));
list.removeFirst();
System.out.println("移除头元素"+list);
list.removeLast();
System.out.println("移除尾元素"+list);
list.remove(1);
System.out.println("移除data为1的元素"+list);
}
}
2. 双链表实现(Double linked list implementation)

- 每个结点,拥有数据域data,指向后继的next指针域,以及指向前驱的prev指针域
- 头结点的前驱为null,尾结点的后继为null
- 效果

- 代码
package com.yzpnb.data_structures.linked_list.double_linked_list;
import com.yzpnb.data_structures.linked_list.single_linked_list.SingleLinkedList;
import java.util.Collection;
import java.util.NoSuchElementException;
public class DoubleLinkedList<E> {
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
public DoubleLinkedList() {}
public DoubleLinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
Node<E> node(int index) {
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
public boolean add(E e) {
linkLast(e);
return true;
}
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
private void linkBefore(E e, Node<E> succ) {
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
}
private void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
}
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
}
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
return true;
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
private E unlinkFirst(Node<E> f) {
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null;
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
return element;
}
private E unlinkLast(Node<E> l) {
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null;
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
return element;
}
E unlink(Node<E> x) {
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
return element;
}
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
@Override
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
Node<E> node = this.first;
stringBuffer.append("first={");
for(;node.next!=null;){
if(node.prev !=null){
stringBuffer.append(toStringNode(node.item,node.next.item,node.prev.item)+",");
}else{
stringBuffer.append(toStringNode(node.item,node.next.item,null)+",");
}
node = node.next;
}
if(node.prev!=null){
stringBuffer.append(toStringNode(node.item,null,node.prev.item));
}else{
stringBuffer.append(toStringNode(node.item,null,null));
}
stringBuffer.append("}");
return stringBuffer.toString();
}
public String toStringNode(E item,E next,E prev){
return "Node{" +
"prev=" + prev +
", item=" + item +
", next=" + next +
'}';
}
public static void main(String[] args) {
DoubleLinkedList<Integer> list = new DoubleLinkedList<>();
list.add(2);
list.add(3);
list.add(0,0);
list.add(1,1);
System.out.println("添加&插入元素完成:"+list);
System.out.println("获取头元素"+list.getFirst());
System.out.println("获取尾元素"+list.getLast());
System.out.println("获取下标为1元素"+list.get(1));
list.removeFirst();
System.out.println("移除头元素"+list);
list.removeLast();
System.out.println("移除尾元素"+list);
list.remove(1);
System.out.println("移除data为1的元素"+list);
}
}
3. 循环链表实现(Circular linked list implementation)

- 可以是单链表,也可以是双链表,只不过最后一个结点的next不指向null,而是头结点
- 如果是双向链表,除了尾结点next指向头结点,还需要头结点prev指向尾结点
- 由于只改一个指针指向,就不写代码演示了,只需要让尾结点的next指向头,双向链表,让头结点prev也指向尾结点