本文摘抄至 benjaminwhx
何为链表
—–链式结构是一种使用对象引用变量来创建对象间的链的数据结构。对象引用变量可以用于创建链式结构摘要 : 链式结构是一种使用对象引用变量来创建对象间的链的数据结构。对象引用变量可以用于创建链式结构,对象引用变量所存储的特定地址一般无关紧要。换句话说,重要的是能够使用引用变量来访问对象,而对象在内存中的特定存储位置并不重要。因此,我们一般将引用变量描述为一个指向对象的名称,而不是显示器地址…
一个指向对象的对象引用变量 :对象引用变量所存储的特定一般无关紧要。换句话说,重要的是能够使用引用变量来访问对象。而对象在内存中的特定存储位置并不重要。因此,我们一般将引用变量描述成一个 指向对象名称。而不是显示其地址,按照这种理解,引用变量有时成为指针
public class Person {
private String name;
private String address;
// Person对象的引用
private Person next;
}
这种类型的对象有时称为自引对象: 这种关系构成了链表的基础。链表是一种链式结构,该结构的每一个对象都指向下一个对象。从而构成了俩表中对象的线性次序。链表中存储的对象一般被称为链表的
节点。 链表:
注意 : 链表单独需要一个引用变量,用以表示其中第一个节点,如果某个节点的 next 引用为null,则表示链表在该节点处终止
实现一个单向链表
插入节点
可能会在链表的任何位置插入一个节点:在链表的首部、或在链表中任何内部节点之间、或在链表的尾部。
(1).在首部插入节点
第一步:设置所添加节点的next,使其指向链表中当前的第一个节点。
第二部:重新设置指向链表首部的引用,使其指向这个新添的节点。
注意 : 如果上述这些步骤颠倒了。则将会遇到困难,如果首先重置 front 引用,则将失去这个唯一指向现有链表的引用,而且再也不能获取它。
(2).在其它部分插入节点
首先,设置新节点的next引用,使其指向current索引引用节点的下一个节点。然后,重新设置当前节点的next引用,使其指向这个新的节点。
删除节点
可能会在链表的任何位置插入一个节点:在链表的首部、或在链表中任何内部节点之间、或在链表的尾部。
(1).删除首部节点
对链表中的第一个节点的处理一般比较特殊。
要删除链表的第一个节点,则需要重新设置指向链表首部的引用。使其指向链表中当前的第二个节点。
(2).删除其它部分节点
一旦找到要删除的节点,则重新设置前一个节点的next引用。使其指向的节点与当前的next引用所指向的节点相同。然后,可以根据需要使用这个删除的节点。
代码实现
package chain;
/**
* 单向链表的实现
*/
public class NodeList<E> {
private static class Node<E> {
E data;
Node<E> next;
public Node(E data, Node<E> next) {
this.data = data;
this.next = next;
}
}
/** 首元素的引用 **/
private Node<E> head;
/** 尾元素的引用 **/
private Node<E> last;
/** 其他元素的引用 **/
private Node<E> other;
/** list的元素个数 **/
private int size = 0;
/**
* 默认构造
*/
public NodeList() {
head = null;
last = head;
}
/**
* 构造一个有参数的list
* @param data 初始化参数
*/
public NodeList(E data) {
Node<E> d = new Node<>(data, null);
head = d;
last = head;
size++;
}
/**
* 增加一个数据到list中
* @param data 新增的数据
*/
public void add(E data) {
Node<E> d = new Node<>(data, null);
if (isEmpty()) {
head = d;
last = head;
} else {
last.next = d;
last = d;
}
size++;
}
/**
* 在index处添加元素data,其他元素后移
* @param index 元素索引,从0开始.
* @param data 新增元素
*/
public void add(int index, E data) {
checkIndex(index);
other = head;
if (index == 0) { //如果加在第一个,就把head的引用设为新元素对象
Node<E> d = new Node<>(data, other);
head = d;
} else {
for (int i = 0; i < index; i++) {
other = other.next;
}
Node<E> d = new Node<>(data, other.next);
other.next = d;
}
size++;
}
/**
* 替换原有的值为newValue
* @param oldValue 旧值
* @param newValue 新值
* @return 如果替换成功返回true, 否则返回false
*/
public boolean set(E oldValue, E newValue) {
other = head;
while (other != null) {
if (other.data.equals(oldValue)) {
other.data = newValue;
return true;
}
other = other.next;
}
return false;
}
/**
* 在指定的值后面插入一个新值
* @param specidiedData 指定的值
* @param newData 新值
* @return 如果插入成功返回true,否则返回false
*/
public boolean addAfter(E specidiedData, E newData) {
other = head;
while (other != null) {
if (other.data.equals(specidiedData)) {
Node<E> d = new Node<>(newData, other.next);
other.next = d;
size++;
return true;
}
other = other.next;
}
return false;
}
/**
* 根据索引, 获得此处的数据
* @param index 索引
* @return 获得的数据
*/
public E get(int index) {
checkIndex(index);
other = head;
for (int i = 0; i < index; i ++) {
other = other.next;
}
return other.data;
}
/**
* 删除list中存在的data
* @param data 要删除的数据
* @return 如果删除成功返回true,否则返回false
*/
public boolean remove(E data) {
other = head;
Node<E> pre = other;
for (int i = 0; i < size; i++) {
if (other.data.equals(data)) {
if (i == 0) {
head = other.next;
return true;
}
pre.next = other.next;
return true;
}
pre = other;
other = other.next;
}
return false;
}
/**
* 是否包含传入的元素
* @param data 传入的数据
* @return 如果存在返回true, 否则false
*/
public boolean contains(E data) {
other = head;
for (int i = 0; i < size; i++) {
if (other.data.equals(data)) {
return true;
}
}
return false;
}
private boolean isEmpty() {
return size == 0 || head == null;
}
/**
* 清空链表
*/
public void clear() {
head = null;
size = 0;
}
public void checkIndex(int index) {
if (index < 0 || index > size-1)
throw new IndexOutOfBoundsException("传入的索引无效: " + index);
}
/**
* 打印全部链表数据
*/
public void print() {
if (head == null || size == 0)
System.out.println("链表为空");
else {
other = head;
while (other != null) {
System.out.print(other.data + " ");
other = other.next;
}
System.out.println();
}
}
}
package chain;
public class NodeListTest {
public static void main(String[] args) {
NodeList<String> nodeList = new NodeList<>("first");
nodeList.add("a");
nodeList.add("b");
nodeList.add("c");
nodeList.print();
nodeList.add(2, "addNew");
nodeList.print();
nodeList.set("c", "C");
nodeList.print();
nodeList.addAfter("addNew", "addNew2");
nodeList.print();
System.out.println(nodeList.get(2));
nodeList.remove("addNew2");
nodeList.print();
nodeList.remove("first");
nodeList.print();
System.out.println(nodeList.contains("a"));
System.out.println(nodeList.contains("A"));
nodeList.clear();
nodeList.print();
/**
* first a b c
first a b addNew c
first a b addNew C
first a b addNew addNew2 C
b
first a b addNew C
a b addNew C
true
false
链表为空
*/
}
}
以上为单向链表的简单实现。
实现一个双向链表
链式结构的另一种实现就是双向链表。双向链表维护两个引用:一个指向链表的第一个结点,另一个指向最后一个结点。链表中的每个结点都存储两个引用:一个指向下一个结点,另一个指向前一个结点。
下面是我仿着LinkedList写的双向链表。自己写完后发现很多地方原作者写的真是精妙,把增加元素、移除元素抽离出来,以及写法的精简、效率的考虑(利用索引构造结点处判断index是否小于size>>1),自己受益匪浅。
package chain;
import java.io.Serializable;
import java.util.NoSuchElementException;
/**
* Created by benjamin on 1/18/16.
*/
public class LinkedList<E> implements Serializable {
private static final long serialVersionUID = -5635851059340344485L;
/**
* 结点类
* @param <E>
*/
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E item, Node<E> next) {
this.prev = prev;
this.item = item;
this.next = next;
}
}
/** 第一个结点的引用 **/
transient Node<E> first;
/** 最后一个结点的引用 **/
transient Node<E> last;
/** list中元素的数目 **/
transient int size = 0;
/** 操作次数 **/
transient int modCount = 0;
public LinkedList() {
}
/**
* 把传入的元素变为第一个元素
* @param e
*/
private void linkFirst(E e) {
Node<E> f = first;
Node<E> newNode = new Node<>(null, e, f);
if (f == null)
last = newNode;
else
f.prev = newNode;
first = newNode;
size++;
modCount++;
}
/**
* 在最后面插入元素
* @param e
*/
private void linkLast(E e) {
Node<E> l = last;
Node<E> newNode = new Node<>(l, e, null);
if (l == null)
first = newNode;
else
l.next = newNode;
last = newNode;
size++;
modCount++;
}
/**
* 在指定结点之前插入元素
* @param e
* @param succ 指定结点
*/
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++;
modCount++;
}
/**
* 取消第一个结点的引用
*/
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--;
modCount++;
return element;
}
/**
* 取消最后一个结点的引用
* @param l
* @return 最后一个结点的值
*/
private E unlinkLast(Node<E> l) {
final E element = l.item;
final Node<E> pred = l.prev;
l.item = null;
l.prev = null;
last = pred;
if (pred == null)
first = null;
else
pred.next = null;
size--;
modCount++;
return element;
}
/**
* 取消结点x的引用
* @param x
* @return 取消的结点元素
*/
E unlink(Node<E> x) {
final E element = x.item;
final Node<E> next = x.next;
final Node<E> pred = x.prev;
// 如果是第一个
if (pred == null)
first = next;
else {
pred.next = next;
x.prev = null;
}
//如果是最后一个
if (next == null)
last = pred;
else {
next.prev = pred;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
/**
* 取得第一个元素
* @return 第一个元素
*/
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
/**
* 取得最后一个元素
* @return 最后一个元素
*/
public E getLast() {
final Node<E> l = last;
if (last == null)
throw new NoSuchElementException();
return l.item;
}
/**
* 删除第一个元素
* @return 第一个被删除的元素
*/
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
/**
* 删除最后一个元素
* @return 最后一个被删除的元素
*/
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
/**
* 增加一个元素到list的第一个位置
* @param e
*/
public void addFirst(E e) {
linkFirst(e);
}
/**
* 增加一个元素到list的结尾
* @param e
*/
public void addLast(E e) {
linkLast(e);
}
/**
* 增加一个元素(默认增加在结尾)
* @param e
* @return true
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* 删除list中存在传入的对象
* @param o
* @return 如果改变了list,返回true,否则false
*/
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;
}
/**
* 清空list,所有引用对象置为null
*/
public void clear() {
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.prev = null;
x.next = null;
x = next;
}
first = null;
last = null;
size = 0;
modCount++;
}
/**
* 获取索引处的元素值
* @param index 索引
* @return 元素值
*/
public E get(int index) {
checkIndex(index);
return node(index).item;
}
/**
* 替换索引处的值为element
* @param index 索引
* @param element 新值
* @return 旧值
*/
public E set(int index, E element) {
checkIndex(index);
Node<E> oldNode = node(index);
E oldValue = oldNode.item;
oldNode.item = element;
return oldValue;
}
/**
* 在指定索引的地方插入元素element,原来的元素以及之后的元素后移
* @param index 插入元素的索引
* @param element 插入的元素
*/
public void add(int index, E element) {
checkIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
/**
* 移除索引处的元素
* @param index 索引
* @return 删除的元素
*/
public E remove(int index) {
checkIndex(index);
return unlink(node(index));
}
/**
* 获取对象在list中的索引
* @param o 要查找的对象
* @return 如果找到了对象,返回对应的索引值,否则返回-1
*/
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
/**
* 是否包含元素
* @param o
* @return 如果包含返回true
*/
public boolean contains(Object o) {
return indexOf(o) != -1;
}
/**
* 返回list中元素的大小
* @return 元素的大小
*/
public int getSize() {
return size;
}
/**
* 根据索引得到结点
* @param index 索引
* @return 结点
*/
Node<E> node(int index) {
// 如果是大小的一半之前,正序查找,否则倒序查找,提高效率
if (index < (size >> 1)) {
Node<E> f = first;
for (int i = 0; i < index; i++)
f = f.next;
return f;
} else {
Node<E> l = last;
for (int i = size - 1; i > index; i--)
l = l.prev;
return l;
}
}
/**
* 检查索引是否正确,不正确抛出 IndexOutOfBoundsException 异常
* @param index
*/
private void checkIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* 越界的错误信息
* @param index 越界的错误索引
* @return 越界的msg
*/
private String outOfBoundsMsg(int index) {
return "Index: " + index + ", Size: " + size;
}
/**
* 检查索引是否没有溢出
* @param index 索引
* @return 如果索引正确返回true
*/
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
}