- 数组,栈,队列底层都是静态数组
- 链表是真正的动态数据结构
链表描述
- 链表
- 数据存在节点(node)中,(Node next)指向下一个节点的引用
- 优点:
- 缺点:
- 丧失了随机访问的能力
- 查询慢,没有索引,需要逐个查询
创建链表
1.创建一个动态链表了
public class MyLinked<T> {
}
2.创建一个节点(内部类)
- 在链表中,每一个节点都有一个指针和一个数据
- 顺便重写节点toString,方便后续调用
private class Node {
T ele;
Node next;
public Node(T ele) {
this.ele = ele;
next = null;
}
public Node() {
this(null);
}
@Override
public String toString() {
return ele.toString();
}
}
3.初始化链表
- size表示链表存储元素
- 定义链表的虚拟头结点
- 这个相当于null,实际不存在
- 虚拟头节点是解决在头部添加节点的特殊处理
- 若没有虚拟头节点,一定要注意链表为空的情况
- 链表的构造方法
private int size;
private Node dummyHead;
public MyLinked() {
size = 0;
dummyHead = new Node(null);
}
public int getSize() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
4.链表的增
- 引用上述的头节点来解决添加头部元素问题,
- 若没有头结点,则遍历为0的位置添加头节点,只能添加到第二个位置,无法绑定,
- 在任意位置添加节点,
- 引入指针pre,重点也是找到待添加元素的前一个节点 pre
- 第一步先判断链表是否为空
- 创建一个新节点node,将待插入的值存入节点内
- 将指针pre指向虚拟头节点
- 然后开始遍历,找到待插入的节点的前一个节点(头节点)
- node.next = pre.next;
pre.next = node;
- 新节点的后继指向待插入节点的头节点的后继
- 待插入节点的头节点的后继指向新节点
- 注:
- for (int i = 0; i < index; i++) {
pre = pre.next;
}
- 这里必须是小于index,只有小于 index才能确定是待插入节点的头节点
- node.next = pre.next;
pre.next = node;
- 顺序不能乱
- 顺序乱了,就变成待插入节点的头节点的后继指向新节点,新节点的后继指向自己了,就无法添加成功
public void addHead(T ele) {
add(ele, 0);
}
public void add(T ele, int index) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("index is error!!");
}
Node node = new Node(ele);
Node pre = dummyHead;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
node.next = pre.next;
pre.next = node;
size++;
}
public void addTail(T ele) {
add(ele, size);
}
5.链表遍历
- 实际就是输出,重写toString
- 创建一个新节点cur ,让他指向虚拟节点的后继(可以理解为是索引为0的节点)
- 在开始进行循环,如果cur节点的后继不是空,就一直添加,为空则跳出循环
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append("HEAD ");
Node cur = dummyHead.next;
while (cur != null) {
result.append(cur.ele + "--->");
if (cur.next == null) {
result.append("NULL");
}
cur = cur.next;
}
re