資料結構之索引堆(IndexHeap)
一.概念及其介紹
索引堆是對堆這種資料結構的最佳化,是利用真正元素的索引值來組成一個堆,可以對映出一個最大堆或者最小堆,索引堆可分為最大索引堆(IndexMaxHeap)和最小索引堆(IndexMinHeap)
為什麼要索引堆:
當我們在使用堆這個資料結構的時候,需要對堆進行一系列的操作,如插入,取出等操作,我們會不斷的改變相應資料的位置,此時當資料較大的時候,如每個堆中的資料都是一個較大文字等,在交換位置是就會對計算機有較大消耗;所以這裡我們引入索引堆,索引堆是根據比較真正資料,但改變位置的是對應資料索引的位置真正操作的是對應資料的索引,真正資料位置不變,其優點總結如下:
最佳化了交換元素的消耗。
加入的資料位置固定,方便尋找。
二.結構圖示(全文以最大索引堆為例)
陣列對應元素
構成堆後,如下圖:
三.程式碼實現
我們需要對之前堆的程式碼實現進行改造,換成直接操作索引的思維。首先建構函式新增索引陣列屬性 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 協議》,轉載必須註明作者和本文連結