一.堆的性質(zhì)
1.本身是個數(shù)組,但是用完全二叉樹的思想來處理數(shù)組內(nèi)容
2.最大堆的根節(jié)點是整個數(shù)據(jù)中的最大元素,最小堆反之
3.下面所有的例子都是最大堆作為參照
4.父節(jié)點的值比子節(jié)點的值大
下濾: 當前節(jié)點和子節(jié)點元素進行比較,如果父節(jié)點比子節(jié)點要小,那么交換父子節(jié)點元素的位置,然后一直將原來的父節(jié)點下濾,直至沒有比它小的子節(jié)點為止
上濾:當前節(jié)點和父節(jié)點比較,如果當前節(jié)點比父節(jié)點大,那么交換,直至當前節(jié)點不比父節(jié)點大
比如下圖中的數(shù)組元素并不滿足堆的性質(zhì),需要對數(shù)組元素進行建堆操作,以滿足堆的性質(zhì)
1.從最后一個非葉子節(jié)點開始下濾,一直持續(xù)到根節(jié)點,那么整個數(shù)組就建好堆了
2.元素索引的關系:比如當前節(jié)點的索引為index
左子節(jié)點的索引:index * 2+1
右子節(jié)點的索引:index * 2+2
父節(jié)點的索引:(index+1)/2
3.整個完全二叉樹最后一個非葉子節(jié)點的索引為
最后一個非葉子節(jié)點的索引:count/2-1
例子:比如數(shù)組 [ 50,40,60,45,27,12,80,100],按照完全二叉樹的排布書序是下面這個樣子的
那么對45下濾的情況,結果為
再對60下濾的效果
再對40下濾的效果
再對50下濾的效果
最后這個數(shù)組就建好了一個最大堆, [100,50,80,45,27,12,60,40]
A.原地建堆并且下濾的代碼,通過這段代碼就完成了數(shù)組建堆操作
//上濾
- (void)siftUp:(NSInteger)index
{
if (index>=_heapArray.count) return;
id obj = _heapArray[index];
while(index>0){
//獲取父元素
NSInteger parentIndex = (index-1)>>1;
id parent = _heapArray[parentIndex];
if (parentIndex>=0 &&_compareBlock(obj,parent)<=0) break;
_heapArray[index] = parent;
index = parentIndex;
}
_heapArray[index] = obj;
}
//原地建堆
- (void)createHeapArray
{
NSInteger count = _heapArray.count;
for (NSInteger index = (count>>1)-1; index>=0; index--) {
[self siftDown:index];
}
}
B.添加元素后,對最后一個元素進行上濾操作,以維持整個堆的性質(zhì)
//上濾
- (void)siftUp:(NSInteger)index
{
if (index>=_heapArray.count) return;
id obj = _heapArray[index];
while(index>0){
//獲取父元素
NSInteger parentIndex = (index-1)>>1;
id parent = _heapArray[parentIndex];
if (parentIndex>=0 &&_compareBlock(obj,parent)<=0) break;
_heapArray[index] = parent;
index = parentIndex;
}
_heapArray[index] = obj;
}
C.刪除元素時,移除根元素,并將最后一個元素替換掉根元素,然后對根元素進行下濾,以維持堆的性質(zhì)
//移除頂部元素
- (NSObject *)removeElementAtTop
{
if (_heapArray.count==0) return nil;
if (_heapArray.count==1) {
NSObject *obj = _heapArray[0];
[_heapArray removeAllObjects];
return obj;
}
//如果存在多個元素,將最后一個元素替換掉0的位置,并且最后一個置為空,同時第一個元素下濾
NSObject *fristObj = _heapArray[0];
NSObject *lastObj = [_heapArray lastObject];
_heapArray[0] = lastObj;
[_heapArray removeLastObject];
[self siftDown:0];
return fristObj;
}
二.堆排序
1.原理:在我們對數(shù)組元素建完堆后,根節(jié)點的元素最大,那么我們將根節(jié)點和最后一個節(jié)點進行交換,然后再對根節(jié)點進行下濾,并且將之前找到的最大值排除在外,重復執(zhí)行此操作,那么整個數(shù)組就排好序了
typedef NSInteger(^SortBlock)(NSObject *obj1,NSObject *obj2);
+ (NSArray *)heapSort:(NSArray *)dataArray compareBlock:(SortBlock)sortBlock;
+ (NSArray *)heapSort:(NSArray *)dataArray compareBlock:(SortBlock)sortBlock
{
if (dataArray==nil || dataArray.count<=1) return dataArray;
NSMutableArray *marr = dataArray.mutableCopy;
NSInteger heapCount = marr.count;
//1.原地建堆:完全二叉樹將數(shù)組元素逐個填充到完全二叉樹,從倒數(shù)第一個非葉子節(jié)點進行下濾操作,那么最終,第一個元素一定是最大值
for (NSInteger index = (marr.count>>1)-1; index>=0; index--) {
[self siftDown:index dataArray:marr compareBlock:sortBlock count:heapCount];
}
//2.將第一個最大元素和最后面的那個元素進行交換,再對交換后的元素進行下濾操作,并縮小最大堆的范圍,循環(huán)執(zhí)行此操作,那么最中數(shù)組就排好序列了
while (heapCount>1) {
--heapCount;
[self swapIndex1:0 index2:heapCount dataArray:marr];
[self siftDown:0 dataArray:marr compareBlock:sortBlock count:heapCount];
}
return marr.copy;
}
//下濾
+ (void)siftDown:(NSInteger)index dataArray:(NSMutableArray *)dataArray compareBlock:(SortBlock)sortBlock count:(NSInteger)count
{
id markObj = dataArray[index];
NSInteger half = count>>1;
while (index<half) {
NSInteger childIndex = (index<<1)+1;
id child = dataArray[childIndex];
NSInteger rightIndex = childIndex+1;
if (rightIndex<count && sortBlock(dataArray[rightIndex],child)>0) {
childIndex = rightIndex;
child = dataArray[childIndex];
}
if (sortBlock(markObj,child)>=0) break;
dataArray[index] = child;
index = childIndex;
}
dataArray[index] = markObj;
}
三.優(yōu)先級隊列
1.比如醫(yī)院來病人了,那么治療順序肯定要按緊急程度治療,總不能別人都快掛了還要排個隊等前面的感冒啥的完了之后再治療
2.利用堆來實現(xiàn)優(yōu)先級隊列的代碼
下面是完整的堆代碼
typedef NSInteger(^HeapCompareBlock)(NSObject *obj1,NSObject *obj2);
@interface GYJHeap : NSObject
//堆中元素數(shù)量
@property(nonatomic,readonly)NSInteger size;
//指定的構造方法,必須傳入對象的比較方式,數(shù)據(jù)內(nèi)容可以先不傳,后面加
- (instancetype)initWithDefaultArray:(nullable NSArray *)defaultArray CompareBlock:(HeapCompareBlock NS_NOESCAPE)compareBlock;
//給堆原來的基礎上添加元素
- (void)continueAddElements:(NSArray *)elements;
//添加一個元素
- (void)addElement:(NSObject *)obj;
//堆是否為空
- (BOOL)isEmpty;
//清空堆
- (void)clear;
//查看最頂部的元素
- (NSObject *)lookTowerTopElement;
//移除頂部元素
- (NSObject *)removeElementAtTop;
@end
#import "GYJHeap.h"
@interface GYJHeap()
@property(nonatomic,strong)NSMutableArray *heapArray;
//元素與元素間比較的block,以確定對象大小關系
@property(nonatomic,copy)HeapCompareBlock compareBlock;
@end
@implementation GYJHeap
//構造方法,必須傳入對象的比較方式
- (instancetype)initWithDefaultArray:(nullable NSArray *)defaultArray CompareBlock:(HeapCompareBlock NS_NOESCAPE)compareBlock
{
if (self = [super init]) {
_compareBlock = compareBlock;
if (defaultArray==nil) {
_heapArray = [[NSMutableArray alloc]init];
}else{
_heapArray = defaultArray.mutableCopy;
[self createHeapArray];
}
}
return self;
}
- (instancetype)init
{
if (self = [super init]) {
NSAssert(NO, @"不要使用這個init方法初始化堆,使用另外一個");
}
return self;
}
//給堆中添加元素
- (void)continueAddElements:(NSArray *)elements
{
if (elements==nil || elements.count==0) return;
for (NSInteger i = 0; i<elements.count; i++) {
[self addElement:elements[i]];
}
}
//添加一個元素
- (void)addElement:(NSObject *)obj
{
if (obj==nil) return;
[_heapArray addObject:obj];
[self siftUp:_heapArray.count-1];
}
//堆是否為空
- (BOOL)isEmpty
{
return _heapArray.count==0?YES:NO;
}
//清空堆
- (void)clear
{
[_heapArray removeAllObjects];
}
//查看最頂部的元素
- (NSObject *)lookTowerTopElement
{
return _heapArray.count>0?_heapArray[0]:nil;
}
//移除頂部元素
- (NSObject *)removeElementAtTop
{
if (_heapArray.count==0) return nil;
if (_heapArray.count==1) {
NSObject *obj = _heapArray[0];
[_heapArray removeAllObjects];
return obj;
}
//如果存在多個元素,將最后一個元素替換掉0的位置,并且最后一個置為空,同時第一個元素下濾
NSObject *fristObj = _heapArray[0];
NSObject *lastObj = [_heapArray lastObject];
_heapArray[0] = lastObj;
[_heapArray removeLastObject];
[self siftDown:0];
return fristObj;
}
- (NSInteger)size;
{
return _heapArray.count;
}
//下濾
- (void)siftDown:(NSInteger)index
{
if (index>=_heapArray.count) return;
id obj = _heapArray[index];
NSInteger halfIndex = _heapArray.count>>1;
while (index<halfIndex) {
//獲取左子元素
NSInteger childIndex = (index<<1)+1;
id child = _heapArray[childIndex];
NSInteger rightIndex = childIndex+1;
//比較左右子元素,確定那個是最大的子元素
if (rightIndex<_heapArray.count &&_compareBlock(_heapArray[rightIndex],child)>0) {
child = _heapArray[rightIndex];
childIndex = rightIndex;
}
//如果本身比左右子元素大,那么直接退出
if (_compareBlock(obj,child)>=0) break;
_heapArray[index] = child;
index = childIndex;
}
_heapArray[index] = obj;
}
//上濾
- (void)siftUp:(NSInteger)index
{
if (index>=_heapArray.count) return;
id obj = _heapArray[index];
while(index>0){
//獲取父元素
NSInteger parentIndex = (index-1)>>1;
id parent = _heapArray[parentIndex];
if (parentIndex>=0 &&_compareBlock(obj,parent)<=0) break;
_heapArray[index] = parent;
index = parentIndex;
}
_heapArray[index] = obj;
}
//原地建堆
- (void)createHeapArray
{
NSInteger count = _heapArray.count;
for (NSInteger index = (count>>1)-1; index>=0; index--) {
[self siftDown:index];
}
}
@end
下面是優(yōu)先級隊列的代碼
@interface GYJPriorityQueue : NSObject
//構造方法
- (instancetype)initWithCompareBlock:(HeapCompareBlock)compareBlock;
//元素數(shù)量
@property(nonatomic,readonly)NSInteger count;
//批量入隊
- (void)enterPriorityQueueDefaultElements:(NSArray *)elements;
//單個入隊
- (void)enterPriorityQueue:(NSObject *)element;
//隊列是否為空
- (BOOL)isEmpty;
//清空隊列
- (void)clear;
//出隊最大或者最小權重的元素
- (NSObject *)outPriorityQueue;
//即將出隊的元素
- (NSObject *)willOutPriorityQueueElement;
@end
#import "GYJPriorityQueue.h"
@interface GYJPriorityQueue ()
@property(nonatomic,strong)GYJHeap *heap;
@end
@implementation GYJPriorityQueue
//構造方法
- (instancetype)initWithCompareBlock:(HeapCompareBlock)compareBlock
{
if (self = [super init]) {
_heap = [[GYJHeap alloc]initWithDefaultArray:nil CompareBlock:compareBlock];
}
return self;
}
- (instancetype)init
{
if (self = [super init]) {
NSAssert(NO, @"請使用指定的優(yōu)先級隊列構造方法,不要使用init");
}
return self;
}
- (NSInteger)count
{
return _heap.size;
}
//批量入隊
- (void)enterPriorityQueueDefaultElements:(NSArray *)elements
{
[_heap continueAddElements:elements];
}
//單個入隊
- (void)enterPriorityQueue:(NSObject *)element
{
[_heap addElement:element];
}
//隊列是否為空
- (BOOL)isEmpty
{
return _heap.size==0?YES:NO;
}
//清空隊列
- (void)clear
{
[_heap clear];
}
//出隊最大或者最小權重的元素
- (NSObject *)outPriorityQueue
{
return [_heap removeElementAtTop];
}
//即將出隊的元素
- (NSObject *)willOutPriorityQueueElement
{
return [_heap lookTowerTopElement];
}
@end