目录
01 简介
- 由于存在尾节点,直接是使用last可以访问,双向链表的存在使得访问更加容易一些,通过它的大小和size一半进行比较.
- 将原始的节点进行添加的过程,添加头节点和尾部节点.
private LinkedList.Node<E> first; private LinkedList.Node<E> last; public LinkedList() { }
- 通过索引的方法进行寻找的方式是如下所示:
private LinkedList.Node<E> node(int index) {
this.rangeCheck(index);
LinkedList.Node node;
int i;
if (index < this.size >> 1) {
node = this.first;
for(i = 0; i < index; ++i) {
node = node.next;
}
return node;
} else {
node = this.last;
for(i = this.size - 1; i > index; --i) {
node = node.prev;
}
return node;
}
}
02 clear
- java虚拟机之中存在gc root,即便两个对象是存在指针指着的,也可能是被清理掉的.
- 情况一:栈指针之中的局部变量(局部变量指向的对象)
public void clear() { this.size = 0; this.first = null; this.last = null; }
03 add
- 插入节点的过程(考虑特殊位置)
- 代码流程
04 remove
- 删除元素的操作,进行断线处理
- 代码流程
05 接口测试
- 重写toString方法,可以使用System.out.println的方式进行测试.
public String toString() { StringBuilder sb = new StringBuilder(); if(prev != null) { sb.append(prev.element); }else{ sb.append("null"); } sb.append("_").append(element).append("_"); if(next != null) { sb.append(next.element); }else{ sb.append("null") } return sb; }
06 总结
- 单向链表和双向链表进行一个比较,操作数量会大大减少.(双向链表的优势)
- 双向链表和动态数组进行比较的过程.
- java官方在双向链表进行clear的时候,将节点之间的链条都是清空了.原因是由于迭代器的存在,清空clear的时候,可能是迭代器正在使用里面链条的节点,可能是不会进行销毁的.因此,最好是是将所有的链条都断开.(非必须)
07 单向循环链表 add
- 单向循环链表示意图
- 特殊情况:①当向index位置是0的地方进行插入的时候,是需要进行相应的一个注意,first指向插入的节点,最后一个位置的节点,指向这一个位置的节点.②没有节点的存在,在index为0的地方进行插入节点.如下图所示:
- 代码段是如下所示:
public void add(int index, E element) { rangeCheckForAdd(index);//检查index是否正确 if(index == 0) { first = new Node<>(element,first);//创建一个新的节点,first指向这个节点 //拿到最后一个节点 Node<E> last = (size == )?first:node(size-1); last.next = first; }else{ Node<E> prev = node(index-1); prev.next = new Node<>(element,prev.next); } size++; }
08 单向循环链表 remove
- 删除操作也是需要注意0节点的过程.
- 注意的情况:①如果要还删除的第一个节点的话,这里应当注意的是需要提前拿到最后一个节点,防止出现越界的情况.②只有一个元素的情况(直接进行滞空处理),如下图所示:
- 代码段是如下所示:
public E remove(int index) { rangeCheck(index); Node<E> node = first; if(index == 0)//当index=0的时候,需要注意两种情况 { if(size == 1) { first = null; }else { Node<E> last = node(size - 1); first = first.next; last.next = first; } } else{ Node<E> prev = node(index - 1); node = prev.next; prev.next = node.next; } size--; return node.element; }
09 约瑟夫问题
- 所谓的约瑟夫问题就是进行一个枪毙的过程,比如说:每隔两个人就是会枪杀一个人,最终活下来的那个人就是最幸运的人。如下图所示:对于上述的问题,我们需要得出元素退出的顺序。
- 如何进行设计,如下所示:
public class Main { public static void josephus(){ CircleLinkedList<Integer> list = new CircleLinkedList<>(); for(int i = 1; i <= 8; i++){ list.add(i); } list.reset(); //指向头结点 while(!list.isEmpty()){ list.next(); list.next(); System.out.println(list.remove()); } } public static void main(String[] args) { josephus(); } }
10 静态链表
- 早期的编程语言是没有指针的,如何实现相应的链表功能?
通过数组来模拟链表,称为静态链表;数组的每个元素存放2个数据:值、下一个元素的索引;数组0位置存放的是头结点信息。
执行过程是如下所示:
- 如果数组的每个元素只能存放1个数据?那就使用两个数组,一个数组存放索引关系,一个数组存放值。
11 ArrayList的优化思路
- 之前的ArrayList删除0位置的元素的时候,所有的数据需要进行一个移动。解决方式可以通过如下解释方式,加入一个first指针的方式。
- 插入元素的优化方式。在插入的过程之中,通过对比前后元素的数量,选取少的一方元素进行移动,这样可以大大减少挪动移动的次数,原理是类似于LinkedList。