数据结构之双向循环链表

最近有点手痒,然后写了个双向循环链表


image.png

1.建立LinkList,分别用frist和last引用头尾节点
2.为了防止循环引用,节点间的引用previous和next必须有一个由weak引用,这样在断掉LinkList的引用和断掉尾节的next的引用时,整个链表就可以释放了

@interface GYJCircleLinkList : NSObject

//获取链表中存储元素的个数
- (NSInteger)size;
//获取元素列表是否为空
- (BOOL)isEmpty;
//添加元素
- (void)addElement:(NSObject *)element;
//在某位置插入元素
- (void)insertElement:(NSObject *)element atIndex:(NSInteger)index;
//移除某索引下元素
- (NSObject *)removeObjcAtIndex:(NSInteger)index;
//替换某索引下元素
- (NSObject *)replaceElementByNewElement:(NSObject *)newElement index:(NSInteger)index;
//是否含有给定元素
- (BOOL)containsElement:(NSObject *)element;
//获取索引下标,如果没找到,返回-1
- (NSInteger)indexOfElement:(NSObject *)element;
//返回索引下的内容
- (NSObject *)elementOfIndex:(NSInteger)index;
//清空链表
- (void)clear;
 
- (NSString *)toString;

@end

///节点
@interface GYJNode : NSObject

@property(nonatomic,strong,nullable)GYJNode *nextNode;
@property(nonatomic,weak,nullable)GYJNode *previousNode;
@property(nonatomic,strong)NSObject *element;
- (instancetype)initWithPreviousNode:(nullable GYJNode *)previous Element:(NSObject *)element nextNode:(nullable GYJNode *)nextNode;

- (NSString *)toString;

@end
#import "GYJCircleLinkList.h"

@interface GYJCircleLinkList ()

@property(nonatomic,assign)NSInteger size;
@property(nonatomic,strong)GYJNode *frist;
@property(nonatomic,strong)GYJNode *last;
@end

static const NSInteger ELEMENT_NOT_FOUND = -1;

@implementation GYJCircleLinkList

- (NSInteger)size;
{
    return _size;
}
- (BOOL)isEmpty{
    return _size == 0;
}
- (void)addElement:(NSObject *)element{
    [self insertElement:element atIndex:_size];
}
- (void)insertElement:(NSObject *)element atIndex:(NSInteger)index
{
    [self indexOfAddRange:index];
    if (_frist==nil) {
        GYJNode *newNode = [[GYJNode alloc]initWithPreviousNode:nil Element:element nextNode:nil];
        _frist = newNode;
        _last = newNode;
        newNode.previousNode = newNode;
        newNode.nextNode = newNode;
    }else{
        GYJNode *lastNode = [self searchNodeUseIndex:_size-1];
        GYJNode *headNode = lastNode.nextNode;
        GYJNode *newNode = [[GYJNode alloc]initWithPreviousNode:lastNode Element:element nextNode:headNode];
        lastNode.nextNode = newNode;
        headNode.previousNode = newNode;
        _last = newNode;
    }
    _size++;
}
- (NSObject *)removeObjcAtIndex:(NSInteger)index{
    [self indexOutOfRange:index];
    if (_frist == nil) {
        return nil;
    }
    GYJNode *node =  [self searchNodeUseIndex:index];
    if (_size==1) {
        node.nextNode = nil;
        _frist = nil;
        _last = nil;
        _size = 0;
    }else{
        if (index==0) {
            _frist = node.nextNode;
            _last.nextNode = node.nextNode;
            node.nextNode.previousNode = _last;
            node.nextNode = nil;
        }else if (index==_size-1){
            _last = node.previousNode;
            _frist.previousNode = _last;
            node.previousNode.nextNode = _frist;
            node.nextNode = nil;
        }else{
            GYJNode *prevoisNode = node.previousNode;
            prevoisNode.nextNode = node.nextNode;
            node.nextNode.previousNode = prevoisNode;
            node.nextNode = nil;
        }
        _size--;
    }
    return node.element;
}
- (NSObject *)replaceElementByNewElement:(NSObject *)newElement index:(NSInteger)index{
    [self indexOutOfRange:index];
    GYJNode *node = [self searchNodeUseIndex:index];
    if (node==nil) {
        return nil;
    }
    NSObject *oldElement = node.element;
    node.element = newElement;
    return oldElement;
}
- (BOOL)containsElement:(NSObject *)element
{
    return [self indexOfElement:element]==ELEMENT_NOT_FOUND?NO:YES;
}
- (NSInteger)indexOfElement:(NSObject *)element{
    
    if (element==nil) {
        GYJNode *node = _frist;
        for (NSInteger i = 0; i<_size; i++) {
            if (node.element == nil) {
                return I;
            }
            node = node.nextNode;
        }
    }else{
        GYJNode *node = _frist;
        for (NSInteger i = 0; i<_size; i++) {
            if ([node.element isEqual:element]) {
                return I;
            }
            node = node.nextNode;
        }
    }
    return ELEMENT_NOT_FOUND;
}
- (NSObject *)elementOfIndex:(NSInteger)index{
    [self indexOutOfRange:index];
    GYJNode *node = [self searchNodeUseIndex:index];
    return node.element;
}
- (void)clear{
    
    if (_last==nil) {
        return;
    }else{
        _last.nextNode = nil;
        _frist = nil;
        _last = nil;
    }
    _size = 0;
}
- (nullable GYJNode *)searchNodeUseIndex:(NSInteger)index
{
    [self indexOutOfRange:index];
    GYJNode *mark = _frist;
    if (index<(_size>>1)) {
        for (NSInteger i = 0; i<index; i++) {
            mark = mark.nextNode;
        }
        return mark;
    }else{
        if (_frist==nil) {
            return nil;
        }else{
            mark = _last;
            for (NSInteger i = _size-1; i>index; i--) {
                mark = mark.previousNode;
            }
            return mark;
        }
    }
    return nil;
}
- (void)indexOutOfRange:(NSInteger)index{
    BOOL res = index<0||index>_size-1;
    NSAssert(res==NO, @"传入的index超出链表索引");
}
- (void)indexOfAddRange:(NSInteger)index{
    BOOL res = index<0||index>_size;
    NSAssert(res==NO, @"传入的index超出链表索引");
}

- (NSString *)toString
{
    NSMutableString *string = [[NSMutableString alloc]init];
    GYJNode *node = _frist;
    [string appendString:[NSString stringWithFormat:@"size=%ld",_size]];
    [string appendString:@"["];
    for (NSInteger i = 0; i<_size; i++) {
        if (i!=0) {
            [string appendString:@","];
        }
        [string appendString:[node toString]];
        node = node.nextNode;
    }
    [string appendString:@"]"];
    return string.copy;
}

@end


@implementation GYJNode

- (instancetype)initWithPreviousNode:(nullable GYJNode *)previous Element:(NSObject *)element nextNode:(nullable GYJNode *)nextNode
{
    if (self = [super init]) {
        _previousNode = previous;
        _nextNode = nextNode;
        _element = element;
    }
    return self;
}

- (NSString *)toString
{
    NSMutableString *string = [[NSMutableString alloc]init];
    [string appendString:[NSString stringWithFormat:@"%@_%@_%@",_previousNode.element,_element,_nextNode.element]];
    return string.copy;
}

- (void)dealloc
{
    NSLog(@"元素释放了:%@_%@_%@",_previousNode.element,_element,_nextNode.element);
}

@end

测试代码

 GYJCircleLinkList *linkList = [[GYJCircleLinkList alloc]init];

    for (NSInteger i = 1; i<10; i++) {
        [linkList addElement:@(i)];
    }
    NSLog(@"%@",[linkList toString]);
    
    [linkList removeObjcAtIndex:0]; //size=8 [9_2_3,2_3_4,3_4_5,4_5_6,5_6_7,6_7_8,7_8_9,8_9_2]
    [linkList removeObjcAtIndex:[linkList size]-1];//size=7 [8_2_3,2_3_4,3_4_5,4_5_6,5_6_7,6_7_8,7_8_2]
    [linkList removeObjcAtIndex:1];// size=6 [8_2_4,2_4_5,4_5_6,5_6_7,6_7_8,7_8_2]
    
    NSLog(@"%@",[linkList toString]);
    
    [linkList replaceElementByNewElement:@(100) index:3];  //size=6 [8_2_4,2_4_5,4_5_6,5_100_7,6_7_8,7_8_2]
    NSLog(@"%@",[linkList toString]);
    [linkList elementOfIndex:5];  //7
    NSInteger number = [linkList indexOfElement:@(5)];  //size=6 [8_2_4,2_4_5,4_5_100,5_100_7,100_7_8,7_8_2]
    NSLog(@"%ld",number); //2
    
    NSLog(@"containElement(%@) = %@",@(100),[linkList containsElement:@(100)]==YES?@"true":@"false");
    
    [linkList clear];  //2,4,5,100,7,8
    
    NSLog(@"%@",[linkList toString]);

最后打印结果显示链表的引用关系正常,对象也正常释放,可以像数组一样使用哈

2022-04-18 17:28:16.300819+0800 CompleteBinaryTree[19209:313617] size=9[9_1_2,1_2_3,2_3_4,3_4_5,4_5_6,5_6_7,6_7_8,7_8_9,8_9_1]
2022-04-18 17:28:16.301134+0800 CompleteBinaryTree[19209:313617] 元素释放了:9_1_(null)
2022-04-18 17:28:16.301378+0800 CompleteBinaryTree[19209:313617] 元素释放了:8_9_(null)
2022-04-18 17:28:16.301581+0800 CompleteBinaryTree[19209:313617] 元素释放了:2_3_(null)
2022-04-18 17:28:16.301836+0800 CompleteBinaryTree[19209:313617] size=6[8_2_4,2_4_5,4_5_6,5_6_7,6_7_8,7_8_2]
2022-04-18 17:28:16.302104+0800 CompleteBinaryTree[19209:313617] size=6[8_2_4,2_4_5,4_5_100,5_100_7,100_7_8,7_8_2]
2022-04-18 17:28:16.302262+0800 CompleteBinaryTree[19209:313617] 2
2022-04-18 17:28:16.302430+0800 CompleteBinaryTree[19209:313617] containElement(100) = true
2022-04-18 17:28:16.302633+0800 CompleteBinaryTree[19209:313617] 元素释放了:8_2_4
2022-04-18 17:28:16.302819+0800 CompleteBinaryTree[19209:313617] 元素释放了:(null)_4_5
2022-04-18 17:28:16.303055+0800 CompleteBinaryTree[19209:313617] 元素释放了:(null)_5_100
2022-04-18 17:28:16.303364+0800 CompleteBinaryTree[19209:313617] 元素释放了:(null)_100_7
2022-04-18 17:28:16.303722+0800 CompleteBinaryTree[19209:313617] 元素释放了:(null)_7_8
2022-04-18 17:28:16.304106+0800 CompleteBinaryTree[19209:313617] 元素释放了:(null)_8_(null)
2022-04-18 17:28:16.304476+0800 CompleteBinaryTree[19209:313617] size=0[]
2022-04-18 17:28:16.333057+0800 CompleteBinaryTree[19209:313617] [UIFocus] Focus system enabled

附上 swift版本的双向链表,已经过测试

import Foundation

enum GYJError:Error{
    case outOfBounds(Int,Int,String)
    case wrongArgument(String)
    case otherError
}

class GYJLinkedList<E:Equatable> {
    
    // MARK : 公开的内容
    ///获取链表元素的个数
    private(set) var size = 0
    private var head : GYJNode<E>?
    private var tail : GYJNode<E>?
    ///判断链表是否为空
    func isEmpty() -> Bool{
       return size == 0
    }
    ///给链表添加元素
    func addElement(_ element:E){
        insertElement(element: element, index: size)
    }
    ///在链表索引处插入元素
    func insertElement(element: E,index : Int) {
        assert(index>=0 && index <= size, "插入元素的索引越界")
        let node = GYJNode(element)
        if head == nil{
            head = node
            tail = node
        }else if index == 0{
            node.next = head
            head?.previous = node
            head = node
        }else if index == size{
            tail?.next = node
            node.previous = tail
            tail = node
        }else{
            let half = size >> 1
            if index < half {
                var currentNode = head
                for _ in 1...index{
                    currentNode = currentNode?.next
                }
                let nextNode = currentNode?.next
                node.next = nextNode
                nextNode?.previous = node
                node.previous = currentNode
                currentNode?.next = node
            }else{
                var previous = tail
                for _ in index..<size{
                    previous = previous?.previous
                }
                let nextNode = previous?.next
                node.next = nextNode
                nextNode?.previous = node
                previous?.next = node
                node.previous = previous
            }
        }
        size += 1
    }
    ///移除链表索引下的元素
    @discardableResult
    func removeElment(index : Int) -> E?{
        assert(index>=0 && index < size, "插入元素的索引越界")
        var node : GYJNode<E>?
        if size == 1 {
            node = head
            head = nil
            tail = nil
            size -= 1
            return node?.element
        }
        if index == 0{
            let next = head?.next
            node = head
            head?.next = nil
            head = next
            
        }else if index == size-1{
            node = tail
            let previous = tail?.previous
            previous?.next = nil
            tail = previous
        }else{
            let half = size >> 1
            if index < half{
                node = head
                for _ in 1...index{
                    node = node?.next
                }
                let next = node?.next
                node?.previous?.next = next
            }else{
                node = tail
                for _ in index..<(size-1){
                    node = node?.previous
                }
                let next = node?.next
                node?.previous?.next = next
                next?.previous = node?.previous
            }
        }
        size -= 1
        return node?.element
    }
    ///移除链表中第一个符合相同条件的元素
    @discardableResult
    func removeElment(_ element : E) -> Bool{
        let index = indexOfElment(element)
        if index == nil {
            return false
        }else{
            removeElment(index: index!)
            return true
        }
    }
    ///获取index = 0的元素
    func headElement() -> E?{
       return head?.element
    }
    ///获取最后一个元素
    func tailElement() -> E?{
       return tail?.element
    }
    ///是否含有元素
    func containsElment(_ element: E) -> Bool{
        if indexOfElment(element) == nil{
            return false
        }
        return true
    }
    func removeAllElement() {
        head = nil
        tail = nil
        size = 0
    }
    // MARK : 私有的内容
    private func indexOfElment(_ elment: E) -> Int?{
        var node = head
        for i in 0..<size{
            if node?.element == elment{
                return i
            }
            node = node?.next
        }
        return nil
    }
    /*
    do{
       try linkedList.insertElement(index: 2)
    }catch GYJError.outOfBounds(_, _, let msg){
        print(msg)
    }catch{
        
    }
     func insertElement(index : Int) throws{
         if index < 0 || index > size{
             throw GYJError.outOfBounds(index, size, "链表越界")
         }
         /*
          ....代码内容
         */
     }
     */

}
extension GYJLinkedList : CustomStringConvertible{
    
    var description: String{
        var str = ""
        var node = head
        while node != nil{
            str.append(", ")
            str.append(node!.previous?.description ?? "null_")
            str.append(node!.description)
            str.append(node!.next?.description ?? "_null")
            node = node?.next
        }
        return str
    }
}

private class GYJNode<E> : CustomStringConvertible{
    var next: GYJNode?
    weak var previous: GYJNode?
    var element: E
    init(_ elem: E){
        element = elem
    }
    var description: String{
        "_\(element)_"
    }
    deinit{
        print("_\(element)_释放了")
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容