資料結構之索引堆(IndexHeap)

ice_moss發表於2021-05-11

資料結構之索引堆(IndexHeap)

一.概念及其介紹

索引堆是對堆這種資料結構的最佳化,是利用真正元素的索引值來組成一個堆,可以對映出一個最大堆或者最小堆,索引堆可分為最大索引堆(IndexMaxHeap)和最小索引堆(IndexMinHeap)

為什麼要索引堆:

當我們在使用堆這個資料結構的時候,需要對堆進行一系列的操作,如插入,取出等操作,我們會不斷的改變相應資料的位置,此時當資料較大的時候,如每個堆中的資料都是一個較大文字等,在交換位置是就會對計算機有較大消耗;所以這裡我們引入索引堆,索引堆是根據比較真正資料,但改變位置的是對應資料索引的位置真正操作的是對應資料的索引,真正資料位置不變,其優點總結如下:

  • 最佳化了交換元素的消耗。

  • 加入的資料位置固定,方便尋找。

二.結構圖示(全文以最大索引堆為例)

陣列對應元素
LK
構成堆後,如下圖:

LK

三.程式碼實現

我們需要對之前堆的程式碼實現進行改造,換成直接操作索引的思維。首先建構函式新增索引陣列屬性 indexes。

//將資料型別設為模板類
template<typename Item>
class IndexMaxHeap {

//對內容進行私有化,不能讓使用者修改
private:
    //data為堆中的元素
  Item *data;
  //開闢新的空間,用來儲存新的索引
  int *indexes;
  //count為元素對應的索引
  int count;
  //capacity表示堆的容量
  int capacity;

相應建構函式調整為,新增初始化索引陣列。

 //建構函式,構造一個空堆,能容納capacity個元素
  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;

我們要對最大索引堆進行插入操作,這裡我們定義一個insert()函式

//傳入的對使用者而言,是從0索引開始的
void insert(int i, Item item) {

    //對capacity和進行範圍限定
  assert(count + 1 <= capacity);
  assert(i+1>1 && i+1 <= capacity);
  //這裡的索引為1開始的
  i+=1;
  data[i] = item;

調整插入操作,indexes 陣列中新增的元素是真實 data 陣列的索引 indexes[count+1] = i。

  indexes[count+1] = i;
  count++;
  shiftUp(count);
}

和堆一樣,我們需要將新插入的元素和原來堆中元素進行比較,我們交換的只是對應元素的索引,所以我們只需將原來的
data[k / 2] < data[k])
改為
data[indexes[k / 2]] < data[indexes[k]])

再將原來的
swap(data[k / 2], data[k]);
改為
swap(indexes[k / 2], indexes[k]);

下面是完整是shiftUp():

void shiftUp(int k) {  //此時的是用來描述索引陣列的位置
  while (k > 1 && data[indexes[k / 2]] < data[indexes[k]]){
        swap(indexes[k / 2], indexes[k]);
  k /= 2;
  }
}

我們要對最大索引堆中元素進行取出,其邏輯和堆一樣,我們也需要將data[1]改為 data[indexes[1]];( 與shiftUp()同理 )

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

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

下面來實現shiftDown(),其陣列索引方法和shiftUp()同理

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

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

在索引堆中還有一個重要的功能,就是我們需要更新索引堆裡的元素,傳入需要改變值對應的索引i,和修改元素值newItem,其程式碼如下:

void change(int i, Item newItem){

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

完整程式碼如下:

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

using namespace std;

template<typename Item>
class IndexMaxHeap {

//對內容進行私有化,不能讓使用者修改
private:
    //data為堆中的元素
  Item *data;
  //開闢新的空間,用來儲存新的索引
  int *indexes;
  //count為元素對應的索引
  int count;
  //capacity表示堆的容量
  int capacity;

 void shiftUp(int k) {  //此時的是用來描述索引陣列的位置
  while (k > 1 && data[indexes[k / 2]] < data[indexes[k]]){
            swap(indexes[k / 2], indexes[k]);
  k /= 2;
  }
    }

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

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

public:

    //建構函式,構造一個空堆,能容納capacity個元素
  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;
  }
    //傳入的對使用者而言,是從0索引開始的
  void insert(int i, Item item) {

        //對capacity和進行範圍限定
  assert(count + 1 <= capacity);
  assert(i+1>1 && i+1 <= capacity);
  //這裡的索引為1開始的
  i+=1;
  data[i] = item;
  indexes[count+1] = i;
  count++;
  shiftUp(count);
  }

    void 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){
        return data[i+1];
  }

    void change(int i, Item newItem){

        i+=1;
  data[i] = newItem;
  //找到indexes[j] = 1,j表示data[i]在堆中的位置
 //之後shiftUp(j), shiftDown(j)  
     for(int j = 1; j <= count; j++)
            if(indexes[j] == i) {
                shiftUp(j);
                shiftDown(j);
  }
                return;
  }
};
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章