数据结构与算法之循环链表(3.4)

199 阅读4分钟
原文链接: www.jianshu.com
目录
  • 单向循环链表
  • 双向循环链表
  • 约瑟夫问题
  • 如何发挥循环链表的最大威力?
一 单向循环链表
单向循环链表.png

单向循环链表 - 只有一个节点

image.png

核心方法讲解

  • add:element:
/// 在索引为index处插入元素值为element的节点
- (void)add:(NSUInteger)index element:(id)element {
    [self rangeCheck:index];
    
    if (index == 0) {
        LinkNode *newFirst = [[LinkNode alloc] initWithElement:element next:_first];
        // 拿到最后一个节点
        LinkNode *last = self.size == 0 ? newFirst : [self node:self.size - 1];
        last.next = newFirst;
        _first = newFirst;
    } else {
        LinkNode *prev = [self node:index - 1];
        prev.next = [[LinkNode alloc] initWithElement:element next:prev.next];
    }
    self.size++;
}
  • remove:
// 删除索引为index的节点
- (id)remove:(NSUInteger)index {
    if ([self rangeCheck:index]) {
        return nil;
    }
    
    LinkNode *node = _first;
    
    if (index == 0) {
        if (self.size == 1) {
            _first = nil;
        } else {
            LinkNode *last = [self node:self.size - 1];
            _first = _first.next;
            last.next = _first;
        }
    } else {
        LinkNode *prev = [self node:index - 1];
        node = prev.next;
        prev.next = node.next;
    }
    self.size--;
    return node.element;
}
  • 实例代码检测
- (void)singleCircleLinkedListTest {
    SingleCircleLinkedList *list = [[SingleCircleLinkedList alloc] init];
    [list add:@11];
    [list add:@22];
    [list add:@33];
    [list add:@44]; // [11, 22, 33, 44]
    
    NSLog(@"第一次打印: %@",list.description);
    
    [list add:0 element:@55];   // [55, 11, 22, 33, 44]
    [list add:2 element:@66];   // [55, 11, 66, 22, 33, 44]
    [list add:list.size  element:@77];  // [55, 11, 66, 22, 33, 44, 77]
    
    NSLog(@"第二次打印: %@",list.description);
    
    [list remove:0];    // size = 6 [11, 66, 22, 33, 44, 77]
    [list remove:2];    // size = 5 [11, 66, 33, 44, 77]
    [list remove:list.size - 1];    // size = 4 [11, 66, 33, 44]
    
    NSLog(@"第三次打印: %@",list.description);
    NSLog(@"----------------");
    NSLog(@"%lu",(unsigned long)[list indexOf:@44]);    // 3
    NSLog(@"%lu",(unsigned long)[list indexOf:@22]);    // NSNotFound
    NSLog(@"%lu",(unsigned long)[list contains:@33]);   // true
    NSLog(@"%@",[list get:0]);  // 11
    NSLog(@"%@",[list get:1]);  // 66
    NSLog(@"%@",[list get:list.size - 1]);  // 4
    
    NSLog(@"%@",list.description);
}

运行结果如下:

image.png

更多相关代码请查看类 SingleCircleLinkedList

二 双向循环链表
双向循环链表.png

双向循环链表 - 只有一个节点

image.png

核心方法讲解

  • add:
/// 在索引为index处插入元素值为element的节点
- (void)add:(NSUInteger)index element:(id)element {
    if (index == self.size) {   // 往最后面添加元素
        LinkNode *oldLast = _last;
        _last = [[LinkNode alloc] initWithPrev:oldLast element:element next:_first];
        if (oldLast == nil) {   // 这是链表添加的第一个元素
            _first = _last;
            _first.next = _first;
            _first.prev = _first;
        } else {    // 往一个非空链表插入新节点
            oldLast.next = _last;
            _first.prev = _last;
        }
    } else {    // 在中间插入节点
        LinkNode *next = [self node:index]; // 先获取要插入位置上的节点
        LinkNode *prev = next.prev;
        LinkNode *node = [[LinkNode alloc] initWithPrev:prev element:element next:next];
        next.prev = node;
        prev.next  = node;
        
        if (next == _first) {   // 插入到第一个位置
            _first = node;
        }
    }
    self.size++;
}
  • remove:
// 删除node节点
- (id)removeLinkNode:(LinkNode *)node {
    if (self.size == 1) {
        _first = nil;
        _last = nil;
    } else {
        LinkNode *prev = node.prev;
        LinkNode *next = node.next;
        prev.next = next;
        next.prev = prev;
        
        if (node == _first) {   // idnex == 0
            _first = next;
        }
        
        if (node == _last) {    // index == size
            _last = prev;
        }
    }
    
    self.size--;
    return node.element;
}
  • 实例代码检测
#pragma mark - 双向循环链表

- (void)doubleCircleLinkedListTest {
    DoubleCircleLinkedList *list = [[DoubleCircleLinkedList alloc] init];
    [list add:@11];
    [list add:@22];
    [list add:@33];
    [list add:@44]; // [11, 22, 33, 44]
    
    NSLog(@"第一次打印: %@",list.description);
    
    [list add:0 element:@55];   // [55, 11, 22, 33, 44]
    [list add:2 element:@66];   // [55, 11, 66, 22, 33, 44]
    [list add:list.size  element:@77];  // [55, 11, 66, 22, 33, 44, 77]
    
    NSLog(@"第二次打印: %@",list.description);
    
    [list remove:0];    // size = 6 [11, 66, 22, 33, 44, 77]
    [list remove:2];    // size = 5 [11, 66, 33, 44, 77]
    [list remove:list.size - 1];    // size = 4 [11, 66, 33, 44]
    
    NSLog(@"第三次打印: %@",list.description);
    NSLog(@"----------------");
    NSLog(@"%lu",(unsigned long)[list indexOf:@44]);    // 3
    NSLog(@"%lu",(unsigned long)[list indexOf:@22]);    // NSNotFound
    NSLog(@"%lu",(unsigned long)[list contains:@33]);   // true
    NSLog(@"%@",[list get:0]);  // 11
    NSLog(@"%@",[list get:1]);  // 66
    NSLog(@"%@",[list get:list.size - 1]);  // 4
    
    NSLog(@"%@",list.description);
}

运行结果如下

双向循环链表.png

更多相关代码请查看类 SingleCircleLinkedList

三 约瑟夫问题
约瑟夫.png
  • 核心代码如下
#pragma mark - 约瑟夫问题

- (void)josephus {
    DoubleCircleLinkedList *list = [[DoubleCircleLinkedList alloc] init];
    for (int i = 1; i <= 8; i++) {
        [list add:@(i)];
    }
    
    // 指向头结点
    [list reset];
    
    // 开始递归循环
    while (![list isEmpty]) {
        [list next];
        [list next];    // 走两步
        NSLog(@"%@",[list remove]);
    }
}

运行结果

image.png

打印数字即为被干掉的位置

四 如何发挥循环链表的最大威力?

可以考虑增加1个成员变量,3个方法

  • current:用于指向某个节点
  • void reset(): 让current指向头节点first
  • next():让current往后走一步,也就是说current = current.next
  • remove():删除current指向的节点,删除成功后让current指向下一个节点
image.png

本文会持续更新中,更多精彩内容敬请期待。


本文参考 MJ老师的 恋上数据结构与算法


本人技术水平有限,如有错误欢迎指正。
书写整理不易,您的打赏点赞是对我最大的支持和鼓励,欢迎点赞打赏。


项目连接链接 - 06_CircleLinkList