1.链表的时间复杂度分析
查询元素时间复杂度
链表是不支持随机访问的,在链表中只有从头节点开始一个一个往下找,找到查询的元素为止,比如链表的长度为n,最好的情况是头节点就是需要找的元素,那么就是一个时间单元1*unit_time,,最坏的情况是最后一个节点是需要找的元素,那么就是n*unit_time,那么平均时间复杂度就是n/2*unit_time;根据大O推导出,时间复杂度为O(n);
删除元素时间复杂度
在链表中删除元素非常简单,链表中的每个节点都有一个指向下个节点的指针,我们只需要找到删除节点B的前一个节点A,把B的指针指向A的下一个节点C,比如:原来是A->B->C,删除后是A->C,非常简单,时间复杂度就是O(1).
2.代码演示
关于代码演示,做下说明,我们关注的是数据结构的特点以及基本实现,至于数据结构中稍微复杂一点的操作,这里不做演示,可以自己研究,花点时间,都是没问题的,这里基本就演示两个操作,添加和删除。
2.1最原始的实现
public class YLink<E> {
private Node head;
private int size;
/**
* 内部类,用来封装链表中每个节点的属性
*/
private class Node{
/**
* 链表中每个节点的值
*/
private E e;
/**
* 指向链表中下个节点的指针
*/
private Node next;
private Node(E e, Node next) {
this.e = e;
this.next = next;
}
}
public int size() {
return size;
}
/**
* 向链表中的头部添加新节点
* @param e
*/
public void add(E e) {
if (head == null) {
head = new Node(e, null);
} else {
head = new Node(e,head);
}
size++;
}
/**
* 删除指定元素
* @param e
*/
public void remove(E e) {
if (head.e.equals(e)) {
head = head.next;
} else {
Node temp = head;
while (temp.next != null) {
if (e.equals(temp.next.e)) {
temp.next = temp.next.next;
break;
}
temp = temp.next;
}
}
size--;
}
@Override
public String toString() {
StringBuilder sbr = new StringBuilder();
Node temp = head;
while (temp != null) {
sbr.append(temp.e).append("->");
temp = temp.next;
}
String s = sbr.toString();
String result = s.substring(0, s.lastIndexOf("->"));
return result;
}
}
说明:在上面代码中,可以发现,在添加和删除的时候,需要对头节点做一个特殊判断,感觉不是很爽,有没有办法去掉这个判断,让代码逻辑更简洁呢,答案当然是有的,我们可以增加一个虚拟节点,这个节点不存储元素,只是让代码实现更加方便。如下:
public class YLinkWithHead<E> {
/**
* 虚拟头节点,不存储实际元素
*/
private Node head;
private int size;
public YLinkWithHead() {
head = new Node(null, null);
size = 0;
}
/**
* 内部类,用来封装链表中每个节点的属性
*/
private class Node{
/**
* 链表中每个节点的值
*/
private E e;
/**
* 指向链表中下个节点的指针
*/
private Node next;
private Node(E e, Node next) {
this.e = e;
this.next = next;
}
}
public int size() {
return size;
}
/**
* 向链表中的头部添加新节点
* @param e
*/
public void add(E e) {
//一行代码搞定节点添加
head.next = new Node(e, head.next);
size++;
}
/**
* 删除指定元素
*
* @param e
*/
public void remove(E e) {
Node temp = head;
while (temp.next != null) {
if (e.equals(temp.next.e)) {
temp.next = temp.next.next;
break;
}
temp = temp.next;
}
size--;
}
@Override
public String toString() {
StringBuilder sbr = new StringBuilder();
Node temp = head.next;
while (temp != null) {
sbr.append(temp.e).append("->");
temp = temp.next;
}
String s = sbr.toString();
String result = s.substring(0, s.lastIndexOf("->"));
return result;
}
}
说明:在上述代码中,add(E e)这个方法只需要一行代码就实现了,非常简单明了,之所以能这样实现,是因为我们在初始化链表的时候初始花了虚拟头节点,让它不为null,后面添加的元素都是在虚拟头节点的后面,唯一需要注意的是,在遍历的时候,需要从虚拟头节点的下个节点开始,整体感觉爽多了,哈哈。