美文网首页
堆/堆排序/优先级队列

堆/堆排序/优先级队列

作者: ZZ_军哥 | 来源:发表于2022-05-27 11:32 被阅读0次

    一.堆的性质
    1.本身是个数组,但是用完全二叉树的思想来处理数组内容
    2.最大堆的根节点是整个数据中的最大元素,最小堆反之
    3.下面所有的例子都是最大堆作为参照
    4.父节点的值比子节点的值大

    下滤: 当前节点和子节点元素进行比较,如果父节点比子节点要小,那么交换父子节点元素的位置,然后一直将原来的父节点下滤,直至没有比它小的子节点为止
    上滤:当前节点和父节点比较,如果当前节点比父节点大,那么交换,直至当前节点不比父节点大

    比如下图中的数组元素并不满足堆的性质,需要对数组元素进行建堆操作,以满足堆的性质

    1.从最后一个非叶子节点开始下滤,一直持续到根节点,那么整个数组就建好堆了
    2.元素索引的关系:比如当前节点的索引为index
    左子节点的索引:index * 2+1
    右子节点的索引:index * 2+2
    父节点的索引:(index+1)/2
    3.整个完全二叉树最后一个非叶子节点的索引为
    最后一个非叶子节点的索引:count/2-1

    例子:比如数组 [ 50,40,60,45,27,12,80,100],按照完全二叉树的排布书序是下面这个样子的


    wecom-temp-68cf984c41c3424f854f53a6163ffe6b.png

    那么对45下滤的情况,结果为


    wecom-temp-d8c8448171a4392b68491e979751456e.png
    再对60下滤的效果
    wecom-temp-ae1f6afa2a6e6e7bd42d6024e83907ae.png

    再对40下滤的效果


    wecom-temp-4a22b18cef7f8c1380c5ac9bc5537910.png
    再对50下滤的效果
    wecom-temp-c4fc5bd16bde0b0ed49fb40787e9e8af.png

    最后这个数组就建好了一个最大堆, [100,50,80,45,27,12,60,40]

    A.原地建堆并且下滤的代码,通过这段代码就完成了数组建堆操作

    //上滤
    - (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.添加元素后,对最后一个元素进行上滤操作,以维持整个堆的性质

    //上滤
    - (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.删除元素时,移除根元素,并将最后一个元素替换掉根元素,然后对根元素进行下滤,以维持堆的性质

    //移除顶部元素
    - (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.原理:在我们对数组元素建完堆后,根节点的元素最大,那么我们将根节点和最后一个节点进行交换,然后再对根节点进行下滤,并且将之前找到的最大值排除在外,重复执行此操作,那么整个数组就排好序了

    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.原地建堆:完全二叉树将数组元素逐个填充到完全二叉树,从倒数第一个非叶子节点进行下滤操作,那么最终,第一个元素一定是最大值
        for (NSInteger index = (marr.count>>1)-1; index>=0; index--) {
            [self siftDown:index dataArray:marr compareBlock:sortBlock count:heapCount];
        }
        //2.将第一个最大元素和最后面的那个元素进行交换,再对交换后的元素进行下滤操作,并缩小最大堆的范围,循环执行此操作,那么最中数组就排好序列了
        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;
    }
    
    

    三.优先级队列
    1.比如医院来病人了,那么治疗顺序肯定要按紧急程度治疗,总不能别人都快挂了还要排个队等前面的感冒啥的完了之后再治疗
    2.利用堆来实现优先级队列的代码

    下面是完整的堆代码

    typedef NSInteger(^HeapCompareBlock)(NSObject *obj1,NSObject *obj2);
    
    @interface GYJHeap : NSObject
    
    //堆中元素数量
    @property(nonatomic,readonly)NSInteger size;
    //指定的构造方法,必须传入对象的比较方式,数据内容可以先不传,后面加
    - (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
    

    下面是优先级队列的代码

    @interface GYJPriorityQueue : NSObject
    
    //构造方法
    - (instancetype)initWithCompareBlock:(HeapCompareBlock)compareBlock;
    //元素数量
    @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, @"请使用指定的优先级队列构造方法,不要使用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
    

    相关文章

      网友评论

          本文标题:堆/堆排序/优先级队列

          本文链接:https://www.haomeiwen.com/subject/ivymprtx.html