数据结构与算法C++之索引堆

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/majinlei121/article/details/84098519

前几篇博客介绍了数据结构堆以及堆排序,但是前面用到的堆有以下不足
(1)在构建堆时,需要首先将数组元素放入堆中,然后通过交换操作(ShiftDown)将合适的元素放入合适的位置,以满足堆的性质,当数组元素是整数之类比较简单的数据类型时,没什么问题,但是当数组元素特别复杂时,如每一个元素都是一个含有十万字文章的字符串时,交换操作就会消耗很大的资源,但是这种也可以通过计算机强大的软硬件能力来解决。
(2)第二个问题相对来讲就比较致命,在构建堆时,会将数组元素的索引改变,这样再想找到数组原来的索引时就很困难了,如下图所示原来数组中元素15对应的索引为1,但是构建完堆之后(下面第二张图),元素15对应的索引变为8了
在这里插入图片描述
在这里插入图片描述
索引堆(Index Heap)就是为了解决上面的两个问题提出来的
索引堆将索引(index)和数据(data)分开存储,真正表征堆的数组是由索引构建成的(下图中的index),
在这里插入图片描述
将上图数组构建成索引堆后,如下图
在这里插入图片描述
可以看出data数组内的元素没有变化,变化的是index数组
堆顶索引index为10,那么10对应的data中的元素是62,10的左节点为索引9, 9对应的data中的元素为41,10的右节点为索引7,7对应的data中的元素为28 …
下面是最大索引堆的构造函数

#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cmath>
#include <cassert>
#include <typeinfo>

using namespace std;

template<typename Item>
class IndexMaxHeap{
private:
    Item *data;
    int *indexes;
    int count;
    int capacity;

    void ShiftUp(int k){
        while (data[indexes[k/2]] < data[indexes[k]] && k > 1){
            swap(indexes[k/2], indexes[k]);
            k /= 2;
        }
    }

    void ShiftDown(int k){
        while (k <= count/2){
            int j = 2*k; //此轮循环中,data[k]和data[j]交换位置
            if (data[indexes[j]] < data[indexes[j+1]] && j + 1 <= count)
                j += 1;
            if (data[indexes[k]] >= data[indexes[j]])
                break;

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

public:
    IndexMaxHeap(int capacity){
        data = new Item[capacity + 1];
        indexes = new int[capacity + 1];
        count = 0;
        this->capacity = capacity;

    }

    ~MaxHeap(){
        delete[] data;
        delete[] indexes;
    }

    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;

        count ++;
        ShiftUp(count);
    }

    Item extractMax(){//删除提取元素
        assert( count > 0);

        Item ret = data[indexes[1]];

        swap(indexes[1], indexes[count]);
        count --;
        ShiftDown(1);

        return ret;
    }

    int extractMaxIndex(){//提取最大元素索引
        assert( count > 0);

        int ret = indexes[1] - 1;

        swap(indexes[1], indexes[count]);
        count --;
        ShiftDown(1);

        return ret;
    }

    Item getItem(int i){//提取data数组中的值
        return data[i+1];
    }

    void change(int i, Item newItem){//改变堆中的元素

        i += 1;
        data[i] = newItem;

        //找到indexes[j] = i, j表示data[i]在堆中的位置
        //之后shiftUp(j),再shiftDown(j),两者可交换顺序
        for (int j = 1; j <= count; j++){ //算法复杂度 n+logn,也就是 O(n)
            if (indexes[j] == i){
                ShiftUp(j);
                ShiftDown(j);
                return;
            }
        }
    }
};

程序的最后一个函数是改变堆的元素,如下

void change(int i, Item newItem){//改变堆中的元素

        i += 1;
        data[i] = newItem;

        //找到indexes[j] = i, j表示data[i]在堆中的位置
        //之后shiftUp(j),再shiftDown(j),两者可交换顺序
        for (int j = 1; j <= count; j++){ //算法复杂度 n+logn,也就是 O(n)
            if (indexes[j] == i){
                ShiftUp(j);
                ShiftDown(j);
                return;
            }
        }
    }

上面的程序中使用了for循环,导致算法复杂度为O(n),我们希望复杂度为O(logn)
下面使用反向查找来改进
在这里插入图片描述
在这里插入图片描述
使用反向查找的实现程序如下(需要在程序中维护reverse数组):
使用反向查找后计算复杂度降低为O(logn)

#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cmath>
#include <cassert>
#include <typeinfo>

using namespace std;

template<typename Item>
class IndexMaxHeap{
private:
    Item *data;
    int *indexes;
    int *reverse;
    int count;
    int capacity;

    void ShiftUp(int k){
        while (data[indexes[k/2]] < data[indexes[k]] && k > 1){
            swap(indexes[k/2], indexes[k]);
            reverse[indexes[k/2]] = k / 2;
            reverse[indexes[k] = k;
            k /= 2;
        }
    }

    void ShiftDown(int k){
        while (k <= count/2){
            int j = 2*k; //此轮循环中,data[k]和data[j]交换位置
            if (data[indexes[j]] < data[indexes[j+1]] && j + 1 <= count)
                j += 1;
            if (data[indexes[k]] >= data[indexes[j]])
                break;

            swap(indexes[j], indexes[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;

    }

    ~MaxHeap(){
        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[1]] = 1;
        reverse[indexes[count]] = 0;
        count --;
        ShiftDown(1);

        return ret;
    }

    int extractMaxIndex(){//提取最大元素索引
        assert( count > 0);

        int ret = indexes[1] - 1;

        swap(indexes[1], indexes[count]);
        reverse[indexes[1]] = 1;
        reverse[indexes[count]] = 0;
        count --;
        ShiftDown(1);

        return ret;
    }

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

    Item getItem(int i){//提取data数组中的值
        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++){ //算法复杂度 n+logn,也就是 O(n)
//            if (indexes[j] == i){
//                ShiftUp(j);
//                ShiftDown(j);
//                return;
//            }
//        }

        int j = reverse[i]; //算法复杂度 1+logn,也就是 O(logn)
        ShiftUp(j);
        ShiftDown(j);
    }
};

猜你喜欢

转载自blog.csdn.net/majinlei121/article/details/84098519