算法-优先队列与堆排序

我们自己每天使用的电脑能同时运行多个应用程序,没有感觉到卡顿,电脑为每个应用程序的事件分配了一个优先级,移动端的手机也是,通常不管我们是在看电影,发短信只要有电话,电话绝对是优先级最高的。这个时候我们需要一种合理的数据结构删除最大元素和插入元素,我们可以称之为优先队列。实现这种优先队列最合适的数据结构可以通过经典的二叉堆来实现,相对于其他数据结构更高效。

优先队列

开始撸代码之前我们需要明白几个概念:

1.当一棵二叉树的每个节点都大于等于等于它的两个子节点时,它称之为堆有序。

2.二叉堆是一组能够用堆有序的完全二叉树排序的元素,并在数组中按照层级存储。

在一个堆中,索引为index的节点的父节点的位置是index/2,子节点的位置为2*index和2*index+1,用数组实现一个完全二叉树的插入元素和删除最大元素是非常简洁的。

核心代码: 

-(void)insert:(NSString *)value{
    [self.dataSource  addObject:value];
    self.maxIndex=self.maxIndex+1;
    [self swimUp:self.maxIndex];
}


-(void)deleteMax{
    [self swap:1 secondIndex:self.maxIndex];//第一个节点和最后一个节点进行交换
    [self.dataSource removeObjectAtIndex:self.maxIndex--];//删除最后的节点
    [self sinkDown:1];//恢复堆的有序性
}

-(void)swimUp:(NSInteger)index{
    //父节点小于当前的子节点
    while (index>1&&[self lessCompare:index/2 secondIndex:index]) {
        [self swap:index/2 secondIndex:index];
        index=index/2;
    }
}

-(void)sinkDown:(NSInteger)index{
    while (2*index<=self.maxIndex) {
        NSInteger  i=2*index;
        //左右节点大小判断
        if (i<self.maxIndex&&[self lessCompare:i secondIndex:i+1]) {
            i++;
        }
        if (![self lessCompare:index secondIndex:i]) break;
        [self swap:index secondIndex:i];
        index=i;
    }
}


-(BOOL)lessCompare:(NSInteger)firstIndex secondIndex:(NSInteger)secondIndex{
    return [[self.dataSource objectAtIndex:firstIndex] integerValue]<[[self.dataSource objectAtIndex:secondIndex] integerValue];
}

-(void)swap:(NSInteger)firstIndex secondIndex:(NSInteger)secondIndex{
    NSString  *temp=[self.dataSource objectAtIndex:firstIndex];
    self.dataSource[firstIndex]=[self.dataSource objectAtIndex:secondIndex];
    self.dataSource[secondIndex]=temp;
}

插入元素调用insert,删除最大元素调用deleteMax即可,注意数组是从1开始的为了方便的使用二叉树的性质,第一个0位不使用:

       PriorityQueue  *queue=[[PriorityQueue alloc]init];
        NSMutableArray *arr=[[NSMutableArray alloc]initWithCapacity:10];
        [arr addObject:[NSNull null]];
        [arr addObject:@"8"];
        [arr addObject:@"5"];
        [arr addObject:@"6"];
        [arr addObject:@"2"];
        [arr addObject:@"3"];
        [arr addObject:@"4"];
        queue.dataSource=arr;
        queue.maxIndex=[arr count]-1;
        [queue insert:@"7"];
//        [queue deleteMax];
        for (NSInteger i=1; i<[queue.dataSource count]; i++) {
            NSLog(@"数值:%@",queue.dataSource[i]);
        }
        NSLog(@"iOS技术交流群:228407086");
        NSLog(@"原文地址:http://www.cnblogs.com/xiaofeixiang");

测试结果:

堆有序

上面的Demo中从最开始就是堆有序的,但是很多情况我们是一个无序的堆,需要自己重新构造排序,分为两步:

1.首先将堆变成一个大根堆,也就是最大堆。

2. 重复删除最大元素,最后的元素和第一个进行交换,然后进行下沉操作(和有序堆中删除元素的方式一样)。

-(void)sort{
    NSInteger count=[self.dataSource count]-1;
    for (NSInteger k=count/2; k>=1; k--) {
        [self sinkSort:k count:count];
    }
    while (count>1) {
        [self swap:1 secondIndex:count--];
        [self sinkSort:1 count:count];
    }
}

-(void)sinkSort:(NSInteger)index count:(NSInteger)count{
    while (2*index<=count) {
        NSInteger  i=2*index;
        //左右节点大小判断
        if (i<count&&[self lessCompare:i secondIndex:i+1]) {
            i++;
        }
        if (![self lessCompare:index secondIndex:i]) break;
        [self swap:index secondIndex:i];
        index=i;
    }
}

测试代码:

   PriorityQueue  *queue=[[PriorityQueue alloc]init];
        NSMutableArray *arr=[[NSMutableArray alloc]initWithCapacity:10];
        [arr addObject:[NSNull null]];
        [arr addObject:@"1"];
        [arr addObject:@"2"];
        [arr addObject:@"3"];
        [arr addObject:@"4"];
        [arr addObject:@"5"];
        [arr addObject:@"6"];
        [arr addObject:@"7"];
        queue.dataSource=arr;
        queue.maxIndex=[arr count]-1;
        [queue sort];
        for (NSInteger i=1; i<[queue.dataSource count]; i++) {
            NSLog(@"数值:%@",queue.dataSource[i]);
        }
        NSLog(@"iOS技术交流群:228407086");
        NSLog(@"原文地址:http://www.cnblogs.com/xiaofeixiang");

上面的测试是最坏的情况的比较,看下效果:

周一好心情,欢迎探讨~ 

原文地址:https://www.cnblogs.com/xiaofeixiang/p/4606332.html