单向链表的时间复杂度
在单向链表原理和实现中已经和动态数组相比对比并分析了单向链表的时间复杂度:
情况分类 | 添加 | 删除 |
---|---|---|
最好 头节点 | O(1) | O(1) |
最差 尾节点 | O(n) | O(n) |
平均 | O(n) | O(n) |
双向链表的时间复杂度
查找节点
因为对节点的添加和删除操作需要查找对于index的节点,首先分析以下查找节点的代码:
- (JKRLinkedListNode *)nodeWithIndex:(NSInteger)index {
[self rangeCheckForExceptAdd:index];
// _size >> 1 相当于 floor(_size / 2),位运算可以大大节省计算时间
if (index < (_size >> 1)) { // 当index位于链表的前半,从头节点向后查找
JKRLinkedListNode *node = _first;
for (NSUInteger i = 0; i < index; i++) {
node = node.next;
}
return node;
} else { // 当index位于链表的后半,从尾节点向前查找
JKRLinkedListNode *node = _last;
for (NSUInteger i = _size - 1; i > index; i--) {
node = node.prev;
}
return node;
}
}
复制代码
当index == 0 或 index == _size - 1时,即查找头节点和尾节点时,直接就可以一次查找就拿到需要的节点。越靠近头节点或尾节点,查找需要遍历的次数越小,最坏的情况就是查找中间的节点,需要 n / 2 次查找。
添加和删除
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index {
[self rangeCheckForAdd:index];
if (index == _size) { // index == size 相当于 插入到表尾 或者 空链表添加第一个节点
JKRLinkedListNode *oldLast = _last;
JKRLinkedListNode *node = [[JKRLinkedListNode alloc] initWithPrev:_last object:anObject next:nil];
_last = node;
// 还可以用 !oldLast 、 !_first 判断