双向链表

单链表具有单向性的缺点,找前驱不方便!

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表

image.png
image.png
创建一个节点,节点包含前节点,后节点,本节点的key和value
/**
 节点信息
 */
@interface TwoWayLinkedListNode : NSObject
{
    @package
    __unsafe_unretained TwoWayLinkedListNode *_prior; // retained by dic
    __unsafe_unretained TwoWayLinkedListNode *_next; // retained by dic
    id _key;
    id _value;
}
@end

@implementation TwoWayLinkedListNode
@end

/**
 链表
 */
@interface TwoWayLinkedList ()
{
    @package
    TwoWayLinkedListNode *_head; // 链表头
    TwoWayLinkedListNode *_tail; // 链表尾
    
    NSMutableDictionary  *_dic;  // 链表字典

}
@end

@implementation TwoWayLinkedList

- (instancetype)init
{
    self = [super init];
    if (self) {
        _dic = [NSMutableDictionary dictionary];
    }
    return self;
}

  • 在最前端添加一个节点

/**
 在最前端添加一个节点

 @param node node
 */
- (void)insertNodeAtHead:(TwoWayLinkedListNode *)node {
    [_dic setObject:node forKey:node->_key];
    
    if (_head) {
        node->_next = _head;
        _head->_prior = node;
        _head = node;
    }
    else {
        _head = _tail = node;
    }
}
  • 将某个节点移到最前端

/**
 将某个节点移到最前端

 @param node node
 */
- (void)bringNodeToHead:(TwoWayLinkedListNode *)node {
    if (_head == node) {
        return;
    }
    if (_tail == node) {
        /*
         条件:node节点是tail节点时
         将node节点的前一个节点变成tail节点
         tail节点的next置空
         */
        _tail = node->_prior;
        _tail->_next = nil;
    }
    else {
        /*
         条件:node节点是中间节点时
         将node的前一个节点赋值给node的下一个节点的prev
         将node的下一个节点赋值给node的前一个节点
         */
        node->_next->_prior = node->_prior;
        node->_prior->_next = node->_next;
    }
    
    // 将node移动到head节点并且将原head节点的放到head->next节点中
    node->_next = _head;
    node->_prior = nil;
    _head->_prior = node;
    _head = node;
}
  • 删除某个节点
/**
 删除某个节点

 @param node 节点
 */
- (void)removeNode:(TwoWayLinkedListNode *)node {
    [_dic removeObjectForKey:node->_key];
    
    if (node->_next) {
        node->_next->_prior = node->_prior;
    }
    
    if (node->_prior) {
        node->_prior->_next = node->_next;
    }
    
    if (_head == node) {
        _head = node->_next;
    }
    
    if (_tail == node) {
        _tail = node->_prior;
    }
}
  • 清空链表

/**
 清空链表
 */
- (void)removeAll {
    _head = nil;
    _tail = nil;
    
    _dic = [NSMutableDictionary dictionaryWithCapacity:0];
}

扩展1
最近最久未使用算法

每次将使用到的节点移动到最前方实现LRU算法

 TwoWayLinkedList *list = [[TwoWayLinkedList alloc]init];

    if (node) {
        [list bringNodeToHead:node];
    } else {
        [list insertNodeAtHead:node];
    }

扩展2
将链表就地逆置


image.png

插入法:扫描a1……an, 将每个ai插入到链表首部即可。


image.png

推荐阅读更多精彩内容