链表
1.基本介绍:
-
链表是以结点的方式来存储。
-
每个节点包含data域,next域:指向下一个节点。
-
如图:发现链表的各个节点不一定是连续存储
-
链表分带头结点的和没有头节点的链表,根据需求来确定。
-
链表可分为单链表和双链表
-
单向链表结构图:
-
-
这里就是一个 size 为3的单链表
-
其中,在每一个结点类有两个东西:一个就是当前储存的值value,另一个就是指向下一个结点的指针—>注意这里先思考一下这个指针为啥能指向下一个元素呢?[思考完毕点这里🔜!](##2.Java 链表结点连接的原理)
Ctrl+点击跳转
-
-
2.快速入门
-
直接上代码 看不懂没关系混个眼熟
-
public class SingleList{ private Node head = null; //创建一个头指针用来指向链表的第一个结点,当然你也可以不要,有这个头指针对一系列操作更方便。 //创建一个类来封装结点 //大家想想链表是不是由多个结点组成,他们之间是一种组合关系,所以做成外部类跟内部内的关系。 //好处:对外隐藏细节,减轻调用者操作量。 private static class Node { 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(1,null); 下面的两种情况都能处理 //2.链表不为空 head = new Node(value, head); } //尾部添加 //1.找到尾部元素 private Node findLast() { if (head == null) { return null; } Node p = head; // 创建一个指针指向头结点去遍历。 while (p != null) { p = p.next; } return p; } //2.创建按删除的方法 public void addLast(int value){ Node last = findLast(); if (last == null){ addFirst(value); return; } last.next = new Node(value,null); } //两种遍历方式 //1.while循环 public void loop(Consumer<Integer> consumer) { Node p = head; //指向要处理的对象 while (p != null) { consumer.accept(p.value); p = p.next; //更新当前结点 } } //2.for循环 public void loop2(Consumer<Integer> consumer) { for (Node p = head; p != null; p = p.next) { consumer.accept(p.value); } } }
-
这只是在头部和尾部添加元素,这是基础入门,后面熟悉后再说插入。🛴
3.Java 链表结点连接的原理
-
首先我们来看一个平时经常用到的表达
-
Person p = new Person;
这明细就是创建了一个Person
类对象引用命名为p
。 在这个=
的左边是对象引用,所以存放在栈中,而右边才是真正new
出来的对象所以在堆里面。我们可以通过p
指向的地址来找到这个对象。 -
eg:
SingleList list = new SingleList(); list.addFirst(2); list.addLast(54); list.addLast(2322); list.loop2(value->{ System.out.println(value); });
-
我们从第二句话开始看 当执行到了
list.addFirst(2332);
时候-
-
这个能称为链表吗? 很明显不能 ,因为它只有数字没有链子链接啊。那怎样才能链接起来呢?
-
回到刚刚的``Person`实例来看
-
-
new Person在堆开辟的空间地址为0x666 这个时候p就会去指向0x666 。此时
Person p = new Person =0x666
。通过这个可以得出结论当我对象引用持有一个内存地址值,那么,此时这个对象引用就会指向下一个结点,并且还是同一个数据类型Person
,在去看看上面,我们想让第一个结点指向下一个。 -
此时会是这个样子
-
我们在掉过头来看第一步
SingleList list = new SingleList();
会是这个样子 -
结合Person内存图实例来看 就会很快想清楚原因是什么了。
-
-
-
4.插入链表
-
有了结点连接的原理的基础我们再来crud就很清晰了,只要思绪不乱。
-
在 快速入门 中我们已经讲了头尾结点的插入,很简单就不再多做赘述。接下来讲一讲中间插入
-
先思考:怎样才能在中间加一个元素呢?我们可以根据索引来,先查找到要添加的位置的上一个结点,然后再进行插入操作
-
1.找到索引位置对应的方法
-
private Node findNode(int index) { int i = 0; for (Node p = head; p != null; p = p.next,i++){ if (i == index){ return p; } } return null; }
-
2.往索引位置加入一个新节点
-
public void insert(int index,int value){ if (index == 0){ addFirst(value); //这里表示如果索引为零代表链表还没有结点,我们就可以调用上面在头部添加结点的方法。 return; } Node prev = findNode(index - 1);//找到上一个结点 if (prev == null){ throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index)); //如果给的索引不合理就抛出一个异常。 } prev.next=new Node(value,prev.next); //这里让上一个结点指向新的结点 } throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index)); //如果给的索引不合理就抛出一个异常。 } prev.next=new Node(value,prev.next); //这里让上一个结点指向新的结点 }
差不多就这样了。。
-