原文地址:Objective-C封裝std::priority_queue<>實現優先隊列
最近項目中需要用到優先隊列,google了半天,發現Cocoa Foundation中竟然木有現成的好用的輪子可以拿來用。找了半天,也只有Core Foundation的CFBinaryHeap算是滿足需求,但是CFBinaryHeap需要自己管理釋放對象,而且不能實時更新heap中的值,再一看文檔中提供的方法,辣么多回調列在那里,做為一個前C++開發者,我想我還不如用我熟悉一點的std::priority_queue來實現我的需求吧。
回憶下什么是優先隊列###
講到隊列一般人都知道,先進先出嘛,就和排隊買東西一樣,先來的人排在前面,買完就從隊列里出去了。那什么是優先隊列呢,假設我們生活在一個特別尊老愛幼的社會,每次排隊買東西的時候,都要按照年齡作為優先級比較的參照,年紀大的在最前面,年紀小的在其次,青壯年排在后面,老爺爺老奶奶買完了,才輪到小孩兒,小孩兒們正買著辣條呢呢,忽然又來了個老奶奶,大家于是很懂禮貌的讓老奶奶排到了第一個,等老奶奶搶完了超市里的土雞蛋離開隊伍后,才輪到剛剛正準備買辣條的小孩子繼續買。
用OC封裝std::priority_queue###
STL中的priority_queue是C++基于heap實現的優先隊列模板類,其魯棒性和性能已經經過了無數開發者的考驗。所以我們放心大膽的用吧。
首先定義一下std::priority_queue<>的包裝類:
----PriorityQueue.h----
@interface QueueIntNodeObject : NSObject
@property (nonatomic, assign) NSUInteger compareValue;
@end
@interface PriorityQueue : NSObject
@property (nonatomic, readonly) QueueIntNodeObject *topObject;
@property (nonatomic, readonly) NSUInteger count;
- (void)pushObject:(QueueNodeObject *)myObject;
- (void)popObject;
- (void)popAllObjects;
QueueIntNodeObject是優先隊列中所要管理的對象的基類,目前先實現以NSUInteger做為比較優先級的類型,有需要的可以擴展其他的基類出來。
PriorityQueue是用來包裝std::priority_queue的wrapper。定義幾個常用的屬性和方法。
----PriorityQueue.mm----
#import "PriorityQueue.h"
#include <queue>
class QueueCompare {
public:
bool operator()(QueueIntNodeObject *l, QueueIntNodeObject *r) const {
if (l.compareValue < r.compareValue) {
return true;
}else {
return false;
}
}
};
typedef std::priority_queue<QueueIntNodeObject *, std::vector<QueueIntNodeObject *>, QueueCompare> Queue;
#pragma mark - QueueIntNodeObject
@implementation QueueIntNodeObject
@end
#pragma mark - PriorityQueue
@interface PriorityQueue ()
@property (nonatomic) Queue *priority_queue;
@end
@implementation PriorityQueue
- (instancetype)init {
self = [super init];
if (self) {
_priority_queue = new Queue();
}
return self;
}
- (void)dealloc {
delete _priority_queue;
_priority_queue = NULL;
}
- (QueueIntNodeObject *)topObject {
return !self.priority_queue->empty() ? self.priority_queue->top() : nil;
}
- (NSUInteger)count {
return (NSUInteger)self.priority_queue->size();
}
- (void)popObject {
if (!self.priority_queue->empty()) {
self.priority_queue->pop();
}
}
- (void)pushObject:(QueueIntNodeObject *)myObject {
self.priority_queue->push(myObject);
}
- (void)popAllObjects {
if (!self.priority_queue->empty()) {
delete _priority_queue;
_priority_queue = new Queue();
}
}
@end
QueueCompare定義一個C++類,用來重載()運算符,實現兩個QueueIntNodeObject對象的比較。
typedef std::priority_queue<QueueIntNodeObject *, std::vector<QueueIntNodeObject *>, QueueCompare> Queue;
給priority_queue另外定義個名字,這個實在太長了。
下面就是實現PriorityQueue的幾個方法,每個方法對應的即是操作std::priority_queue的方法。當然別忘了再不使用std::priority_queue的時候delete掉,否則會有內存泄漏。
我們再來看段實例代碼,以前面舉的排隊的例子,先定義一個排隊的人的對象,對象有兩個屬性,名稱和年紀:
@interface Person : QueueIntNodeObject
@property (nonatomic, copy) NSString *name;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;
@end
@implementation SeatInfo
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age {
if (self = [super init]) {
self.name = name;
self.compareValue = age;
}
return self;
}
@end
然后再創建幾個Person對象,放到隊列管理去。
Person *s1 = [[Person alloc] initWithName:@"賈母" age:70];
NSLog(@"Retain count is %ld", CFGetRetainCount((__bridge CFTypeRef)s1));
Person *s2 = [[Person alloc] initWithName:@"寶玉" age:16];
Person *s3 = [[Person alloc] initWithName:@"黛玉" age:15];
Person *s4 = [[Person alloc] initWithName:@"寶釵" age:17];
Person *s5 = [[Person alloc] initWithName:@"妙玉" age:18];
Person *s6 = [[Person alloc] initWithName:@"賈政" age:40];
Person *s7 = [[Person alloc] initWithName:@"鳳姐兒" age:20];
Person *s8 = [[Person alloc] initWithName:@"平兒" age:19];
PriorityQueue *queue = [[PriorityQueue alloc] init];
[queue pushObject:s1];
[queue pushObject:s2];
[queue pushObject:s3];
[queue pushObject:s4];
[queue pushObject:s5];
[queue pushObject:s6];
[queue pushObject:s7];
[queue pushObject:s8];
NSLog(@"Retain count is %ld", CFGetRetainCount((__bridge CFTypeRef)s1));
while (queue.count) {
Person *person = (Person *)[queue topObject];
NSLog(@"%@", person.name);
[queue popObject];
}
NSLog(@"Retain count is %ld", CFGetRetainCount((__bridge CFTypeRef)s1));
這段代碼執行后,會按年齡從大到小輸出person對象,達到了我們想要的按照年紀作為優先級參照輸出的效果。
并且,我在其中加入了輸出person對象引用計數的log,運行后發現,priority_queue在ARC下也很好的管理了計數,person對象在被push到queue中后,queue對其持強引用,引用計數加1,從queue中pop出來后,引用計數減1。所以我們依舊不用擔心如何在ARC中管理內存。
如果想實現更復雜的優先級的控制,只需要實現一個類似于QueueIntNodeObject的類和一個用于比較優先級的類即可。
是不是很簡單啦啦啦~~~
文中代碼都提交到 github 啦:https://github.com/LicoC/PriorityQueue , 歡迎star ??