堆
特点:
- 堆是完全二叉树.
- 堆的某个人节点值总是不大于或者不小它的父亲节点的值.
堆存储结构和节点关系:
堆分为:大堆(大根堆) 小堆(小根堆):
上面图可以看出大根堆和小根堆特点: 节点值总是不小于于它的父亲节点的值或者节点值总是不大于于它的父亲节点的值
堆操作简述:
1.堆向下调整:让调整的结点与其孩子节点进行比较(用于创建堆过程和堆排序)
//向下调整 n:数组长度或堆大小 root:根 ) O(logN)
void ShiftDown(HPDataType* a, int n, int root)
{
assert(a);
int parent = root;
int child = 2 * parent + 1;//定义左孩子
//当前节点是否有孩子
while (child <n)
{
//是否有右孩子 如果左孩子存在,选出孩子中最大的那个
if (child + 1 < n && a[child+1] > a[child])//左孩子存在且左孩子大于右孩子
++child;//更新孩子索引
if (a[child]>a[parent])//判断孩子中最大的节点和父亲节点的关系 如果大于
{
//进行交换
Swap(&a[child], &a[parent]);//传址调用
//更新下一次调整位置
parent = child;
child = 2 * parent + 1;
}
//如果小于 结束此次调整
else
{
break;//以parent为根的子树已经是一个大堆了
}
}
}
2.向上调整:让调整的结点与其父亲结点进行比较 从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树 (用于堆插入)
//向上调整
void shiftUp(HPDataType* a, int n, int child)
{
int parent = (child - 1) / 2;
while (child>0)
{
//判断孩子是否大于父亲
if (a[child] > a[parent])
{
Swap(&a[child], &a[parent]);
//更新
child = parent;
parent = (child - 1) / 2;
}
else
break;
}
}
3.堆插入:
在进行堆插入时,我们可以将要插入的节点先放在最后面,其次对该堆进行不断向上调整,直到满足即可.
4.堆删除:
堆删除指的是删除堆顶数据.因此我们可以将堆顶的数据与根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
5.堆排序
从原堆末尾数据开始,与堆顶数据进行交换,然后进行堆向下调整算法,再交换原堆倒数第二个节点与堆顶数据再进行调整,直到交换到堆顶节点停止.
排序图解:
代码实现堆:
//Heap.h
#ifndef _HEAP_H_
#define _HEAP_H_
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int HPDataType;
typedef struct Heap
{
HPDataType* _a;
int _size;
int _capacity;
}Heap;
void HeapInit(Heap* hp, HPDataType* a, int n);
void HeapDestory(Heap* hp);
void HeapPush(Heap* hp, HPDataType x);
void HeapPop(Heap* hp);
HPDataType HeapTop(Heap* hp);
int HeapSize(Heap* hp);
int HeapEmpty(Heap* hp);
// 堆排序
void HeapSort(Heap* hp);
void HeapPrint(Heap* hp);
void TestHeap();
#endif /*_HAEP_H_*/
//Heap.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"
//向下调整算法 实现大堆
void adjustDown(Heap* hp, int m)
{
int cur = m;
int n;//记录左右结点中较大孩子结点的下标
while (cur*2+1<hp->_size)
{
//找到左右孩子中较大的结点下标
//判断右孩子是否存在
if (cur * 2 + 2 >= hp->_size)//左孩子不存在
{
n = cur * 2 + 1;
}
else
{
//左孩子存在,且右孩子结点大于左孩子结点
if (hp->_a[cur * 2 + 1] > hp->_a[cur * 2 + 2])
{
//交换
n = cur * 2 + 1;
}
//左孩子存在,且右孩子结点小于等于左孩子结点
else
{
//交换
n = cur * 2 + 2;
}
}
//比较较大结点和根的大小
if (hp->_a[cur] < hp->_a[n])
{
//交换
int tmp = hp->_a[cur];
hp->_a[cur] = hp->_a[n];
hp->_a[n] = tmp;
//重置根节点
cur = n;
}
else
{
break;
}
}
}
//创建堆
void HeapInit(Heap* hp, HPDataType* a, int n)
{
assert(hp);
hp->_capacity = n * 2;
hp->_size = n;
hp->_a = (HPDataType*)calloc(hp->_capacity, sizeof(HPDataType));
for (int j = 0; j < n; j++)
{
hp->_a[j] = a[j];
}
//从第一个非叶子节点开始,向前遍历,对每一个叶子结点进行向下调整算法
for (int i = n/2-1; i >= 0; i--)
{
adjustDown(hp, i);
}
}
void HeapDestory(Heap* hp)
{
//防止重复释放
if (hp->_a)
{
free(hp->_a);
}
hp->_size = 0;
hp->_capacity = 0;
}
//堆插入
//先插入一个num到数组的尾上,再进行向上调整算法,直到满足堆。
void HeapPush(Heap* hp, HPDataType x)
{
assert(hp);
//判断容量
if (hp->_capacity = hp->_size)
{
hp->_capacity *= 2;
hp->_a = (HPDataType*)realloc (hp->_a, hp->_capacity*sizeof(HPDataType));
}
int cur = hp->_size;
//先在最后面添加新节点
hp->_a[hp->_size] = x;
hp->_size++;
//再进行向上调整
while (cur>0)
{
if (hp->_a[cur] > hp->_a[(cur-1)/2])
{
//交换
int tmp = hp->_a[cur];
hp->_a[cur] = hp->_a[(cur - 1) / 2];
hp->_a[(cur - 1) / 2] = tmp;
cur = (cur - 1) / 2;
}
else
{
break;
}
}
}
//删除堆顶的数据
//将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
void HeapPop(Heap* hp)
{
if (hp->_size==0)
{
return;
}
hp->_size--;
//进行交换
int tmp = hp->_a[0];
hp->_a[0] = hp->_a[hp->_size];
hp->_a[hp->_size] = tmp;
//进行向下调整
adjustDown(hp, 0);
}
HPDataType HeapTop(Heap* hp)
{
if (hp->_size==0)
{
return (HPDataType)0;
}
return hp->_a[0];
}
int HeapSize(Heap* hp)
{
return hp->_size;
}
int HeapEmpty(Heap* hp)
{
return hp->_size == 0;
}
// 堆排序
void HeapSort(Heap* hp)
{
int tmp = hp->_size;
while (hp->_size>1)
{
HeapPop(hp);
}
hp->_size = tmp;
}
void HeapPrint(Heap* hp)
{
assert(hp);
for (int i = 0; i < hp->_size; i++)
{
printf("%d ", hp->_a[i]);
}
}
void TestHeap()
{
int data[10] = { 6,2,5,4,1,9,8,10,7,3 };
Heap hp;
HeapInit(&hp, data, 10);
HeapPush(&hp, 13);
HeapPush(&hp, 11);
//HeapPop(&hp);
HeapSort(&hp);
HeapPrint(&hp);
HeapDestory(&hp);
}
//TestHead.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"
int main()
{
TestHeap();
system("pause");
return 0;
}