1.堆
堆是一种特殊的数据结构,可以用来维护一组元素,并支持快速的插入、删除和查找操作。堆通常被用来实现优先队列,也可以用来排序和实现图算法等。
这里我们用数组来实现大根堆,大根堆中父节点的值总是大于子节点。
数组中,(k为索引)
1.规定节点K的子节点为2*K和2*K+1
2.节点k的父节点为2/k
2.从数组的1索引处开始存储元素,便于操作。
实现堆类中的基本成员:
class Heap
{
private:
int* items;
int N;
public:
Heap(int capacity)
{
this->items = new int[capacity+1]; //从1索引开始,长度+1
this->N = 0;
}
void exch(int i, int j)
{
int temp;
temp = items[i];
items[i] = items[j];
items[j] = temp;
}
void insert(int item)
{
items[++N] = item;
swim(N); //上浮法,后边有解析
}
int delMax()
{
if (N == 0)
{
return -1;
}
int max = items[1];
exch(1, N);
items[N] = NULL;
N--;
sink(1); //下沉法,后边有解析
return max;
}
bool less(int i, int j)
{
if (items[i] < items[j])
{
return true;
}
return false;
}
void disAll()
{
for (int i = 1; i <= N; i++)
{
cout << items[i] << endl;
}
}
2.上浮法
insert()插入元素后,新的元素到来打乱了堆的规律——父节点总比子节点大。
于是,要针对这个新元素把它放到适合的索引下。
swim(),上浮法,新元素初始时是在树的顶端处,不断循环追踪它的位置,让它不断与父节点比较,如果大于就交换,直到小于父节点(等于是不是可以替换?),结束循环,新元素也就放到正确的位置上了,不用担心交换的后果,在最开始的几次添加时就已经为后面的添加奠定了基础,父节点总比子节点大。
swim():
void swim(int k)
{
if (k / 2 < 1)
{
return;
}
if(less(k/2, k))
{
exch(k, k / 2);
k = k / 2;
}
else
{
return;
}
swim(k);
}
3.下沉法
交换根节点与最新子节点(N)的元素值后,再让N处指向NULL,以此除去最大值,然后让最小值从根节点往下沉,几乎与上浮法一样,循环追踪其位置,如果小于当前两子节点中最大的节点,就交换位置,直到交换失败。
sink():
void sink(int k)
{
while (2 * k <= N)
{
int max;
if (2 * k + 1 <= N)
{
if (less(2 * k, 2 * k + 1))
{
max = 2 * k + 1;
}
else
{
max = 2 * k;
}
}
else
{
max = 2 * k;
}
if (!less(k, max))
{
return;
}
exch(k, max);
k = max;
}
}
函数验证:
int main()
{
Heap heap(10);
heap.insert(1);
heap.insert(2);
heap.insert(3);
heap.insert(4);
heap.insert(5);
heap.insert(6);
heap.insert(7);
heap.insert(8);
heap.insert(9);
heap.insert(10);
heap.disAll();
int result = 0;
while ((result = heap.delMax() )!= -1)
{
cout << "当前最大值为:" << result << endl;
}
}
结果:
当前数组排序
10
9
6
7
8
2
5
1
4
3
当前最大值为:10
当前最大值为:9
当前最大值为:8
当前最大值为:7
当前最大值为:6
当前最大值为:5
当前最大值为:4
当前最大值为:3
当前最大值为:2
当前最大值为:1