目录
- 单向循环链表
- 双向循环链表
- 约瑟夫问题
- 如何发挥循环链表的最大威力?
一 单向循环链表

单向循环链表.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老师的 恋上数据结构与算法
本人技术水平有限,如有错误欢迎指正。
书写整理不易,您的打赏点赞是对我最大的支持和鼓励,欢迎点赞打赏。