索引堆(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
    评论
当然可以,以下是一个使用优化的 Dijkstra 算法的 C 代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> #define MAX_VERTICES 10000 #define INF INT_MAX typedef struct { int v; int w; } Edge; typedef struct { Edge* edges[MAX_VERTICES]; int edge_count[MAX_VERTICES]; int vertex_count; } Graph; typedef struct { int* heap; int* indices; int* dist; int size; } Heap; Graph* create_graph(int vertex_count) { Graph* graph = (Graph*) malloc(sizeof(Graph)); graph->vertex_count = vertex_count; for (int i = 0; i < vertex_count; i++) { graph->edges[i] = (Edge*) malloc(sizeof(Edge) * vertex_count); graph->edge_count[i] = 0; } return graph; } void add_edge(Graph* graph, int u, int v, int w) { Edge edge = {v, w}; graph->edges[u][graph->edge_count[u]++] = edge; } Heap* create_heap(int size) { Heap* heap = (Heap*) malloc(sizeof(Heap)); heap->heap = (int*) malloc(sizeof(int) * size); heap->indices = (int*) malloc(sizeof(int) * size); heap->dist = (int*) malloc(sizeof(int) * size); heap->size = 0; return heap; } void heap_swap(Heap* heap, int i, int j) { int temp = heap->heap[i]; heap->heap[i] = heap->heap[j]; heap->heap[j] = temp; heap->indices[heap->heap[i]] = i; heap->indices[heap->heap[j]] = j; } void heap_push(Heap* heap, int v, int dist) { heap->heap[heap->size] = v; heap->indices[v] = heap->size; heap->dist[v] = dist; int i = heap->size++; while (i > 0) { int p = (i - 1) / 2; if (heap->dist[heap->heap[i]] < heap->dist[heap->heap[p]]) { heap_swap(heap, i, p); i = p; } else { break; } } } int heap_pop(Heap* heap) { int v = heap->heap[0]; heap->indices[v] = -1; heap->heap[0] = heap->heap[--heap->size]; heap->indices[heap->heap[0]] = 0; int i = 0; while (i * 2 + 1 < heap->size) { int l = i * 2 + 1; int r = i * 2 + 2; int j = l; if (r < heap->size && heap->dist[heap->heap[r]] < heap->dist[heap->heap[l]]) { j = r; } if (heap->dist[heap->heap[j]] < heap->dist[heap->heap[i]]) { heap_swap(heap, i, j); i = j; } else { break; } } return v; } void dijkstra(Graph* graph, int s, int* dist) { Heap* heap = create_heap(graph->vertex_count); for (int i = 0; i < graph->vertex_count; i++) { dist[i] = INF; } dist[s] = 0; heap_push(heap, s, 0); while (heap->size > 0) { int u = heap_pop(heap); for (int i = 0; i < graph->edge_count[u]; i++) { int v = graph->edges[u][i].v; int w = graph->edges[u][i].w; if (dist[u] + w < dist[v]) { dist[v] = dist[u] + w; if (heap->indices[v] >= 0) { heap_push(heap, v, dist[v]); } else { heap_push(heap, v, dist[v]); } } } } } int main() { int n, m, s; scanf("%d%d%d", &n, &m, &s); Graph* graph = create_graph(n); for (int i = 0; i < m; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); add_edge(graph, u - 1, v - 1, w); } int dist[MAX_VERTICES]; dijkstra(graph, s - 1, dist); for (int i = 0; i < n; i++) { if (dist[i] == INF) { printf("INF\n"); } else { printf("%d\n", dist[i]); } } return 0; } ``` 希望这个代码能够帮助你理解 Dijkstra 算法的优化实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值