三.链表
3.1 概述
链表是数据元素的线性集合,其每个元素都指向下一个元素,元素存储上并不连续。
可以分为:
-
单向链表,每个元素只知道其下一个元素是谁
-
双向链表,每个元素知道其上一个元素和下一个元素
-
循环链表,通常的链表尾节点 tail 指向的都是 null,而循环链表的 tail 指向的是头节点 head
链表内还有一种特殊的节点称为哨兵(Sentinel)节点,也叫做哑元( Dummy)节点,它不存储数据,通常用作头尾,用来简化边界判断,如下图所示
随机访问性能
根据 index 查找,时间复杂度 O(n)
插入或删除性能
- 起始位置:O(1)
- 结束位置:如果已知 tail 尾节点(双向链表)是 O(1),不知道 tail 尾节点(单向链表)是 O(n)
- 中间位置:根据 index 查找时间 + O(1) = O(n)
3.2 单向链表
实现以下链表方法:
- addFirst(int value):向链头添加
- 遍历
- addLast(int value):向链尾添加
- findLast():找最后一个节点
- get(int index):根据索引找对应结点的值
- insert(int index ,int value):向索引index位置插入元素
- findNode(int index):根据索引找到对应的结点
- removeFirst():删除第一个元素
- remove(int index):从index索引位置删除
// 单向链表
//链表由多个节点Node组成,即组合的关系
//所以可以考虑将Node写成内部类
public class SinglyLinkedList implements Iterable<Integer>{//看作整体链表
// 初始时,只有头指针没有节点
//所以头指针指向null
private Node head = null;//头指针,
// 节点类
private static class Node{//jing'tai
int value;//值
Node next;//下一节点指针
// 有参构造,目的是方便是赋初值
public Node(int value, Node next) {
this.value = value;
this.next = next;
}
}
// 向链头添加元素
public void addFirst(int value){
// 1.链表为空
// head = new Node(value, null);
// 2.链表非空(可以处理链表为空的情况)
//this.head为null,新增节点指向null,并作为新的this.head
//若this.head不为null,新增节点指向原来的this.head,并作为新的this.head
this.head = new Node(value,this.head);
}
// 遍历链表
// 遍历方式一
public void loop1(Consumer<Integer> consumer){
// 定义指针,默认指向头指针的位置
Node p = head;
while (p !=null){
// 打印指针p的值
consumer.accept(p.value);
// 指针向后移动
p = p.next;
}
}
// 遍历方式二
public void loop2(Consumer<Integer> consumer){
for (Node p = head; p != null; p = p.next){
consumer.accept(p.value);
}
}
// 遍历方式三,迭代器遍历
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
Node p = head;
@Override
public boolean hasNext() {
//hasNext():是否有下一个元素
return p != null;
}
@Override
public Integer next() {
// next():返回当前节点值,并指向下一个元素
int value = p.value;
p = p.next;
return value;
}
};
}
// 向链表尾部添加
// 首先需要找到最后一个节点
private Node findLast(){
//空链表情况,即只有一个头指针指向null
if (head == null){
return null;
}
Node p ;
for (p = head; p.next != null; p = p.next ){
}
return p;
}
// 在尾部添加
public void addLast(int value){
// 找打最后一个结点
Node last = findLast();
// 空链表情况,即没有结点,只有一个头指针指向null
//此时直接添加,相当于在头部添加
if (last == null){
addFirst(value);
return;
}
// 将最后结点的next指针指向添加的元素
//新添加的元素next指针指向null
last.next= new Node(value, null);
}
// 根据索引找到对应的结点
private Node findNode(int index){
int i = 0;
for (Node p = head ; p != null ; p = p.next,i++){
if(i == index){
//找到返回对应的结点
return p;
}
}
// 循环结束没有索引index对应的结点返回null
return null;
}
// 根据索引找对应结点的值
public int get(int index){
Node node = findNode(index);
if (node == null){
throw new IllegalArgumentException(
String.format("index[%d] 不合法",index));
}
return node.value;
}
// 向索引index位置插入元素
public void insert(int index,int value){
if (index == 0){
addFirst(value);
// 此处return作用是让下面代码不再执行
return;
}
// 找到插入位置的前一个结点
Node prev = findNode(index - 1);
if (prev == null){
throw new IllegalArgumentException(
String.format("index[%d] 不合法",index));
}
prev.next = new Node(value, prev.next);
}
// 删除第一个元素
public void removeFirst(){
// 空链表情况
if (head == null){
throw new IllegalArgumentException(
String.format("index[%d] 不合法",0));
}
// head:指向第一个结点,即 head为第一个结点
// head.next:表示第二个结点
head = head.next;
}
// 根据index索引位置删除
public void remove(int index){
if (index == 0){
removeFirst();
return;
}
// index的上一个节点
Node prev = findNode(index-1);
if (prev == null){
throw new IllegalArgumentException(
String.format("index[%d] 不合法",index));
}
// 被删除的节点
Node removed = prev.next;
if (removed == null){
throw new IllegalArgumentException(
String.format("index[%d] 不合法",index));
}
prev.next = removed.next;
}
}
========================================================
public class Test {
public static void main(String[] args) {
SinglyLinkedList list = new SinglyLinkedList();
// 向头部添加元素
list.addFirst(1);
list.addFirst(2);
list.addFirst(3);
// 方式一遍历
list.loop1(value->{
System.out.println(value);
});// 3 2 1
// 方式二遍历
list.loop2(value->{
System.out.println(value);
});// 3 2 1
// 方式三遍历
for (Integer value : list) {
System.out.println(value);
}
Iterator<Integer> it = list.iterator();
while (it.hasNext()){
Integer next = it.next();
System.out.println(next);
}
// 向尾部添加
SinglyLinkedList list2 = new SinglyLinkedList();
list2.addLast(1);
list2.addLast(2);
list2.addLast(3);
// 遍历
list2.loop2(Value->{
System.out.println(Value);
});// 1 2 3
// 根据索引找值
int i = list2.get(0);
System.out.println(i);//1
// 向索引index位置插入元素
list2.insert(2,6);
list2.loop2(Value->{
System.out.println(Value);// 1 2 6 3
});
System.out.println("删除第一个元素");
// 删除第一个元素
list2.removeFirst();
list2.loop2(Value->{
System.out.println(Value);//2 6 3
});
System.out.println("删除index索引位置元素");
//删除index索引位置元素
list2.remove(0);
list2.loop2(Value->{
System.out.println(Value);//6 3
});
}
}
- Node 定义为内部类,是为了对外隐藏实现细节,没必要让类的使用者关心 Node 结构
- 定义为 static 内部类,是因为 Node 不需要与 SinglyLinkedList 实例相关多个SinglyLinkedList实例能共用 Node 类定义