链表
链表是链式存储的集合数据结构,节点与节点之间通过记录地址来联系,对于经常需要增删的数据结构,以链表为基础实现会比数组实现高很多。
分析要求
1. 支持泛型
2. 可迭代
3. 尽量高效
其实要实现一个节点,只需要声明一个包含存储对象和存储地址的抽象数据类型即可,如下:
private class Node<Item> {
public Node(Item item) {this.item = item;}
private Item item;
private Node next;
}
接口设计
public interface LinkedListInterface<Item> extends Iterable<Item> {//支持迭代
/** 插入节点 */
void add(int idx, Item item);
/** 删除节点 */
Item remove(int idx);
}
单向实现
public class LinkedList<Item> implements LinkedListInterface<Item> {
private Node firstNode = new Node(null);//第一个节点不存储数据,只是作为链表开始的标志
public int length = 0;//链表长度
public void add(int idx, Item item) {//插入
Node pre = getPreNodeByIndex(idx);
Node node = new Node(item);
node.next = pre.next;
pre.next = node;
length++;
}
public Item remove(int idx) {//移除
Node pre = getPreNodeByIndex(idx);
Node del = pre.next;
if (del == null) throw new IndexOutOfBoundsException("" + idx);
pre.next = pre.next.next;
length--;
return del.item;
}
public Item get(int idx) {//获取
Node curr = getPreNodeByIndex(idx).next;
if (curr == null) throw new IndexOutOfBoundsException("" + idx);
return curr.item;
}
public Iterator<Item> iterator() {//实现迭代
return new Iterator<Item>() {
private Node currNode = firstNode;
public boolean hasNext() {return currNode.next != null;}
public Item next() {
currNode = hasNext() ? currNode.next : currNode;
return currNode.item;
}
public void remove() {}
};
}
/** 获取该索引所对应的节点的前驱 */
private Node getPreNodeByIndex(int idx) {
if (idx < 0) throw new IndexOutOfBoundsException("" + idx);
Node pre = firstNode;
for (int i = 0; i < idx; i++) {
pre = pre.next;
if (pre == null) throw new IndexOutOfBoundsException("" + idx);
}
return pre;
}
}
双向实现
相比较于单向实现,双向实现中的每一个节点不仅需要记录它的后继节点,还应记录它的前驱节点的地址,因此空间开销会变大,但是可以解决单向链表操作链表尾端时低效的问题,不用遍历至链表尾部即可操作。
//TODO 抽空写