首先这篇只是一个开篇,后面计划的更新文章包括:动态数组、链表、二叉树、哈希表、堆等,而且中间会穿插一些具体的现实中的应用和时间复杂度对比。git
文章大部分的代码逻辑并不是我本身想出来的,本人也没有这么好天分,大部分代码逻辑来自于因为李明杰的数据结构与算法课程。源课程语言为Java,我将其用Objective-C转写,但不是复制粘贴源码,也并不是不注明出处的抄袭。若是以为文章不错,而且想更深刻系统的学习数据结构,推荐去看下试听课。github
更新:算法
感谢哈大沙关于dealloc时,对C++数组回收内存遗漏的指正。数组
为了方便iOS同行朋友的阅读理解,全部的数据结构所有用Objective-C实现。 语言并不能影响数据结构自己的原理,逻辑都是同样的,只是细节会有不一样,好比Objective-C在链表和二叉树中要考虑循环引用的问题,而Java就没必要考虑这些,可是原理和本质都是同样的。bash
就一个缘由吧,Objective-C这家伙没有,只能本身搞一个。数据结构
建立一个数组对象JKRArray,直接继承自NSObject。 首先自定义数组应该和Objective-C原生语言环境相匹配,那么咱们的静态数组也是应该和NSArray同样,存放对象的指针,惟一的区别就是,自定义静态数组须要指定长度初始化,而且初始化以后是一个空数组,而且能够在数组长度范围内的任意位置直接经过下标插入和取出元素,好比初始化一个长度为3的数组后,数组的内部结构应该是:app
[nil, nil, nil]
复制代码
直接经过下标在array[1]插入一个@1后,数组的内部结构应该是:post
[nil, 1, nil]
复制代码
因为数组须要保存对象的指针,并且因为功能限制,内部必需要本身静态数组来存放指针,而Objective-C没有这种功能,咱们就只可以经过C++的数组来实现。将JKRArray.m文件改成JKRArray.mm,在项目的TARGETS-BuildPhases-Compile Sources中将JKRArray.mm的Compiler Flags添加-fno-objc-arc。学习
这里尽可能模仿Objective-C官方语言的接口设计风格,来达到便于记忆接口,方便使用的目的。测试
{
@protected
void ** _array;
NSUInteger _length;
}
- (instancetype)init __unavailable;
+ (instancetype)new __unavailable;
+ (instancetype)arrayWithLength:(NSUInteger)length;
- (instancetype)initWithLength:(NSUInteger)length;
- (NSUInteger)length;
- (void)setObject:(nullable ObjectType)object AtIndex:(NSUInteger)index;
- (nullable ObjectType)objectAtIndex:(NSUInteger)index;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (NSUInteger)indexOfObject:(nullable ObjectType)object;
- (BOOL)containsObject:(ObjectType)object;
@end
@interface JKRArray<ObjectType> (JKRExtendedArray)
- (void)enumerateObjectsUsingBlock:(void (^)(_Nullable ObjectType obj, NSUInteger idx, BOOL *stop))block;
- (_Nullable ObjectType)objectAtIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(_Nullable ObjectType)obj atIndexedSubscript:(NSUInteger)idx;
@end
@interface JKRArray<ObjectType> (NSGenericFastEnumeraiton) <NSFastEnumeration>
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained _Nullable [_Nonnull])buffer count:(NSUInteger)len;
@end
复制代码
接口的定义和功能,都和官方的NSArray所有保持一致,因此大部分接口就不解释了,这里逐一说明一些NSArray存在单又不经常使用的和咱们自定义的接口。
{
// 如下两个成员变量的访问权限设定私有,由于它们不该该被更改
@private
// 指针数组,存放是加入到数组中对象的指针
void ** _array;
// 数组的长度
NSUInteger _length;
}
// 如下定义是为了让咱们的数组只可以经过指定长度来建立,由于静态数据必需要指定长度
- (instancetype)init __unavailable;
+ (instancetype)new __unavailable;
+ (instancetype)arrayWithLength:(NSUInteger)length;
- (instancetype)initWithLength:(NSUInteger)length;
/// 如下两个方法的声明和实现是为了让咱们自定义的数组类可以实现经过[]取值和赋值,例如id obj = array[10]、array[3] = [NSObject new]。
- (_Nullable ObjectType)objectAtIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(_Nullable ObjectType)obj atIndexedSubscript:(NSUInteger)idx;
// <NSFastEnumeration>协议和这个方法是为了实现 for in 快速遍历
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained _Nullable [_Nonnull])buffer count:(NSUInteger)len;
复制代码
初始化接口实现
初始化的同时,建立一个指针数组,长度为初始化方法传入的length。
#pragma mark - 初始化
+ (instancetype)arrayWithLength:(NSUInteger)length {
// 手动管理类方法建立对象须要调用autorelease
return [[[self alloc] initWithLength:length] autorelease];
}
- (instancetype)initWithLength:(NSUInteger)length {
self = [super init];
_length = length;
// 建立一个指针数组
_array = new void*[length]();
return self;
}
复制代码
边界检查
先封装一个方法,用于全部关于index操做的方法执行前,先检查时候越界
#pragma mark - 边界检查
- (void)checkRangeWithIndex:(NSUInteger)index {
if (index < 0 || index >= _length) NSAssert(NO, @"Index: %zd, Length: %zd", index, _length);
}
复制代码
在index位置添加元素
时间复杂度:O(1)
#pragma mark - 添加元素
- (void)setObject:(id)object AtIndex:(NSUInteger)index {
// 检查传入的index是否超过数组的范围
[self checkRangeWithIndex:index];
// 先获取index位置原来存放的元素
id oldObject = [self objectAtIndex:index];
// 若是添加的元素和原来的元素同样,则直接返回。
if (oldObject == object) return;
// 若是添加的元素是新元素,则旧元素引用计数-1
if (oldObject != nil) [oldObject release];
// 新添加元素引用计数+1
if (object) [object retain];
// 新元素指针添加到数组中
*(_array + index) = (__bridge void *)object;
}
复制代码
经过index删除元素
时间复杂度:O(1)
#pragma mark - 删除元素
- (void)removeObjectAtIndex:(NSUInteger)index {
// 检查传入的index是否超过数组的范围
[self checkRangeWithIndex:index];
// 将index位置元素取出
id object = (__bridge id)(*(_array + index));
// 若是该位置存放了元素,删除引用计数-1(由于添加的时候+1)
if (object) [object release];
*(_array + index) = 0;
}
复制代码
经过index获取元素
时间复杂度:O(1)
#pragma mark - 获取元素
- (id)objectAtIndex:(NSUInteger)index {
[self checkRangeWithIndex:index];
// 直接在数组指针中经过index取值便可
id object = (__bridge id)(*(_array + index));
return object;
}
复制代码
枚举全部元素
#pragma mark - 枚举
- (void)enumerateObjectsUsingBlock:(void (^)(id _Nullable, NSUInteger, BOOL * _Nonnull))block {
BOOL stop = NO;
// 遍历长度范围内的全部index取值
for (NSUInteger i = 0; i < _length && !stop; i++) {
id object = [self objectAtIndex:i];
block(object, i, &stop);
}
}
复制代码
获取元素的index
时间复杂度:O(N)
#pragma mark - 元素在数组中存储的第一个下标
- (NSUInteger)indexOfObject:(id)object {
// 若是找不到知足条件的index,就返回NSUIntegerMax(这里和NSArray处理同样)
__block NSUInteger index = NSUIntegerMax;
// 经过遍历,从index为0开始,找到第一个知足的index
[self enumerateObjectsUsingBlock:^(id _Nullable obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (object == obj) {
index = idx;
*stop = YES;
} else if ([object isEqual:obj]){
index = idx;
*stop = YES;
}
}];
return index;
}
复制代码
返回数组是否包含某元素
时间复杂度:O(N)
#pragma mark - 是否包含
- (BOOL)containsObject:(id)object {
// 查看元素的index,若是index<_length则包含(不存在会index为NSUIntegerMax)
NSUInteger index = [self indexOfObject:object];
return index < _length;
}
复制代码
返回数组长度
时间复杂度:O(1)
#pragma mark - 返回长度
- (NSUInteger)length {
return _length;
}
复制代码
支持数组运算符
#pragma mark - 支持数组运算符
- (id)objectAtIndexedSubscript:(NSUInteger)idx {
return [self objectAtIndex:idx];
}
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx {
[self setObject:obj AtIndex:idx];
}
复制代码
支持 for in 快速遍历
#pragma mark - 快速遍历
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id _Nullable [])buffer count:(NSUInteger)len {
if (state->state > 0) return 0;
state->mutationsPtr = (unsigned long*)self;
NSUInteger retCount = state->extra[0];
state->itemsPtr = (id *)(_array + state->extra[1]);
if (retCount == 0) retCount = _length;
if (retCount > len) {
state->extra[0] = retCount - len;
state->extra[1] += len;
retCount = len;
} else {
state->state++;
}
return retCount;
}
复制代码
重写dealloc
#pragma mark - dealloc
- (void)dealloc {
// dealloc以前,要将处理内存在的元素引用计数还原(-1),不然会形成内存泄漏
for (NSUInteger i = 0; i < _length; i++) {
id object = [self objectAtIndex:i];
if (object) [object release];
}
delete _array;
[super dealloc];
}
复制代码
重写 description方法
咱们要让自定义数组也能像NSArray同样,有数组形式的打印
- (NSString *)description {
NSMutableString *mutableString = [NSMutableString string];
[mutableString appendString:[NSString stringWithFormat:@"<%@: %p>: \nlength: %zd\n{\n", self.class, self, _length]];
for (NSUInteger i = 0; i < _length; i++) {
if (i) [mutableString appendString:@"\n"];
id object = [self objectAtIndex:i];
if (object) {
[mutableString appendString:@" "];
[mutableString appendString:[object description]];
} else {
[mutableString appendString:@" "];
[mutableString appendString:@"Null"];
}
}
[mutableString appendString:@"\n}"];
return mutableString;
}
复制代码
初始化测试
JKRArray *array = [JKRArray arrayWithLength:6];
NSLog(@"%@", array);
打印:
<JKRArray: 0x102107cb0>:
length: 6
{
Null
Null
Null
Null
Null
Null
}
复制代码
插入测试
array[2] = [Person new];
NSLog(@"%@", array);
打印:
<JKRArray: 0x1006079f0>:
length: 6
{
Null
Null
<Person: 0x10286bc60>
Null
Null
Null
}
复制代码
删除测试
array[2] = nil;
NSLog(@"%@", array);
打印:
<Person: 0x100713400> dealloc
<JKRArray: 0x100607e20>:
length: 6
{
Null
Null
Null
Null
Null
Null
}
复制代码
有了静态数据,下面就用它为基础实现一个相似于NSMutableArray的自定义动态数组。