索引堆(Index Heap)及其优化 —— C++

引入索引堆的原因:堆构建前后数组元素位置发生改变,其具有一定的局限性:
1.元素是非常复杂的结构,交换消耗本身是巨大的,比如每一个元素都是一篇十万字以上的文章,对于这些大型的字符串,让它们来回交换,会产生大量的性能上的消耗
2.由于整个元素在数组中的位置发生改变,使得当我们的堆建成之后,很难所引导到它,以至于我们很难去改变它,比如说我们这些元素表示的是一个一个的系统任务,初始的时候,数值的索引是系统进程的 ID 号,可是将数组构建成堆之后,这些索引和系统进程之间就失去关联,如果现在我们想将原来进程中 ID 号为 6 的系统任务的优先性提一提,这个操作就变得非常难。
索引堆:我们将数据和索引两部分内容分开存储,而真正表征堆的数组,是由索引构建成的
构建堆之前:
在这里插入图片描述
将数组构建成堆之后:
在这里插入图片描述
索引堆与普通堆的区别:思路和普通堆的创建一样,不过在做元素比较的时候比的是date 的数据,而做元素交换的时候,交换的是索引 index 的数据
用处示例:将进程号为 7 的系统任务优先级提高,即令 data[7] = 38,之后进行一定操作,来维持堆的性质(根据 date 数组来改变 index 数组)
代码

template<typename Item>
class IndexMaxHeap{
private:
    Item *data;//用来存储堆元素的数组
    int *indexes;用来存储索引元素的数组
    int count;//用来统计元素个数
    int capacity;//让用户指定堆的大小
    //插入元素需要用到
    void shiftUp( int k ){
        //先拿到 indexes[k] 位置上存放的索引,然后才能通过这个索引找到 date
        while( k > 1 && data[indexes[k/2]] < data[indexes[k]] ){
            swap( indexes[k/2] , indexes[k] );
            k /= 2;
        }
    }
    //删除函数需要用到
    void shiftDown( int k ){
        while( 2*k <= count ){
            int j = 2*k;
            if( j + 1 <= count && data[indexes[j+1]] > data[indexes[j]] )
                j += 1;

            if( data[indexes[k]] >= data[indexes[j]] )
                break;

            swap( indexes[k] , indexes[j] );
            k = j;
        }
    }

public:
    IndexMaxHeap(int capacity){//堆的构造函数
        //创建数据数组
        data = new Item[capacity+1];
        //创建索引数组
        indexes = new int[capacity+1];
        count = 0;//元素个数
        this->capacity = capacity;//堆的大小,由用户指定
    }

    ~IndexMaxHeap(){//析构函数
        delete[] data;
        delete[] indexes;
    }

    int size(){//统计堆中元素个数
        return count;
    }

    bool isEmpty(){//检测堆是否为空
        return count == 0;
    }
    //插入元素函数
    // 传入的 i 对用户而言,是数组的索引,i 是从 0 开始计算的
    //而我们声明的堆,第 0 位是空的,是从 1 开始计算的,所以要在内部处理这个差异
    void insert(int i, Item item){
        //检测数据输入的合法性
        assert( count + 1 <= capacity );
        assert( i + 1 >= 1 && i + 1 <= capacity );
        //处理用户输入差异
        i += 1;
        
        data[i] = item;
        // indexes 数组添加上新的索引
        indexes[count+1] = i;
        count++;
        //将新添元素向上挪动位置,以使其满足堆得性质
        shiftUp(count);
    }
    //取出堆顶元素
    Item extractMax(){
        //监测数据输入合法性
        assert( count > 0 );
        //此时堆顶数据不是 date[1] ,而是 data[indexes[1]]
        Item ret = data[indexes[1]];
        //交换的应该是索引数组中的元素
        swap( indexes[1] , indexes[count] );
        count--;
        
        shiftDown(1);
        return ret;
    }
    //取出堆顶元素并获得索引值
    int extractMaxIndex(){
        assert( count > 0 );
        //对外部用户来说,索引是从 0 开始的
        int ret = indexes[1] - 1;
        swap( indexes[1] , indexes[count] );
        count--;
        //将堆顶元素向下挪动位置,以使其满足堆得性质
        shiftDown(1);
        return ret;
    }

    Item getMax(){
        assert( count > 0 );
        return data[indexes[1]];
    }
    
    int getMaxIndex(){
        assert( count > 0 );
        return indexes[1]-1;
    }
    //给定索引值来获取相应的 date
    Item getItem( int i ){
        return data[i+1];
    }
    //将索引为 i 的内容修改为新的 item
    void change( int i , Item newItem ){
        i += 1;
        data[i] = newItem;
        //为了维持堆的性质  
        // 需要找到 j,j 表示data[i]在堆中的位置,而 indexes[j] = i, 
        // 之后尝试着将其向上挪(shiftUp(j)), 再尝试着将其向下挪(shiftDown(j))

        for( int j = 1 ; j <= count ; j ++ )
            if( indexes[j] == i ){
                shiftUp(j);
                shiftDown(j);
                return;
            }
    }

template<typename T>
void heapSortUsingIndexMaxHeap(T arr[], int n){
    //堆的声明
    IndexMaxHeap<T> indexMaxHeap = IndexMaxHeap<T>(n);
    for( int i = 0 ; i < n ; i ++ )
        indexMaxHeap.insert( i , arr[i] );

    for( int i = n-1 ; i >= 0 ; i -- )
        arr[i] = indexMaxHeap.extractMax();
}

优化:反向查找
在这里插入图片描述
代码

template<typename Item>
class IndexMaxHeap{
private:
    Item *data;//存放堆中数据
    int *indexes;//存放索引
    int *reverse;//存放索引在堆中的位置
    int count;
    int capacity;

    void shiftUp( int k ){
        while( k > 1 && data[indexes[k/2]] < data[indexes[k]] ){
            swap( indexes[k/2] , indexes[k] );
			// indexes[k/2] 在堆中的位置标记目前还是 k
			// indexes[k] 在堆中的位置标记目前还是 k/2
            reverse[indexes[k/2]] = k/2;
            reverse[indexes[k]] = k;
            k /= 2;
        }
    }

    void shiftDown( int k ){
        while( 2*k <= count ){
            int j = 2*k;
            if( j + 1 <= count && data[indexes[j+1]] > data[indexes[j]] )
                j += 1;

            if( data[indexes[k]] >= data[indexes[j]] )
                break;

            swap( indexes[k] , indexes[j] );
			// indexes[k] 在堆中的位置标记目前还是 j
            // indexes[j] 在堆中的位置标记目前还是 k
            reverse[indexes[k]] = k;
            reverse[indexes[j]] = j;
            k = j;
        }
    }
public:
    IndexMaxHeap(int capacity){
        data = new Item[capacity+1];
        indexes = new int[capacity+1];
        reverse = new int[capacity+1];
        for( int i = 0 ; i <= capacity ; i ++ )
            reverse[i] = 0;
        count = 0;
        this->capacity = capacity;
    }

    ~IndexMaxHeap(){
        delete[] data;
        delete[] indexes;
        delete[] reverse;
    }

    int size(){
        return count;
    }

    bool isEmpty(){
        return count == 0;
    }

    // 传入的i对用户而言,是从0索引的
    void insert(int i, Item item){
        assert( count + 1 <= capacity );
        assert( i + 1 >= 1 && i + 1 <= capacity );

        i += 1;
        data[i] = item;
        indexes[count+1] = i;
        reverse[i] = count+1;
        count++;

        shiftUp(count);
    }

    Item extractMax(){
        assert( count > 0 );

        Item ret = data[indexes[1]];
        swap( indexes[1] , indexes[count] );
        reverse[indexes[count]] = 0;
        reverse[indexes[1]] = 1;
        count--;
        shiftDown(1);
        return ret;
    }

    int extractMaxIndex(){
        assert( count > 0 );

        int ret = indexes[1] - 1;
        swap( indexes[1] , indexes[count] );
        reverse[indexes[count]] = 0;
        reverse[indexes[1]] = 1;
        count--;
        shiftDown(1);
        return ret;
    }

    Item getMax(){
        assert( count > 0 );
        return data[indexes[1]];
    }

    int getMaxIndex(){
        assert( count > 0 );
        return indexes[1]-1;
    }

    bool contain( int i ){
        assert( i + 1 >= 1 && i + 1 <= capacity );
        return reverse[i+1] != 0;
    }

    Item getItem( int i ){
        assert( contain(i) );
        return data[i+1];
    }

    void change( int i , Item newItem ){
        assert( contain(i) );
        i += 1;
        data[i] = newItem;

//       找到indexes[j] = i, j表示data[i]在堆中的位置
//       之后shiftUp(j), 再shiftDown(j)
//        for( int j = 1 ; j <= count ; j ++ )
//            if( indexes[j] == i ){
//                shiftUp(j);
//                shiftDown(j);
//                return;
//            }
		// reverse[i] 表示索引在堆中的位置 
        int j = reverse[i];
        shiftUp( j );
        shiftDown( j );
    }

template<typename T>
void heapSortUsingIndexMaxHeap(T arr[], int n){

    IndexMaxHeap<T> indexMaxHeap = IndexMaxHeap<T>(n);
    for( int i = 0 ; i < n ; i ++ )
        indexMaxHeap.insert( i , arr[i] );

    for( int i = n-1 ; i >= 0 ; i -- )
        arr[i] = indexMaxHeap.extractMax();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值