時間の複雑さの道リンクリスト
では片方向リンクリストの原理と時間の複雑さに比べて動的配列とリンクリストの一方向の比較や分析があります。
分類 | 加えます | 削除 |
---|---|---|
最高のヘッドノード | O(1) | O(1) |
最悪のテール・ノード | O(n) | O(n) |
平均 | O(n) | O(n) |
時間の複雑さの二重リンクリスト
ノードを探します
そのため、インデックスへのノードの追加および削除の次を見つけるために、ノードを見つけるために、解析コードの最初のノードが必要になります。
- (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;
}
}
复制代码
インデックス== 0またはインデックス== _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 判断
if (_size == 0) { // 空链表添加第一个节点
_first = _last;
} else { // 添加到表尾
oldLast.next = _last;
}
} else { // 插入到表的非空节点的位置上
JKRLinkedListNode *next = [self nodeWithIndex:index];
JKRLinkedListNode *prev = next.prev;
JKRLinkedListNode *node = [[JKRLinkedListNode alloc] initWithPrev:prev object:anObject next:next];
next.prev = node;
// 还可以用 !prev 、 next == _first 判断
if (index == 0) { // 插入到表头
_first = node;
} else { // 插入到表中间
prev.next = node;
}
}
_size++;
}
复制代码
ノードの全てを添加する前、次の動作は同じであり、違いはそれを見つけるために、ノードの複雑さに応じて、インデックスでノードを見つけることです。したがって、それは二重にリンクされたリストのヘッドノードを削除し、結論付けることができる、テイルノードがリストの中央にO(1)、高速動作時のヘッドノードまたはテール・ノードに近いほど、遅い近いです。平均的な複雑さはO(N)であり、そして追加同じ削除します。
時間複雑二重にリンクされたリスト
分類 | 加えます | 削除 |
---|---|---|
最高のヘッドノード | O(1) | O(1) |
好ましくはテール・ノード | O(1) | O(1) |
平均 | O(n) | O(n) |
二重リンクリストと一方向の時間複雑さの比較
上記の結論、単方向および双方向のリンクリストのヘッドノードリスト操作から知ることができ、リストの最初の半分は、リスト内の中間ノードは同じです。動作ではテール・ノード二重にリンクされたリストは、リンクされたリストは、後半途中にリンクされたリストよりもはるかに優れています。
比較試験であり、以下、比較の比較は、リストのヘッドノード、テール・ノード、ノード前半、ノードの後半、中間ノード5例における単方向および双方向のリンクされたリストです。
void compareSingleLinkedListAndLinkedList() {
NSUInteger testCount = 50000;
[JKRTimeTool teskCodeWithBlock:^{
JKRBaseList *array = [JKRSingleLinkedList new];
for (NSUInteger i = 0; i < testCount; i++) {
[array insertObject:[NSNumber numberWithInteger:i] atIndex:0];
}
for (NSUInteger i = 0; i < testCount; i++) {
[array removeFirstObject];
}
NSLog(@"单向链表操作头节点");
}];
[JKRTimeTool teskCodeWithBlock:^{
JKRBaseList *array = [JKRLinkedList new];
for (NSUInteger i = 0; i < testCount; i++) {
[array insertObject:[NSNumber numberWithInteger:i] atIndex:0];
}
for (NSUInteger i = 0; i < testCount; i++) {
[array removeFirstObject];
}
NSLog(@"双向链表操作头节点");
}];
[JKRTimeTool teskCodeWithBlock:^{
JKRBaseList *array = [JKRSingleLinkedList new];
for (NSUInteger i = 0; i < testCount; i++) {
[array addObject:[NSNumber numberWithInteger:i]];
}
for (NSUInteger i = 0; i < testCount; i++) {
[array removeLastObject];
}
NSLog(@"单向链表操作尾节点");
}];
[JKRTimeTool teskCodeWithBlock:^{
JKRBaseList *array = [JKRLinkedList new];
for (NSUInteger i = 0; i < testCount; i++) {
[array addObject:[NSNumber numberWithInteger:i]];
}
for (NSUInteger i = 0; i < testCount; i++) {
[array removeLastObject];
}
NSLog(@"双向链表操作尾节点");
}];
[JKRTimeTool teskCodeWithBlock:^{
JKRBaseList *array = [JKRSingleLinkedList new];
for (NSUInteger i = 0; i < testCount; i++) {
[array insertObject:[NSNumber numberWithInteger:i] atIndex:array.count >> 2];
}
for (NSUInteger i = 0; i < testCount; i++) {
[array removeObjectAtIndex:array.count >> 2];
}
NSLog(@"单向链表操作 index = 总节点数*0.25 节点");
}];
[JKRTimeTool teskCodeWithBlock:^{
JKRBaseList *array = [JKRLinkedList new];
for (NSUInteger i = 0; i < testCount; i++) {
[array insertObject:[NSNumber numberWithInteger:i] atIndex:array.count >> 2];
}
for (NSUInteger i = 0; i < testCount; i++) {
[array removeObjectAtIndex:array.count >> 2];
}
NSLog(@"双向链表操作 index = 总节点数*0.25 节点");
}];
[JKRTimeTool teskCodeWithBlock:^{
JKRBaseList *array = [JKRSingleLinkedList new];
for (NSUInteger i = 0; i < testCount; i++) {
[array insertObject:[NSNumber numberWithInteger:i] atIndex:array.count * 0.75];
}
for (NSUInteger i = 0; i < testCount; i++) {
[array removeObjectAtIndex:array.count * 0.75];
}
NSLog(@"单向链表操作 index = 总节点数*0.75 节点");
}];
[JKRTimeTool teskCodeWithBlock:^{
JKRBaseList *array = [JKRLinkedList new];
for (NSUInteger i = 0; i < testCount; i++) {
[array insertObject:[NSNumber numberWithInteger:i] atIndex:array.count * 0.75];
}
for (NSUInteger i = 0; i < testCount; i++) {
[array removeObjectAtIndex:array.count * 0.75];
}
NSLog(@"双向链表操作 index = 总节点数*0.75 节点");
}];
[JKRTimeTool teskCodeWithBlock:^{
JKRBaseList *array = [JKRSingleLinkedList new];
for (NSUInteger i = 0; i < testCount; i++) {
[array insertObject:[NSNumber numberWithInteger:i] atIndex:array.count >> 1];
}
for (NSUInteger i = 0; i < testCount; i++) {
[array removeObjectAtIndex:array.count >> 1];
}
NSLog(@"单向链表操作中间节点");
}];
[JKRTimeTool teskCodeWithBlock:^{
JKRBaseList *array = [JKRLinkedList new];
for (NSUInteger i = 0; i < testCount; i++) {
[array insertObject:[NSNumber numberWithInteger:i] atIndex:array.count >> 1];
}
for (NSUInteger i = 0; i < testCount; i++) {
[array removeObjectAtIndex:array.count >> 1];
}
NSLog(@"双向链表操作中间节点");
}];
}
复制代码
印刷結果:
单向链表操作头节点
耗时: 0.020 s
双向链表操作头节点
耗时: 0.038 s
单向链表操作尾节点
耗时: 49.085 s
双向链表操作尾节点
耗时: 0.033 s
单向链表操作 index = 总节点数*0.25 节点
耗时: 12.144 s
双向链表操作 ndex = 总节点数*0.25 节点
耗时: 12.215 s
单向链表操作 index = 总节点数*0.75 节点
耗时: 35.735 s
双向链表操作 index = 总节点数*0.75 节点
耗时: 18.488 s
单向链表操作中间节点
耗时: 23.856 s
双向链表操作中间节点
耗时: 36.420 s
复制代码
印刷は、上記から分かる操作で二重にリンクされたリストの一方向リンクリストのノードの前半、場合によっては二重にリンクされたリストに優れている片方向リンクリストのヘッドノード、テール・ノード、中間ノードは、として動作するノードの数未満でありますそれは現在のノードのノードへのポインタポイントはありません。しかし、演算ノードの後半、エンドノード、効率は、二重リンクリストの一方向リンクリストよりもはるかに低くなります。
したがって、場合にのみ、ヘッダデータの操作や前半、及び一貫性二重リンクリスト一方向時の複雑さ、より合理的かつ効率的な方法でリンクされたリスト。しかし、あなたは頭にデータが必要な場合は、尾を操作する必要があり、双方向リンクリストは、片方向リンクリストよりもはるかに優れています。