双向链表
双向链表与链表的区别在于,双向链表有指向前一个节点的指针,可以获取到前一个节点。
获取值的时候,可以判断index下标所在的位置,从而决定从首节点开始查找还是从尾节点开始查找
public class LinkedList<E> {
private static final int ELEMENT_NOT_FOUNT = -1;
private int size = 0;
private Node<E> first;
private Node<E> last;
private static class Node<E> {
E element;
Node<E> prev;
Node<E> next;
public Node(E element, Node<E> prev, Node<E> next) {
this.element = element;
this.prev = prev;
this.next = next;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (prev != null) {
sb.append(prev.element);
} else {
sb.append("null");
}
sb.append("_").append(element).append("_");
if (next != null) {
sb.append(next.element);
} else {
sb.append("null");
}
return sb.toString();
}
}
/**
* 清除所有元素
*/
public void clear() {
first = null;
last = null;
size = 0;
}
/**
* 元素的数量
*
* @return
*/
public int size() {
return size;
}
/**
* 是否为空
*
* @return
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 是否包含某个元素
*
* @param element
* @return
*/
public boolean contains(E element) {
return indexOf(element) != ELEMENT_NOT_FOUNT;
}
/**
* 添加元素到尾部
*
* @param element
*/
public void add(E element) {
add(size, element);
}
/**
* 获取index位置的元素
*
* @param index
* @return
*/
public E get(int index) {
return node(index).element;
}
/**
* 设置index位置的元素
* 获取到该位置的节点,直接覆盖原来的值
*
* @param index
* @param element
* @return 原来的元素ֵ
*/
public E set(int index, E element) {
Node<E> node = node(index);
E oldElement = node.element;
node.element = element;
return oldElement;
}
/**
* 在index位置插入一个元素
*
* @param index
* @param element
*/
public void add(int index, E element) {
checkRangeForAdd(index);
if (index == size) { // 往最后面添加元素
Node<E> oldLast = last;
last = new Node<>(element, oldLast, null);
if (oldLast == null) { // 链表为空,往链表中添加第一个元素 size == 0 index == 0
first = last;
} else {
oldLast.next = last;
}
} else {
Node<E> next = node(index);
Node<E> prev = next.prev;
Node<E> node = new Node<>(element, prev, next);
next.prev = node;
if(prev == null) { // index == 0
first = node;
} else {
prev.next = node;
}
}
size++;
}
/**
* 删除index位置的元素
*
* @param index
* @return
*/
public E remove(int index) {
checkRange(index);
Node<E> node = node(index);
Node<E> prev = node.prev;
Node<E> next = node.next;
if(prev == null) { // index == 0 删除首节点
first = next;
} else {
prev.next = next;
}
if (next == null) { // index == size - 1 删除尾节点
last = prev;
} else {
next.prev = prev;
}
size--;
return node.element;
}
/**
* 查看元素的索引
*
* @param element
* @return
*/
public int indexOf(E element) {
if (element == null) { // 处理传入的参数为null的情况
Node<E> node = first;
for (int i = 0; i < size; i++) {
if (node.element == null) {
return i;
}
node = node.next;
}
} else {
Node<E> node = first;
for (int i = 0; i < size; i++) {
if (element.equals(node.element)) { // 节点可能存储null,所以用传入的element调用equals方法,避免出现空指针异常
return i;
}
node = node.next;
}
}
return ELEMENT_NOT_FOUNT;
}
/**
* 获取对应下标的节点
* @param index
* @return
*/
private Node<E> node(int index) { // 遍历找到对应下标的节点,找到index的节点,循环执行的次数为index - 1次
checkRange(index);
if (index < (size >> 1)) {
Node<E> node = first;
for (int i = 0; i < index; i++) {
node = node.next;
}
return node;
} else {
Node<E> node = last;
for (int i = size - 1; i > index; i--) {
node = node.prev;
}
return node;
}
}
private void outOfBounds(int index) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
private void checkRange(int index) {
if (index < 0 || index >= size) {
outOfBounds(index);
}
}
private void checkRangeForAdd(int index) {
if (index < 0 || index > size) {
outOfBounds(index);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("size=").append(size).append(", [");
Node<E> node = first;
for (int i = 0; i < size; i++) {
if (i != 0) {
sb.append(", ");
}
sb.append(node);
node = node.next;
}
sb.append("]");
return sb.toString();
}
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(11);
list.add(22);
list.add(33);
list.add(44); // 11 22 33 44
list.add(1, 55); // 11 55 22 33 44
list.remove(0); // 55 22 33 44
list.set(2, 66); // 55 22 66 44
// list.clear();
System.out.println(list.indexOf(22));
System.out.println(list);
}
}
双向循环链表
与双向链表的区别在于,首节点的前一个节点指向尾节点,尾节点的下一个节点指向首节点。
在代码上主要的区别体现在添加和删除上
/**
* 在index位置插入一个元素
*
* @param index
* @param element
*/
public void add(int index, E element) {
checkRangeForAdd(index);
if (index == size) { // 往链表最后面添加节点
Node<E> oldLast = last;
last = new Node<>(element, oldLast, first);
if (oldLast == null) { // 链表为空,添加第一个节点 size == 0 index == 0
first = last;
first.next = first;
first.prev = first;
} else {
oldLast.next = last;
first.prev = last;
}
} else {
Node<E> next = node(index);
Node<E> prev = next.prev;
Node<E> node = new Node<>(element, prev, next);
next.prev = node;
prev.next = node;
if (next == first) { // 向链表的开头位置添加节点 index == 0
first = node;
}
}
size++;
}
/**
* 删除index位置的元素
*
* @param index
* @return
*/
public E remove(int index) {
checkRange(index);
return remove(node(index));
}
/**
* 删除传入的节点
*
* @param node
* @return
*/
private E remove(Node<E> node) {
if (size == 1) { // 链表中仅有一个节点
first = null;
last = null;
} else {
Node<E> prev = node.prev;
Node<E> next = node.next;
prev.next = next;
next.prev = prev;
if (node == first) { // 删除首节点
first = next;
}
if (node == last) { // 删除尾节点
last = prev;
}
}
size--;
return node.element;
}