7-15 堆的操作 (20分)
编写代码,实现最小堆(Min-Heap)的操作。
输入格式:
第一行是两个不大于1000的正整数N和K,用空格间隔。其中N是堆的容量,需创建一个容量为N的堆。
接下来K行,是对这个堆的依次的K项插入或删除操作:用 1 x 表示插入元素x;用 -1 表示删除堆顶。
接下来一行是一个不大于1000的正整数M,
接下来一行是M个整数(在整型范围内),用空格间隔,
要求将这M个整数组成的列表调整为一个最小堆。
输出格式:
对于第一个堆的K项操作,每次操作后,在一行中依次序打印堆元素,元素间使用1个空格分隔;
对于第二个堆,在调整完成后,在一行中依次序打印堆元素,元素间使用1个空格分隔。
输入样例:
10 8
1 1
1 2
1 3
1 4
1 5
-1
1 6
-1
8
1 2 3 4 5 6 7 8
输出样例:
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
2 4 3 5
2 4 3 5 6
3 4 6 5
1 2 3 4 5 6 7 8
这道题本身没有太复杂的地方,就我自己和周围一些其他同学的做题情况来看,容易出错的地方有以下两点:
1. 没有判堆满(题目中有设置堆的容量,自然会出现因为堆满而无法插入的情况)。
2. 每当向存储结构中添加一个元素时就上滑调整和把整颗树中每个结点的值全部添加到存储结构中再调整,得到的堆(或者叫做特殊的完全二叉树)是不一定相同的。
虽然algorithm库中包含了make_heap这样的函数(可以用vector向量作为存储结构来建堆),但是这儿还是给出了根据课本写的的堆类和用algorithm库建堆的两种方法。
1.课本中的堆类
#include <iostream>
#include <cstdio>
#define MaxVolume 1000
using namespace std;
class MinHeap
{
public:
MinHeap(int sz = MaxVolume);
MinHeap(int* arr, int n);//通过一个数组建堆
~MinHeap()
{
delete []heap;
}
bool Insert(const int& x);
bool Remove(int& x);
bool IsEmpty()const
{
return (currentSize == 0) ? true : false;
}
void makeEmpty()
{
currentSize = 0;
}
void PrintHeap();
private:
int* heap;//存放最小堆元素的数组
int currentSize;//最小堆中当前元素个数
int maxHeapSize;//最小堆最多允许元素个数
void siftDown(int start, int m);//从start到m号结点向下调整成为最小堆
void siftUp(int start);
//两个调整函数中j总是指向较小的,i总是指向较大的
};
MinHeap::MinHeap(int sz):heap(0), currentSize(0), maxHeapSize(sz)
{
maxHeapSize = (MaxVolume > sz) ? sz : MaxVolume;
heap = new int[maxHeapSize];
if(heap == NULL)
{
cerr << "堆存储分配失败!" << endl;
exit(1);
}
currentSize = 0;
}
MinHeap::MinHeap(int* arr, int n):heap(0), currentSize(0), maxHeapSize(n)
{
maxHeapSize = (MaxVolume < n) ? n : MaxVolume;//如果容量大于1000,就申请
//一个更大的
heap = new int[maxHeapSize];
if(heap == NULL)
{
cerr << "堆存储分配失败!" << endl;
exit(1);
}
for(int i = 0; i < n; i++)
{
heap[i] = arr[i];
}
currentSize = n;
int currentPos = (currentSize - 2) / 2;
while(currentPos >= 0)
{
siftDown(currentPos, currentSize - 1);
currentPos--;
}
}
bool MinHeap::Insert(const int& x)
{
if(currentSize == maxHeapSize)//堆满
return false;
heap[currentSize] = x;
siftUp(currentSize);
currentSize++;
return true;
}
bool MinHeap::Remove(int& x)
{
if(currentSize <= 0)
return false;
x = heap[0];
heap[0] = heap[currentSize - 1];
currentSize--;
siftDown(0, currentSize - 1);
return true;
}
void MinHeap::PrintHeap()
{
if(currentSize > 0)
{
int i;
for(i = 0; i < currentSize - 1; i++)
{
printf("%d ", heap[i]);
}
printf("%d\n", heap[i]);
}
}
void MinHeap::siftDown(int start, int m)
{
int i = start, j = 2 * i + 1;//j为i的左孩子
int temp = heap[i];
while(j <= m)
{
if(j < m && heap[j] > heap[j + 1])//让j指向i的左右孩子中较小的那一个
j++;
if(temp < heap[j])//双亲结点小则不做调整
break;
else
{
heap[i] = heap[j];//孩子上移,i,j下降一层
i = j;
j = 2 * i + 1;
}
}
heap[i] = temp;//回放temp中暂存的元素
}
void MinHeap::siftUp(int start)
{
int j = start, i = (j - 1) / 2;
int temp = heap[j];
while(j > 0)
{
if(heap[i] <= temp)//双亲小则不做调整
break;
else
{
heap[j] = heap[i];
j = i;
i = (j - 1) / 2;
}
heap[j] = temp;
}
}
int main()
{
int n, k, m;
scanf("%d%d", &n, &k);
MinHeap firstHeap(n);
int operate_number, insert_number, i, temp = 0;
for(i = 0; i < k; i++)
{
scanf("%d", &operate_number);
if(operate_number == 1)
{
scanf("%d", &insert_number);
firstHeap.Insert(insert_number);
firstHeap.PrintHeap();
}
else if(operate_number == -1)
{
firstHeap.Remove(temp);
firstHeap.PrintHeap();
}
}
scanf("%d", &m);
int* arr = new int[m];
for(int i = 0; i < m; i++)
{
scanf("%d", &arr[i]);
}
MinHeap secondHeap(arr, m);
secondHeap.PrintHeap();
free(arr);
return 0;
}
2.stl的vector+algorithm的make_heap函数,push_heap函数和pop_heap函数
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
void PrintHeap(vector<int>& Minimum_heap);
int main()
{
int n, k, m;
scanf("%d%d", &n, &k);//n是堆的容量,需创建一个容量为n的堆。
//接下来k行,是对这个堆的依次的k项插入或删除操作:用 1 x 表示插入元素x;
//用 -1 表示删除堆顶。
vector<int> minHeap_1, minHeap_2;
make_heap(minHeap_1.begin(), minHeap_1.end(), greater<int>());
int operate_number, insert_number, i;
for(i = 0; i < k; i++)
{
scanf("%d", &operate_number);
if(operate_number == 1)
{
scanf("%d", &insert_number);
if((int)minHeap_1.size() < n)//堆满则不能插入元素
{
minHeap_1.push_back(insert_number);
push_heap(minHeap_1.begin(), minHeap_1.end(), greater<int>());
}
PrintHeap(minHeap_1);
}
else if(operate_number == -1)
{
if(!minHeap_1.empty())//堆空不可删除
{
pop_heap(minHeap_1.begin(), minHeap_1.end(), greater<int>());
minHeap_1.pop_back();
}
PrintHeap(minHeap_1);
}
}
scanf("%d", &m);
for(int i = 0; i < m; i++)
{
scanf("%d", &insert_number);
minHeap_2.push_back(insert_number);
}
make_heap(minHeap_2.begin(), minHeap_2.end(), greater<int>());
//这一步尤其关键,执行了这一句后会对vector中所有的元素做调整,使之
//成为一个最小堆
PrintHeap(minHeap_2);
return 0;
}
void PrintHeap(vector<int>& Minimum_heap)
{
int heap_size = Minimum_heap.size();
vector<int>::iterator ptr = Minimum_heap.begin();
for (int i = 1; i < heap_size; i++, ptr++)
{
printf("%d ", *ptr);
}
printf("%d\n", *ptr);
}
可以看出STL还是很方便的,但是要是没有搞清楚make_heap和push_heap的机制,还是会出很多问题,刚开始的时候这道题自己写的类可以过,但是make_heap就不行。