iOS 数据结构之队列

点击上方“iOS开发”,选择“置顶公众号”

关键时刻,第一时间送达!

640?640?wx_fmt=gif


作者:大兵布莱恩特

https://www.jianshu.com/p/d979f70c9ed1

iOS开发整理发布,转载请联系作者获得授权


队列是iOS 中常见的一种数据结构,比如 NSOpeartionQueue 和 GCD 的 各种队列 ,其特点是先进先出(First In First out), 在多线程执行执行多个任务时候 ,放进同一队列的任务是顺次从队列里取出任务并执行的.

gith 代码仓库 : 

https://github.com/ZhaoBingDong/iOS-DataStructures.git


队列在我们生活中也很常见,比如排队购票 去银行办理业务排队办理业务,都是队首的办理完业务后,离开柜台 下一个人接着办理业务

640?wx_fmt=jpeg

本文我们将介绍两种队列实现方式, 数组队列和循环队列.
数组队列是利用我们已经写好的数据结构 ArrayList 来存放元素,每次入队时候 往数组后边添加一个元素,每次出队时候从数组第0个元素位置出队一个元素,但是用数组队列其缺点是 在出队时候 数组里所有元素都要整体往前移动1个位置 .

数组和循环队列所有方法如下

+ (instancetype) arrayQueue;
+ (instancetype) arrayQueueWithCapacity:(NSInteger)capacity;
- (instancetype) initWithCapacity:(NSInteger)capacity;

- (void) enqueue:(ObjectType)obj; ///入队列
- (id) dequeue; ///出队列
- (void) removeAllObjects; ///移除队列里边所有元素

///firstObject
@property (nonatomic,weak) ObjectType firstObject;
///size
@property (nonatomic,assign,readonly) NSInteger size;
///isEmpty
@property (nonatomic,assign,getter=isEmpty) BOOL empty;
#import "ArrayList.h"
#import "ArrayQueue.h"

@interface ArrayQueue ()
{
    ArrayList *_array;
}

@end

@implementation ArrayQueue

+ (instancetype)arrayQueue {
    return [[ArrayQueue alloc] initWithCapacity:10];
}

+ (instancetype)arrayQueueWithCapacity:(NSInteger)capacity {
    return [[ArrayQueue alloc] initWithCapacity:capacity];
}

- (instancetype)initWithCapacity:(NSInteger)numItems {
    if (self = [super init]) {
        _array = [ArrayList arrayWithCapacity:numItems];
    }
    return self;
}

- (void)enqueue:(id)obj {
    [_array addObject:obj];
}

- (id)dequeue {
    [_array removeObjectAtIndex:0];
    return nil;
}

- (id)firstObject {
    return [_array firstObject];
}

- (void)removeAllObjects {
    [_array removeAllObjects];
}

- (NSInteger)size {
    return _array.count;
}
- (BOOL)isEmpty {
    return _array.count == 0;
}

- (NSString *)description {

    NSMutableString *res = [NSMutableString string];
    [res appendFormat:@"ArrayQueue: %p \n",self];
    [res appendString:@"front [ "];
    for (int i = 0; i<_array.count; i++) {
        id object = [_array objectAtIndex:i];
        [res appendFormat:@"%@",object];
        if (i != _array.count - 1) {
            [res appendString:@" , "];
        }
    }
    [res appendString:@" ] tail "];
    return res;
}

- (void)dealloc
{

}

@end

循环队列当出队一个元素后,不需要队列里所有元素往前移动,只需要移动队列 _front 和 _tail 指向的位置 , 可以复用已经出队元素留下的内存空间 ,类似与一个环形 , 其内部实现也是一个数组用来存放入队的元素, 当入队元素个数小于数组元素时 就会将元素放到一个空的内存空间位置,只需要维护下_front 和 _tail 指向位置 用来区分队首和队尾

640?wx_fmt=gif

#import "LoopQueue.h"

static NSInteger const defaultCapacity = 10;
typedef void * AnyObject;

@interface LoopQueue ()
{
@private
    AnyObject *_array;
    NSInteger _front , _tail;
    NSInteger _size;
}

///capacity
@property (nonatomic,assign) NSInteger capacity;

@end

@implementation LoopQueue

- (instancetype)initWithCapacity:(NSInteger)capacity {
    if (self = [super init]) {
        _capacity = capacity + 1;
        size_t size = sizeof(AnyObject) * _capacity;
        _array = calloc(_capacity, size);
        _front = 0;
        _size = 0;
        _tail = 0;
    }
    return self;
}

+ (instancetype)loopQueue {
    return [[LoopQueue alloc] initWithCapacity:defaultCapacity];
}

+ (instancetype)loopQueueWithCapacity:(NSInteger)capacity {
    return [[LoopQueue alloc] initWithCapacity:capacity];
}

- (id)dequeue {
    if (self.isEmpty) {
        @throw [NSException exceptionWithName:@"queue empty" reason:@"Cannot dequeue from an empty queue." userInfo:nil];
        return nil;
    }
    id object = (__bridge_transfer id)(_array[_front]);
    _array[_front] = NULL;
    _front = (_front + 1) % (_size);
    _size--;
    if (_size == self.size * 0.25 && self.size * 0.5 != 0) {
        [self resize:self.size * 0.5];
    }
    return object;
}

- (void)enqueue:(id)obj {
    if (!obj) {
        @throw [NSException exceptionWithName:@"Object empty" reason:@"Object cannot a null object" userInfo:nil];
        return;
    }
    if ((_tail + 1) % self.capacity == _front ) {
        [self resize:self.capacity * 2];
    }
    _array[_tail] = (__bridge_retained AnyObject)obj;
    _tail = (_tail + 1) % self.capacity;
    _size ++;
}

- (void)removeAllObjects {

}

/**
 对数组扩容

 @param capacity 新的容量
 */
- (void) resize:(NSInteger)capacity {

    _capacity = capacity + 1;
    AnyObject *oldArray = _array;
    AnyObject *newArray = calloc(_capacity,sizeof(AnyObject));
    size_t size = sizeof(AnyObject) * self.size;
    memcpy(newArray,oldArray,size); ///对旧的数组进行值的拷贝
    _array = newArray;
    _front = 0;
    _tail = self.size;
    if (oldArray != NULL) {
        free(oldArray);
        oldArray = NULL;
    }

}

- (NSInteger)size {
    return _size;
}

- (BOOL)isEmpty {
    return (_front == _tail);
}

- (id)firstObject {
    if (self.isEmpty) {
        @throw [NSException exceptionWithName:@"queue empty" reason:@"Cannot dequeue from an empty queue." userInfo:nil];
        return nil;
    }
    AnyObject obj = _array[_front];
    return (__bridge id)obj;
}

- (NSString *)description {
    NSMutableString *res = [NSMutableString string];
    [res appendFormat:@"ArrayQueue: %p \n",self];
    [res appendString:@"front [ "];
    for (NSInteger i = 0; i <self.size;i++) {
        id object = (__bridge id)(_array[i]);
        if (!object) continue;
        [res appendFormat:@"%@",object];
        if (i < self.size - 1) {
            [res appendString:@" , "];
            continue;
        }
    }
    [res appendString:@" ] tail "];
    return res;
}

- (void)dealloc
{
    if (_array != NULL) {
        NSInteger i = self.capacity - 1;
        while (i >= 0) {
            AnyObject *obj = _array[i];
            if (obj != NULL)
                CFRelease(obj);
            i--;
        }
        free(_array);
    }
}

@end

测试用例 由于数组队列需要出队和入队时候需要频繁的移动数组里元素索引位置 ,这个操作时耗时的 ,而循环队列只需要把新入队的元素 存放在一个空的位置即可 ,减少了对数组内部元素的移动的操作.因此循环队列比数组队列时间耗时更少 性能更好

*   (void) testStack {

_timeArray = [NSMutableArray array];

///10万次对比 NSMutableArray 和 ArrayList
int number = 100000;
Person *p = [Person new];
for (int i = 0; i<10; i++) {
    CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
    ArrayQueue <Person *> *arrayQueue = [ArrayQueue arrayQueue];
    for (int i = 0; i<number; i++) {
        [arrayQueue enqueue:p];
    }
    [arrayQueue removeAllObjects];
    CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime);
    CFTimeInterval duration = linkTime * 1000.0f;
    [self->_timeArray addObject:@(duration)];
    [NSThread sleepForTimeInterval:0.3f];
}

NSLog(@"ArrayQueue time is %@",[_timeArray valueForKeyPath:@"@avg.self"]);

NSLog(@"********************************");

[_timeArray removeAllObjects];

for (int i = 0; i<10; i++) {
    CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
    LoopQueue <Person *> *loopQueue = [LoopQueue loopQueue];
    for (int i = 0; i<number; i++) {
        [loopQueue enqueue:p];
    }
    [loopQueue removeAllObjects];
    CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime);
    CFTimeInterval duration = linkTime * 1000.0f;
    [self->_timeArray addObject:@(duration)];
    [NSThread sleepForTimeInterval:0.3f];
}

NSLog(@"LoopQueue time is %@",[_timeArray valueForKeyPath:@"@avg.self"]);

}

测试用例对比如下

640?wx_fmt=png

640?wx_fmt=gif640?【点击成为源码大神】

▼点击「阅读原文」进入程序员商城

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值