单链表
单链表在内存中不像数组那样,拥有连续的内存空间,内存空间是碎片化的
单链表主要是在每个节点中保存下一个节点的内存地址,便于获取下一个节点的内容
添加的时候,直接找到要添加的位置的前一个节点,然后将引用指向要添加的内容
例如在下标为1的位置添加一个59
只需要找到下标为0的位置的节点,将该节点的下一个引用指向新的节点,然后新的节点的下一个引用指向原来下标为1的节点。
不需要像动态数组那样,将后面所有的节点向后移动一个位置
public class SingleLinkedList<E> {
private static final int ELEMENT_NOT_FOUNT = -1;
private int size = 0;
private Node<E> head;
private static class Node<E> {
E element;
Node<E> next;
public Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}
/**
* 清除所有元素
*/
public void clear() {
head = 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 == 0) { // 从0位置插入,需要将头指向新的节点,新节点的下一个引用为原来的头
head = new Node<>(element, head);
} else { // 从其他位置插入,获取要插入位置的前一个节点,将前一个节点的引用指向新的节点,新的节点的引用指向原来index位置的节点
Node<E> prev = node(index - 1);
prev.next = new Node<>(element, prev.next);
}
size++;
}
/**
* 删除index位置的元素
*
* @param index
* @return
*/
public E remove(int index) {
checkRange(index);
Node<E> node = head;
if (index == 0) { // 删除第一个节点,直接将头节点指向原来头节点的下一个
head = head.next;
} else { // 删除其他节点,获取要删除节点的前一个节点,该节点的引用指向下下(两个)个节点
Node<E> prev = node(index - 1);
node = prev.next;
prev.next = node.next;
}
size--;
return node.element;
}
/**
* 查看元素的索引
*
* @param element
* @return
*/
public int indexOf(E element) {
if (element == null) { // 处理传入的参数为null的情况
Node<E> node = head;
for (int i = 0; i < size; i++) {
if (node.element == null) {
return i;
}
node = node.next;
}
} else {
Node<E> node = head;
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);
Node<E> node = head;
for (int i = 0; i < index; i++) {
node = node.next;
}
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 = head;
for (int i = 0; i < size; i++) {
if (i != 0) {
sb.append(", ");
}
sb.append(node.element);
node = node.next;
}
sb.append("]");
return sb.toString();
}
public static void main(String[] args) {
SingleLinkedList<Integer> list = new SingleLinkedList<>();
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
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 == 0) { // 从0位置插入,需要将头指向新的节点,新节点的下一个引用为原来的头
Node<E> newHead = new Node<>(element, head);
// 当链表为空时,将头指向新节点,新节点的引用指向其本身
Node<E> last = (size == 0) ? newHead : node(size - 1);
last.next = newHead;
head = newHead;
} else { // 从其他位置插入,获取要插入位置的前一个节点,将前一个节点的引用指向新的节点,新的节点的引用指向原来index位置的节点
Node<E> prev = node(index - 1);
prev.next = new Node<>(element, prev.next);
}
size++;
}
/**
* 删除index位置的元素
*
* @param index
* @return
*/
public E remove(int index) {
checkRange(index);
Node<E> node = head;
if (index == 0) { // 删除第一个节点,直接将头节点指向原来头节点的下一个
if (size == 1) {
head = null;
} else {
Node<E> last = node(size - 1);
head = head.next;
last.next = head;
}
} else { // 删除其他节点,获取要删除节点的前一个节点,该节点的引用指向下下(两个)个节点
Node<E> prev = node(index - 1);
node = prev.next;
prev.next = node.next;
}
size--;
return node.element;
}