C++实现索引堆及完整测试代码

首先贴一篇我看的博客,写的很清楚。作者:Emma_U

一些解释

索引堆首先是堆,但比堆肯定是更有用。
用处:
1.加速。 索引堆存储的是索引,并不直接存储值。在堆上浮下沉的元素交换的时候,交换索引可比交换值来的快。虽然我代码只实现了int类型的索引堆,但比方说string类型的索引堆,交换两个string,显然没有交换两个int型的索引快。
2.方便改动原数组 比如现在有一个vector,然后我们把它初始化为普通的堆,然后我们突然想改动vector中的第i个元素并恢复堆性质。但我们怎么找这个值在哪呢,毕竟堆只有首元素是可以在O(1)时间访问的。我们只有一个个遍历去寻找,这样时间是O(N),N是堆规模。但索引堆可以做到O(1)时间寻找并更新。虽然寻找并更新后重新恢复堆性质二者都需要O(logN)时间,但对于较大的N,二者的总体时间复杂度一个是O(N),一个是O(logN)。

稍微讲一下原理,因为我也是看文首的那篇博客学习的,也相当于复习了。首先我们声明一个data数组作为我们的原始数据集,除非人为更新其中的值,不然是不变的。data_size为data数组的规模。
另外就是我们堆的主体,一个index数组,大小等于data_size。其中的值初始化为0、1、2、3…data_size-1。也就是data数组所有元素的索引(此时还没初始化堆,所以是顺序的)。之后我们建堆后,data数组是不变的,我们只对index进行改动。最终我们对于index要保持这样的性质:data[index[i]]>data[index[2i+1]] and data[index[i]]>data[index[2i+2]]。 最终的结果就是:如果把index数组的每个元素值index[i]都作为索引改成data[index[i]],那index就是堆,这部分应该蛮好理解的。但到目前为止,我们如果更改data中的某个值,仍然要到index数组中一个个查找哪个是被改的,时间还是O(N),没有优化。目前所做的工作只是提高了建堆、维护堆性质等操作的时间开销,也就是只做了上面优点1的优化,优点2并没有实现。
下面是我觉得很不好理解的部分,怎么O(1)时间查询data数组中的第i个值。这里推荐去看文首的博客,我自我感觉不会讲的比作者更好。我下面针对那篇博客的内容补充一个证明,请先阅读那篇博客,一定要先看,不然你不知道我下面在证明什么!!!
下面的1~5编号的代码在我文后贴的index_heap.cpp中。下面证明map数组的值按照我的代码这样设置是正确的。
在这里插入图片描述

实现代码:

index_heap.h:

#pragma once
// #pragma warning(disable:4996)
#ifndef _INDEX_HEAP_H_
#define _INDEX_HEAP_H_
#include<iostream>
#include<vector>
using std::vector;
using std::cout;
using std::endl;
class index_heap {
	vector<int> data;   //数据数组
	vector<int> index;  //堆,index中存的是data中的索引,不存具体元素
	vector<int> map;    //映射,map[i]为data中索引i的元素在堆中的索引
	int data_size;
public:
	index_heap() :data_size(0) {}
	~index_heap() {}
	index_heap(const vector<int>&);
	void create_max_heap();
	void print();
	void shift_down(size_t);
	void shift_up(size_t);
	void push(int);
	void pop();
	int top();
	void change(size_t, int);
};
#endif

index_heap.cpp

#include"index_heap.h"


index_heap::index_heap(const vector<int>& vec) {
	data = vec;
	data_size = vec.size();
	index.resize(data_size);
	for (int i = 0; i < data_size; ++i) {
		index[i] = i;
	}
	map = index;
	create_max_heap();
}

void index_heap::create_max_heap() {
	for (int i = data_size / 2 - 1; i >= 0; --i) {
		shift_down(i);
	}
}


void index_heap::print() {
	if (data_size == 0) {
		cout << "No data!" << endl;
		return;
	}
	cout << "DATA:" << endl;
	for (int num : data) {
		cout << num << " ";
	}
	cout << endl << "INDEX:" << endl;
	for (int num : index) {
		cout << num << " ";
	}
	cout << endl << "MAP:" << endl;
	for (int num : map) {
		cout << num << " ";
	}
}

//下沉
void index_heap::shift_down(size_t i) {
	if (i >= data_size - 1) {
		return;
	}
	int j = i;
	if (2 * i + 1 < data_size and data[index[2 * i + 1]] > data[index[j]]) {
		j = 2 * i + 1;
	}
	if (2 * i + 2 < data_size and data[index[2 * i + 2]] > data[index[j]]) {
		j = 2 * i + 2;
	}
	if (i != j) {
		int temp = index[i];
		index[i] = index[j];
		index[j] = temp;
		map[index[i]] = i;
		map[index[j]] = j;
		shift_down(j);
	}
}

//上浮
void index_heap::shift_up(size_t i) {
	if (i >= data_size or i == 0) {
		return;
	}
	size_t i_parent = i / 2 - 1;  //i_parent是i的父亲节点
	if (data[index[i_parent]] < data[index[i]]) {
		int temp = index[i];
		index[i] = index[i_parent];
		index[i_parent] = temp;
		map[index[i]] = i;
		map[index[i_parent]] = i_parent;
		shift_up(i_parent);
	}
}

void index_heap::push(int x) {
	data.push_back(x);
	++data_size;
	index.push_back(data_size - 1);
	map.push_back(data_size - 1);
	shift_up(data_size - 1);
}

void index_heap::pop() {
	if (data_size == 0) {
		cout << "No data can be popped!" << endl;
		return;
	}
	size_t p = index[0];
	data.erase(data.begin() + p);
	index[0] = index[data_size - 1];//最后一个元素(即最小的元素)拿到堆顶
	--data_size;                //相当于删除了堆顶
	index.resize(data_size);
	map.resize(data_size);
	map[index[0]] = 0;
	shift_down(0);  //恢复大顶堆性质
}

int index_heap::top() {
	if (data_size == 0) {
		cout << "No data can be topped!" << endl;
		return INT_MAX;
	}
	return data[index[0]];
}

void index_heap::change(size_t i, int num) {
	if (i >= data_size) {
		cout << "Invalid index!" << endl;
		return;
	}
	if (data[i] == num) {
		cout << "Same number,no need to change!" << endl;
		return;
	}
	int original_num = data[i];
	data[i] = num;
	if (original_num > num) {
		shift_down(map[i]);
	}
	else {
		shift_up(map[i]);
	}
}

main.cpp(测试):

#include"index_heap.h"
int main()
{
	vector<int> p = { 15,17,19,13,22,16,28,30,41,62};
	index_heap heap(p);
	heap.print();
	getchar();
	return 0;
}

我的运行截图:

在这里插入图片描述
话说之前根本没听过索引堆这个名词,昨天做leetcode一道双端队列题时候看见这个名词,于是从昨天晚上看到今天下午,搞懂了一个新东西,还蛮有意思的哈。
2019年11月15日 16:05:44

posted @ 2019-11-15 16:07  NeoZy  阅读(272)  评论(0编辑  收藏  举报