自己动手实现java数据结构(二) 链表
|
从概率的角度上来看,下标访问是随机的,因此find方法的时间复杂度被认为是O(n)。 3.4.2 链表的插入方法实现 add(E e) {
:::将新的数据 插入到列表末尾 ==> 作为last节点的前一个节点插入
this.last.linkAsLeft((e));
:::size自增
this.size++;
true:::插入时,下标越界检查
rangeCheckForAdd(index);
:::查询出下标对应的 目标节点
Node<E> targetNode = find(index);
:::将新的数据节点 作为目标节点的前一个节点插入
targetNode.linkAsLeft((e));
;
}
插入方法的时间复杂度:尾部插入: 尾部插入方法中,由于我们维护了一个last哨兵节点,通过直接将新节点插入last哨兵的左边,完成尾部数据的插入。 因此,尾部插入方法的时间复杂度为O(1)。 下标插入: 下标插入方法中,依赖find方法查询出下标对应的节点,然后将新节点进行插入。 因此,下标插入方法的时间复杂度为O(n);对于头部,尾部节点的插入,时间复杂度为O(1)。 3.4.3 删除方法的实现 remove(E e) {
.first;
删除方法的时间复杂度:特定元素删除: 链表特定元素的删除和find方法类似,通过一次遍历获得目标节点,然后进行删除。 因此,特定元素的删除方法时间复杂度为O(n)。 下标删除: 下标删除方法中,依赖find方法查询出下标对应的节点,然后将目标节点进行删除。 因此,下标删除方法的时间复杂度为O(n);对于头部,尾部节点的删除,时间复杂度为O(1)。 3.4.4?修改/查询方法实现public E set(:::先暂存要被替换的"data"
E oldValue = targetNode.data;
:::将当前下标对应的"data"替换为"e"
targetNode.data = e;
:::返回被替换的data
oldValue;
}
@Override
public E get( targetNode.data;
}
修改/查询方法的时间复杂度:可以看到,链表基于下标的修改和查询都依赖于find方法。 因此,修改/查询方法的时间复杂度为O(n);对于头部,尾部节点的修改和查询,时间复杂度为O(1)。 3.5 链表其它接口3.5.1 clear方法clear方法用于清空链表内的元素,初始化链表。 clear() {
:::当头部哨兵节点不和尾部哨兵节点直接相连时
while(this.first.right != .last){
:::删除掉头部哨兵节点后的节点
remove(0);
}
:::执行完毕后,代表链表被初始化,重置size
;
}
3.5.2 toString方法 String toString() {
Iterator<E> iterator = .iterator();
:::空列表
if(!iterator.hasNext()){
return "[]";
}
:::列表起始使用"["
StringBuilder s = new StringBuilder("[");
:::反复迭代
:::获得迭代的当前元素
E data = iterator.next();
:::判断当前元素是否是最后一个元素
iterator.hasNext()){
:::是最后一个元素,用"]"收尾
s.append(data).append("]");
:::执行完毕 返回拼接完成的字符串
s.toString();
}{
:::不是最后一个元素
:::使用","分割,拼接到后面
s.append(data).append(",");
}
}
}
链表的toString方法在之前"向量篇"的基础上进行了优化。基于列表List的Iterator接口进行遍历,实现了同样的功能。事实上,改进之后的toString方法也同样可以适用于向量。 在jdk的Collection框架中,框架设计者要求所有的单值数据结构容器都必须实现Iterator接口,因而将toString方法在AbstractCollection这一顶层抽象类中进行了实现,达到统一单值数据结构容器toString方法默认行为的目的,增强了代码的可重用性。 4.链表的Iterator(迭代器)
* 链表迭代器实现
* class Itr implements Iterator<E>
* 当前迭代器光标位置
* 初始化指向 头部哨兵节点
* private Node<E> currentNode = LinkedList..first;
* 最近一次迭代返回的数据
* lastReturned;
@Override
hasNext() {
:::判断当前节点的下一个节点 是否是 尾部哨兵节点
this.currentNode.right != LinkedList..last);
}
@Override
E next() {
:::指向当前节点的下一个节点
this.currentNode = .currentNode.right;
:::设置最近一次返回的节点
this.lastReturned = currentNode;
:::返回当前节点的data
.currentNode.data;
}
@Override
remove() {
if(this.lastReturned == new IteratorStateErrorException("迭代器状态异常: 可能在一次迭代中进行了多次remove操作");
}
:::当前光标指向的节点要被删除,先暂存引用
Node<E> nodeWillRemove = .currentNode;
:::由于当前节点需要被删除,因此光标前移,指向当前节点的上一个节点
.currentNode.left;
:::移除操作需要将最近一次返回设置为null,防止多次remove
this.lastReturned = :::将节点从链表中移除
nodeWillRemove.unlinkSelf();
}
}
迭代器在实现时需要满足接口的行为定义,remove方法在一次next迭代中不能被重复调用,否则会产生古怪的异常。 5.链表性能链表作为线性表的一种,一般被用来和同为线性表的向量进行比较。 空间效率:比起向量,链表的单位数据是存储在内部节点之中的。而内部节点除了含有数据域,还包括了左右节点的引用,因此链表的空间效率比向量稍差。 时间效率:在有些书籍或者博客中会笼统地介绍:"比起向量,链表的插入,删除操作时间复杂度较低(向量O(n),链表O(1));查询,修改操作时间复杂度较高(向量O(1),链表O(n))"。 链表插入,删除操作的时间复杂度本身确实是O(1)(仅需要修改节点引用),因为不用和向量一样批量的移动内部元素。 (编辑:应用网_阳江站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


