链表的概念
链表是一种物理存储单元上非连续、非顺序的存储结构,链表的顺序是由各个对象里的指针(下个元素的地址)决定的。链表由一系列结点(链表中每一个元素称为结点)组成,它在进行查找或访问特定下标的结点的效率不高,但是插入和删除的速度很快。链表可以有很多种不同形式,单向的或双向的,循环的或非循环的,已排序的或未排序的。
单向链表的结构
单向链表是一种线性表,每个结点中包括两个部分:一部分存储数据体,另一个部分存储下个结点的地址,对链表的操作都是通过其头结点进行的。
上图中的每个结点的黑色部分存储数据体,而白色部分存储着下个结点的地址,其中head为头结点,其结构与其他结点一致,但是作为起始结点,没有其他的结点指向它。
单向链表和一些对链表操作的实现
package datastructures;
import java.lang.reflect.Field;
import java.util.NoSuchElementException;
public class LinkedList2<T> {
Node<T> head;
/**
* 结点类,包含本身的数据体和下个结点
*/
private static class Node<T> {
T item;
Node<T> next;
Node(T elem, Node<T> next) {
this.item = elem;
this.next = next;
}
}
/**
* 把结点塞到头部
* @param t 数据体
*/
public void insertHead(T t) {
Node<T> newNode = new Node<>(t, head);
head = newNode;
}
/**
* 把结点塞到尾部
* @param t 数据体
*/
public void insertTail(T t) {
Node<T> newNode = new Node<>(t, null);
if (head == null) {
head = newNode;
} else {
Node<T> node = head;
while (node.next != null) {
node = node.next;
}
node.next = newNode;
}
}
/**
* 默认加到链表尾部
* @param t 数据体
*/
public void add(T t) {
insertTail(t);
}
/**
* 把接点塞到指定下标的位置
* @param index 下标
* @param elem 数据体
*/
public void add(int index, T elem) {
if (index < 0 || index > this.getSize()) {
throw new IndexOutOfBoundsException();
}
if (index == this.getSize())
insertTail(elem);
else if (index == 0) {
insertHead(elem);
}
else {
Node<T> prev = head;
Node<T> node = head.next;
for (int i = 1; i < index; i++) {
prev = node;
node = node.next;
}
Node<T> newNode = new Node<T>(elem, node);
prev.next = newNode;
}
}
/**
* 更改下标为index的结点的数据体
* @param index 下标
* @param element 数据体
* @return 返回原本位置的数据体
*/
public T set(int index, T elem) {
if (index < 0 || index >= this.getSize()) {
throw new IndexOutOfBoundsException();
}
Node<T> node = head;
for (int i = 0; i < index; i++) {
node = node.next;
}
T oldVal = node.item;
node.item = elem;
return oldVal;
}
/**
* 删除链表的头结点
* @return 返回被删除位置的数据体
*/
public T removeHead() {
if (head == null)
throw new NoSuchElementException();
Node<T> next = head.next;
T elem = head.item;
head = next;
return elem;
}
/**
* 删除链表的尾结点
* @return 返回被删除位置的数据体
*/
public T removeTail() {
if (head == null)
throw new NoSuchElementException();
if (head.next == null) {
T elem = head.item;
head = null;
return elem;
} else {
Node<T> cur = head.next;
Node<T> prev = head;
while (cur.next != null) {
prev = cur;
cur = cur.next;
}
prev.next = null;
return cur.item;
}
}
/**
* 根据数据体删除结点,只会删除第一个符合条件的结点
* @param obj 需要删除的数据体内容
* @return 是否对链表进行了删除操作(链表的尺寸是否变小)
*/
public boolean removeOneByElement(Object obj) {
if (head != null) {
if (obj == null) {
if (head.item == null) {
head = head.next;
return true;
}
Node<T> prev = head;
for (Node<T> x = head.next; x != null; prev = x, x = x.next) {
if (x.item == null) {
prev.next = x.next;
return true;
}
}
} else {
if (obj.equals(head.item)) {
head = head.next;
return true;
}
Node<T> prev = head;
for (Node<T> x = head.next; x != null; prev = x, x = x.next) {
if (obj.equals(x.item)) {
prev.next = x.next;
return true;
}
}
}
}
return false;
}
/**
* 根据数据体删除结点,会删除所有符合条件的接点
* @param obj 需要删除的数据体内容
*/
public void removeAllByElement(Object obj) {
if (head != null) {
if (obj == null) {
Node<T> prev = head;
for (Node<T> x = head.next; x != null;) {
if (x.item == null) {
prev.next = x.next;
x = x.next;
} else {
prev = x;
x = x.next;
}
}
if (head.item == null) {
head = head.next;
}
} else {
Node<T> prev = head;
for (Node<T> x = head.next; x != null;) {
if (obj.equals(x.item)) {
prev.next = x.next;
x = x.next;
} else {
prev = x;
x = x.next;
}
}
if (obj.equals(head.item)) {
head = head.next;
}
}
}
}
/**
* 根据下标删除
* @param index 下标
* @return 返回原本位置的数据体
*/
public T remove(int index) {
if (index < 0 || index >= this.getSize()) {
throw new IndexOutOfBoundsException();
}
if (index == 0) {
return removeHead();
}
Node<T> node = head.next;
Node<T> prev = head;
for (int i = 1; i < index; i++) {
prev = node;
node = node.next;
}
prev.next = node.next;
return node.item;
}
/**
* 获取链表的尺寸
*/
public int getSize() {
int size = 0;
Node<T> node = head;
while (node != null) {
node = node.next;
size++;
}
return size;
}
/**
* 返回头结点
* @return 返回链表头部的数据体
*/
public T getHead() {
final Node<T> f = head;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
/**
* 返回尾结点
* @return 返回链表尾部的数据体
*/
public T getTail() {
if (head == null)
throw new NoSuchElementException();
Node<T> node = head;
while (node.next != null) {
node = node.next;
}
return node.item;
}
/**
* 根据下标获取数据体
* @param index 下标
* @return 返回对于位置的数据体
*/
public T get(int index) {
if (index < 0 || index >= this.getSize()) {
throw new IndexOutOfBoundsException();
}
Node<T> node = head;
for (int i = 0; i < index; i++) {
node = node.next;
}
return node.item;
}
/**
* 判断链表是否为空
*/
public boolean isEmpty() {
return head == null;
}
/**
* 清空链表
*/
public void clear() {
for (Node<T> x = head; x != null; ) {
Node<T> next = x.next;
x.item = null;
x.next = null;
x = next;
}
head = null;
}
/**
* 反转链表
*/
public void reverse() {
if (head == null || head.next == null) {
return;
}
Node<T> cur = head;
Node<T> prev = null;
while (cur != null) {
Node<T> next = cur.next;
if (next == null) {
cur.next = prev;
head = cur;
return;
}
cur.next = prev;
prev = cur;
cur = next;
}
}
/**
* 获取中间结点的数据体
* @return 数据体
*/
public T getMid() {
Node<T> node1 = head;
Node<T> node2 = head;
while (node1 != null && node1.next != null && node1.next.next != null) {
node1 = node1.next.next;
node2 = node2.next;
}
return node2.item;
}
/**
* 查找倒数第n个元素
* @param index 倒数第几个
* @return 数据体
*/
public T getFromTail(int index) {
if (index < 0 || index >= this.getSize()) {
throw new IndexOutOfBoundsException();
}
Node<T> node1 = head;
Node<T> node2 = head;
for (int i = 0; i < index; i++) {
node1 = node1.next;
}
while (node1.next != null) {
node1 = node1.next;
node2 = node2.next;
}
return node2.item;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
try {
if (head == null) {
return "This list is empty\n";
}
Node<T> f = head;
while (f.next != null) {
if (f.item != null) break;
f = f.next;
}
int size = this.getSize();
if (f.item == null) {
for (int i = 0; i < size; i++) {
sb.append("[null]\n");
}
return sb.toString();
}
Class<T> clazz = (Class<T>) f.item.getClass();
Field[] fields = clazz.getDeclaredFields();
Node<T> node = this.head;
for (int i = 0; i < size; i++) {
T elem = node.item;
node = node.next;
if (elem == null) {
sb.append("[");
for (Field field : fields) {
sb.append(field.getName() + ": null, ");
}
sb.delete(sb.length() - 2, sb.length());
sb.append("]\n");
continue;
}
sb.append("[");
for (Field field : fields) {
field.setAccessible(true);
sb.append(field.getName() + ":" + field.get(elem) + ", ");
}
sb.delete(sb.length() - 2, sb.length());
sb.append("]\n");
}
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
return sb.toString();
}
}
双向链表的结构
双向链表的每个结点中存储着数据体还有上个结点和下个结点的地址。
上图中的每个结点的黑色部分存储数据体,而白色部分存储着上下结点的地址,其中head为头结点,tail为尾结点。
双向链表及对链表操作的实现
package datastructures;
import java.lang.reflect.Field;
import java.util.NoSuchElementException;
public class LinkedList3<T> {
int size = 0;
Node<T> head;
Node<T> tail;
private static class Node<T> {
T item;
Node<T> next;
Node<T> prev;
Node(Node<T> prev, T element, Node<T> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
/**
* 根据下标查找结点
* @param index 下标
* @return 结点
*/
private Node<T> findNode(int index) {
if (index <= (size / 2)) {
Node<T> t = head;
for (int i = 0; i < index; i++)
t = t.next;
return t;
} else {
Node<T> t = tail;
for (int i = size - 1; i > index; i--)
t = t.prev;
return t;
}
}
/**
* 把某个结点从链表中移除
* @param x 结点
* @return 数据体
*/
private T unlink(Node<T> t) {
T elem = t.item;
Node<T> next = t.next;
Node<T> prev = t.prev;
if (prev == null) {
head = next;
} else {
prev.next = next;
t.prev = null;
}
if (next == null) {
tail = prev;
} else {
next.prev = prev;
t.next = null;
}
t.item = null;
size--;
return elem;
}
/**
* 把结点塞到头部
* @param t 数据体
*/
public void insertHead(T t) {
Node<T> f = head;
Node<T> newNode = new Node<>(null, t, f);
head = newNode;
if (f == null)
tail = newNode;
else
f.prev = newNode;
size++;
}
/**
* 把结点塞到尾部
* @param t 数据体
*/
public void insertTail(T t) {
Node<T> l = tail;
Node<T> newNode = new Node<>(l, t, null);
tail = newNode;
if (l == null)
head = newNode;
else
l.next = newNode;
size++;
}
/**
* 默认加到链表尾部
* @param t 数据体
*/
public void add(T t) {
insertTail(t);
}
/**
* 把接点塞到指定下标的位置
* @param index 下标
* @param elem 数据体
*/
public void add(int index, T t) {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException();
}
if (index == size)
insertTail(t);
else {
Node<T> cur = this.findNode(index);
Node<T> pred = cur.prev;
Node<T> newNode = new Node<>(pred, t, cur);
cur.prev = newNode;
if (pred == null)
head = newNode;
else
pred.next = newNode;
size++;
}
}
/**
* 更改下标为index的结点的数据体
* @param index 下标
* @param element 数据体
* @return 返回原本位置的数据体
*/
public T set(int index, T t) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
Node<T> x = this.findNode(index);
T oldVal = x.item;
x.item = t;
return oldVal;
}
/**
* 删除链表的头结点
* @return 返回被删除位置的数据体
*/
public T removeHead() {
Node<T> f = head;
if (f == null)
throw new NoSuchElementException();
T elem = f.item;
Node<T> next = f.next;
head = next;
if (next == null)
tail = null;
else
next.prev = null;
size--;
return elem;
}
/**
* 删除链表的尾结点
* @return 返回被删除位置的数据体
*/
public T removeTail() {
Node<T> l = tail;
if (l == null)
throw new NoSuchElementException();
T elem = l.item;
Node<T> prev = l.prev;
tail = prev;
if (prev == null)
head = null;
else
prev.next = null;
size--;
return elem;
}
/**
* 根据数据体删除结点,只会删除第一个符合条件的结点
* @param obj 需要删除的数据体内容
* @return 是否对链表进行了删除操作(链表的尺寸是否变小)
*/
public boolean remove(Object o) {
if (o == null) {
for (Node<T> x = head; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<T> x = head; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
/**
* 根据下标删除
* @param index 下标
* @return 返回原本位置的数据体
*/
public T remove(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
return unlink(findNode(index));
}
/**
* 返回头结点
* @return 返回链表头部的数据体
*/
public T getHead() {
Node<T> f = head;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
/**
* 返回尾结点
* @return 返回链表尾部的数据体
*/
public T getTail() {
Node<T> l = tail;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
/**
* 根据下标获取数据体
* @param index 下标
* @return 返回对于位置的数据体
*/
public T get(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
return findNode(index).item;
}
/**
* 获取链表的尺寸
*/
public int size() {
return size;
}
/**
* 清空链表
*/
public void clear() {
for (Node<T> x = head; x != null; ) {
Node<T> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
head = tail = null;
size = 0;
}
/**
* 判断链表是否为空
*/
public boolean isEmpty() {
return head == null;
}
/**
* 反转链表
*/
public void reverse() {
if (head == null || head.next == null) {
return;
}
Node<T> cur = head;
while (cur != null) {
Node<T> prev = cur.prev;
Node<T> next = cur.next;
if (next == null) {
cur.prev = cur.next;
cur.next = prev;
head = cur;
return;
}
cur.prev = cur.next;
cur.next = prev;
cur = next;
}
}
/**
* 获取中间结点
* @return 数据体
*/
public T getMid() {
return get((size - 1) / 2);
}
/**
* 查找倒数第n个元素
* @param index 倒数第几个
* @return 数据体
*/
public T getFromTail(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
Node<T> node = head;
for (int i = 1; i < size - index; i++) {
node = node.next;
}
return node.item;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
try {
if (head == null) {
return "This list is empty";
}
Node<T> f = head;
while (f.next != null) {
if (f.item != null) break;
f = f.next;
}
if (f.item == null) {
for (int i = 0; i < this.size; i++) {
sb.append("[null]\n");
}
return sb.toString();
}
Class<T> clazz = (Class<T>) f.item.getClass();
Field[] fields = clazz.getDeclaredFields();
Node<T> node = this.head;
for (int i = 0; i < this.size; i++) {
T elem = node.item;
node = node.next;
if (elem == null) {
sb.append("[null]\n");
continue;
}
sb.append("[");
for (Field field : fields) {
field.setAccessible(true);
sb.append(field.getName() + ":" + field.get(elem) + ", ");
}
sb.delete(sb.length() - 2, sb.length());
sb.append("]\n");
}
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
return sb.toString();
}
}
其它形式链表的概念
如果是已排序的链表,则链表的线性顺序与链表元素中关键字的线性顺序一致;
如果是未排序的链表,则链表中各元素可以以任何顺序出现;
在循环链表中,如果是单向链表,则表尾的下一个结点指向表头,如果是双向链表,则表头的前一个结点指向表尾,而表尾的下一个结点指向表头,循环链表看起来就像一个圆环。如若要判断某个链表是否是循环链表,只需判断其头尾元素是否有链接起来。