一、链表特点以及原理
查询慢,增删快。
这个分为两种情况,在头尾增加,查询、和在中间增删,查询;
在头尾进行:
在头尾进行增删,直接在存储头尾对象的first、last添加上新加的元素即可(效率极高);
在头尾进行查询,因为内部优化,大于一半从后往前查询,效率方面也是可以的。
在中间进行:
在中间查询,需要把位数走一遍,也就是需要查询索引5的内容,就要创建(5-1)次的对象,时间及其慢。
在中间删除,要把上面查询的步骤走一遍以后,在把指定的元素删除,再把其前后两个对象(链表),连接起来。
二、单个实现原理解析
1、添加元素add方法(添加内容)
分为添加第一个元素和其它元素两种:
第一个元素: 存储的首个元素(first)和末尾元素(last)都是当前设置的元素;
其它元素: 先将两个元素进行绑定、设置最后一个元素的next为null,存储的最后一个元素为当前元素;
2、toString方法
利用StringBuilder创建对象,从first元素递归获取每一个存储的对象(element)值,存储进StringBuilder中。
3、get方法(根据索引)
调用7、检测索引,调用4、获取指定对象,并从对象中获取元素(element)的值;
4、获取指定索引的对象(索引)
分两种情况,索引大于有效长度一半(size),从后侧开始检索,不满足从左侧;
根据当前对象存储的下一个对象的内容,进行递归判断,直到找到自己想要的次数对象为止;
5、remove方法(删除指定索引位置元素)
调用4、获取指定对象,分情况进行删除(1、开头;2、中间位置;3、末尾位置),长度减一(size–)
开头: 根据当前元素获取后一个元素对象,设置存储的首个元素(first)为后一个元素;
中间: 获取当前元素前后两个元素对象,并把这两个元素进行连接;
末尾: 根据当前元素获取前一个元素对象,设置存储的末尾元素(last)为前一个元素;
6、add方法(根据索引添加到指定位置)
调用4、获取指定对象,分情况进行添加(1、开头;2、中间位置;3、末尾位置),长度加一(size )
开头: 创建两个元素连接,设置存储的首个元素(first)为当前对象;
中间: 获取当前元素前一个元素对象,将前一个元素与当前元素连接,将当前元素与后一个元素连接;
末尾: 创建两个元素连接,设置存储的末尾元素(last)为当前对象;
7、索引检测
判断索引的数组内容进行判断,不满足则抛出运行时异常(RuntimeException)
三、完整增加步骤
1、创建基础结构,添加add、toString方法
Node存储元素对象
代码语言:javascript复制public class Node {
Node privious; //上一个节点
Node next; //下一个节点
Object element; //元素数据
public Node(Node privious, Node next, Object element) {
this.privious = privious;
this.next = next;
this.element = element;
}
public Node(Object element) {
this.element = element;
}
}
MyLinkedList工具类
代码语言:javascript复制public class MyLinkedList {
private Node first;
private Node last;
// 1、添加元素add方法
public void add(Object obj) {
Node node = new Node(obj);
if (first == null) {
first = node;
last = node;
} else {
// 元素的开头为上一个元素对象(最后一个对象)
node.privious = last;
// 元素的最后为null
node.next = null;
// 上一个元素设置为后一个对象
last.next = node;
// 设置最后元素为现在的对象
last = node;
}
}
// 2、toString方法
public String toString() {
StringBuilder sb = new StringBuilder("[");
Node temp = first;
while (temp != null) {
sb.append(temp.element ",");
temp = temp.next;
}
sb.setCharAt(sb.length() - 1, ']');
return sb.toString();
}
调用测试:
代码语言:javascript复制 public static void main(String[] args) {
MyLinkedList list = new MyLinkedList();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
String s = list.toString();
System.out.println(s);
}
}
难点解析:
是设置新创建的元素的对象里面的,前一个元素地址(privious)和后一个元素地址(next)
代码语言:javascript复制//元素的开头为上一个元素对象(最后一个对象)
node.privious = last;
//元素的最后为null
node.next = null;
是设置创建的上一个元素里面的,后一个元素地址(next)的值,其中last存储的上一个元素对象的地址值;
代码语言:javascript复制//上一个元素设置为后一个对象
last.next = node;
//设置最后元素为现在的对象
last = node;
2、增加get方法
增加size变量,并添加size的增加方法
代码语言:javascript复制 private int size;
// 1、添加元素add方法
public void add(Object obj) {
Node node = new Node(obj);
if (first == null) {
first = node;
last = node;
} else {
// 元素的开头为上一个元素对象(最后一个对象)
node.privious = last;
// 元素的最后为null
node.next = null;
// 上一个元素设置为后一个对象
last.next = node;
// 设置最后元素为现在的对象
last = node;
}
size ;
}
增加get方法
代码语言:javascript复制 // 3、get方法
public Object get(int index) {
if (index < 0 || index > size - 1) {
throw new RuntimeException("索引数字不合法" index);
}
Node temp = null;
if (index <= (size >> 1)) { //索引位置靠近与开头位置
temp = first;
for (int i = 0; i < index; i ) {
temp = temp.next;
}
} else { //索引位置靠近于末尾位置
temp = last;
for (int i = size - 1; i > index; i--) {
temp = temp.privious;
}
}
return temp.element;
}
3、get方法优化、增加remove方法
get方法优化
代码语言:javascript复制 // 3、get方法
public Object get(int index) {
if (index < 0 || index > size - 1) {
throw new RuntimeException("索引数字不合法" index);
}
//调用封装的getNode方法获取指定索引位置对象
Node temp = getNode(index);
return temp != null ? temp.element : null;
}
// 4、封装的获取指定索引的对象
public Node getNode(int index) {
Node temp = null;
if (index <= (size >> 1)) { //索引位置靠近与开头位置
temp = first;
for (int i = 0; i < index; i ) {
temp = temp.next;
}
} else { //索引位置靠近于末尾位置
temp = last;
for (int i = size - 1; i > index; i--) {
temp = temp.privious;
}
}
return temp;
}
增加remove方法
代码语言:javascript复制 // 5、remove方法
public void remove(int index) {
if (index < 0 || index > size - 1) {
throw new RuntimeException("索引数字不合法" index);
}
Node temp = getNode(index);
if (temp != null) {
Node before = temp.privious;
Node after = temp.next;
if (before != null) {
before.next = after;
}
if (after != null) {
after.privious = before;
}
// 被删除的元素是第一个元素时
if (index == 0) {
first = after;
}
// 被删除的元素时最后一个元素时
if (size == index - 1) {
last = before;
}
size--;
}
}
4、根据索引添加元素
代码语言:javascript复制// 6、add方法
public void add(int index,Object obj){
// 索引判断
if (index<0||index>size-1){
throw new RuntimeException("请输入指定范围索引 " index);
}
// 取指定索引元素
Node newNode = new Node(obj);
Node temp = getNode(index);
// 赋值
if (temp!=null){
Node before = temp.privious;
// 1、添加的元素在第一个时
if (index==0){
newNode.next=temp;
temp.privious=newNode;
first=newNode;
}
// 2、添加的元素在最后一个时
if (index==size-1){
newNode.privious=temp;
temp.next=newNode;
last=newNode;
size ;
return;
}
// 3、添加的元素在普通位置时
if (before!=null){
// 新建元素与前一个创建连接
before.next=newNode;
newNode.privious=before;
// 新建元素与后一个创建连接
newNode.next=temp;
temp.privious=newNode;
}
// 大小 1
size ;
}
}
5、增加索引检测、泛型
索引检测:
代码语言:javascript复制 // 7、索引检测
private void checkRange(int index) {
if (index < 0 || index > size - 1) {
throw new RuntimeException("索引数字不合法: " index);
}
}
泛型:
代码语言:javascript复制public class MyLinkedList<E> {
private Node first;
private Node last;
private int size;
// 1、添加元素add方法
public void add(E element) {
Node node = new Node(element);
if (first == null) {
first = node;
last = node;
} else {
// 元素的开头为上一个元素对象(最后一个对象)
node.privious = last;
// 元素的最后为null
node.next = null;
// 上一个元素设置为后一个对象
last.next = node;
// 设置最后元素为现在的对象
last = node;
}
size ;
}
四、完整版代码
代码语言:javascript复制public class MyLinkedList<E> {
private Node first; //存储第一个元素对象
private Node last; //存储最后一个元素对象
private int size; //有效的索引长度(存有值的位数)
// 1、添加元素add方法(添加内容)
public void add(E element) {
Node node = new Node(element);
if (first == null) {
first = node;
last = node;
} else {
// 元素的开头为上一个元素对象(最后一个对象)
node.privious = last;
// 元素的最后为null
node.next = null;
// 上一个元素设置为后一个对象
last.next = node;
// 设置最后元素为现在的对象
last = node;
}
size ;
}
// 2、toString方法
public String toString() {
StringBuilder sb = new StringBuilder("[");
Node temp = first;
while (temp != null) {
sb.append(temp.element ",");
temp = temp.next;
}
sb.setCharAt(sb.length() - 1, ']');
return sb.toString();
}
// 3、get方法(根据索引)
public E get(int index) {
// 检测索引
checkRange(index);
//调用封装的getNode方法获取指定索引位置对象
Node temp = getNode(index);
return temp != null ? (E)temp.element : null;
}
// 4、获取指定索引的对象
private Node getNode(int index) {
// 检测索引
checkRange(index);
Node temp = null;
if (index <= (size >> 1)) { //索引位置靠近与开头位置
temp = first;
for (int i = 0; i < index; i ) {
temp = temp.next;
}
} else { //索引位置靠近于末尾位置
temp = last;
for (int i = size - 1; i > index; i--) {
temp = temp.privious;
}
}
return temp;
}
// 5、remove方法(删除指定索引位置元素)
public void remove(int index) {
// 检测索引
checkRange(index);
Node temp = getNode(index);
if (temp != null) {
Node before = temp.privious;
Node after = temp.next;
if (before != null) {
before.next = after;
}
if (after != null) {
after.privious = before;
}
// 被删除的元素是第一个元素时
if (index == 0) {
first = after;
}
// 被删除的元素时最后一个元素时
if (size == index - 1) {
last = before;
}
size--;
}
}
// 6、add方法(根据索引添加到指定位置)
public void add(int index, E element) {
// 检测索引
checkRange(index);
// 取指定索引元素
Node newNode = new Node(element);
Node temp = getNode(index);
// 赋值
if (temp != null) {
Node before = temp.privious;
// 1、添加的元素在第一个时
if (index == 0) {
newNode.next = temp;
temp.privious = newNode;
first = newNode;
}
// 2、添加的元素在最后一个时
if (index == size - 1) {
newNode.privious = temp;
temp.next = newNode;
last = newNode;
size ;
return;
}
// 3、添加的元素在普通位置时
if (before != null) {
// 新建元素与前一个创建连接
before.next = newNode;
newNode.privious = before;
// 新建元素与后一个创建连接
newNode.next = temp;
temp.privious = newNode;
}
// 大小 1
size ;
}
}
// 7、索引检测
private void checkRange(int index) {
if (index < 0 || index > size - 1) {
throw new RuntimeException("索引数字不合法: " index);
}
}
}