个人Github地址java
小码哥《恋上数据结构与算法》笔记node
iOS大厂面试高频算法题总结github
参考:小码哥数据结构与算法(二): 链表算法
链式存储
的线性表, 全部元素的内存地址不必定是连续的。LinkedList
类,用来管理链表数据,其中的size
属性记录存储数据的数量,first
属性引用链表的第0
个元素。Node
,其中的element
属性用于存储元素,next
属性用于指向链表中的下一个节点。public class LinkedList<E> {
private int size;
private Node<E> first;
// 元素的数量
int size();
// 是否为空
boolean isEmpty();
// 是否包含某个元素
boolean contains(E element);
// 添加元素到最后面
void add(E element);
// 返回index位置对应的元素
E get(int index);
// 设置index位置的元素
E set(int index, E element);
// 往index位置添加元素
void add(int index, E element);
// 删除index位置对应的元素
E remove(int index);
// 查看元素的位置
int indexOf(E element);
// 清除全部元素
void clear();
// 私有类, 链表中的节点
private class Node<E> {
E element;
Node<E> next;
// 构造方法
public Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}
}
复制代码
空间
属性,来决定这个数组的容量
。但链表元素是在添加时才建立的,内存地址不必定是连续的。因此链表不须要在单独设计构造方法,使用默认构造方法便可。size加1
。当前链表没有数据,新节点拼接到first
和当前链表有数据,新节点拼接到最后的节点
。public void add(E element) {
// 当first等于null时, 说明此事没有节点, 因此first引用新节点
if (first == null) {
first = new Node<E>(element, null);
}
// 当fitst不等于null时, 说明链表中有节点, 此时获取最后一个节点, 并将该节点的next指向新节点
else {
Node<E> node = node(size - 1);
node.next = new Node<E>(element, null);
}
size++;
}
复制代码
变动插入位置前一个元素next指针指向
,插入指定位置便可。插入到0的位置,使用first指向新节点
和插入到非0位置,找到前一个节点进行处理
两种状况。不能小于0, 也不能大于等于size
,因此咱们在插入元素以前须要先进行索引检查。protected void outOfBounds(int index) {
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
}
protected void rangeCheck(int index) {
if (index < 0 || index >= size) {
outOfBounds(index);
}
}
复制代码
插入
元素代码以下:public void add(int index, E element) {
// 检查索引是否越界
rangeCheckForSize(index);
// 当插入到0的位置时
if (index == 0) {
// 将first指向新节点, 新节点的next指向first以前指向的节点
first = new Node<E>(element, first.next);
}else {
// 找到指定位置前面的节点
Node<E> prev = node(index - 1);
// 将前面节点的next指向新节点, 新节点的next指向prev以前指向的节点
prev.next = new Node<>(element, prev.next);
}
size++;
}
复制代码
添加
元素也能够简写:public void add(E element) {
// 元素添加到size位置, 即添加到最后面
add(size, element);
}
复制代码
(delete_node)
的前一个节点(pre_node)
,而后经过变动(pre_node)节点next指针指向删除节点(delete_node)的下一个节点
便可,而后size
减1
。第0个元素
,若是是,则使用first指向第1个节点
。public E remove(int index) {
// 检查索引是否越界
rangeCheck(index);
// 记录须要删除的节点
Node<E> old = first;
// 当删除第0个元素时, 将first的next指向索引为`1`的节点便可
if (index == 0) {
first = first.next;
}else {
// 找到前一个元素
Node<E> prev = node(index - 1);
// 记录须要删除的节点
old = prev.next;
// 将prev的next指向须要删除节点的后一个节点
prev.next = old.next;
}
// size-1
size--;
// 返回删除的元素
return old.element;
}
复制代码
first
指向null
,释放链表全部node,同时size
置为0
便可。public void clear() {
first = null;
size = 0;
}
复制代码
private Node<E> node(int index) {
//越界判断
rangeCheck(index);
Node<E> node = first;
for (int i = 0; i < index; i++) {
node = node.next;
}
return node;
}
复制代码
node
节点的element
便可。public E set(int index, E element) {
// 找到对应节点, node方法中已经判断了索引是否越界
Node<E> node = node(index);
// 记录旧元素
E old = node.element;
// 覆盖元素
node.element = element;
// 返回旧元素
return old;
}
复制代码
public E get(int index) {
// node方法中已经判断了索引是否越界
return node(index).element;
}
复制代码
element
为null
,则须要分两种状况处理。private static final int ELEMENT_ON_FOUND = -1;
public int indexOf(E element) {
// 取出头结点
Node<E> node = first;
// 当element为null时的处理
if (element == null) {
// 遍历节点, 找到存储为null的节点, 返回索引
for (int i = 0; i < size; i++) {
if (node.element == null) return i;
node = node.next;
}
}else {
for (int i = 0; i < size; i++) {
// 遍历节点, 找到存储的元素与指定元素相等的节点, 返回索引
if (element.equals(node.element)) return i;
node = node.next;
}
}
// 没有找到元素对应的节点, 返回ELEMENT_ON_FOUND
return ELEMENT_ON_FOUND;
}
复制代码
size
的值。public int size() {
return size;
}
复制代码
size
是否等于0
便可。public boolean isEmpty() {
return size == 0;
}
复制代码
ELEMENT_ON_FOUND
便可。public boolean contains(E element) {
return indexOf(element) != ELEMENT_ON_FOUND;
}
复制代码
public String toString() {
StringBuilder string = new StringBuilder();
string.append("size = ").append(size).append(", [");
Node<E> node = first;
for (int i = 0; i < size; i++) {
if (i != 0) {
string.append(",");
}
string.append(node.element);
node = node.next;
}
string.append("]");
return string.toString();
}
复制代码
到此为止,咱们成功的实现了链表。markdown