3.1 堆的概念
- 堆是一种特殊的二叉树,也是完全二叉树,一般是把堆使用顺序结构来存储
- 堆的底层结构是数组
- 堆也有大堆和小堆之分,根节点最大的堆叫最大堆/大根堆,根节点最小的堆叫最小堆/小根堆
- 堆的性质
- 堆的某个节点值总是不大于或不小于其父节点的值
- 堆总是一棵完全二叉树
- 二叉树的性质
- 对于有N个节点的完全二叉树,对于在下标0~k的数组中有:
3.2 堆的实现
3.2.1 堆的数据结构
typedef int HeapDataType;
typedef struct Heap
{
HeapDataType* arr;
int size;
int capacity;
}Heap;
3.2.2 堆的初始化
void HeapInit(Heap* pheap)
{
assert(pheap);
pheap->arr = NULL;
pheap->size = pheap->capacity = 0;
}
3.2.3 堆插入数据
void Swap(HeapDataType* p1, HeapDataType* p2)
{
HeapDataType tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void AdjustUp(HeapDataType* arr, HeapDataType child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (arr[child] < arr[parent])
{
Swap(&arr[child], &arr[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
break;
}
}
void HeapPush(Heap* pheap, HeapDataType x)
{
assert(pheap);
if (pheap->size == pheap->capacity)
{
int newCapacity = pheap->capacity == 0 ? 4 : 2 * pheap->capacity;
HeapDataType* tmp = (HeapDataType*)realloc(pheap->arr, sizeof(HeapDataType) * newCapacity);
if (tmp == NULL)
{
perror("realloc");
exit(1);
}
pheap->arr = tmp;
pheap->capacity = newCapacity;
}
pheap->arr[pheap->size] = x;
AdjustUp(pheap->arr, pheap->size);
pheap->size++;
}
3.2.4 删除堆顶数据
void Swap(HeapDataType* p1, HeapDataType* p2)
{
HeapDataType tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void AdjustDown(HeapDataType* arr, HeapDataType parent, int n)
{
HeapDataType child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && arr[child] < arr[child + 1])
{
child++;
}
if (arr[child] > arr[parent])
{
Swap(&arr[child], &arr[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
void HeapPop(Heap* pheap)
{
assert(!HeapEmpty(pheap));
Swap(&pheap->arr[0], &pheap->arr[pheap->size - 1]);
pheap->size--;
AdjustDown(pheap->arr, 0, pheap->size);
}
HeapDataType HeapTop(Heap* pheap)
{
assert(pheap);
return pheap->arr[0];
}
3.2.5 堆的判空和求有效数据个数
bool HeapEmpty(Heap* pheap)
{
assert(pheap);
return pheap->size == 0;
}
int HeapSize(Heap* pheap)
{
assert(pheap);
return pheap->size;
}
3.3 堆排序
- 之所以升序要建大堆,是因为最后还需要将堆顶元素与最后元素交换,然后向下调整,具体操作如下:
- 堆排序时,每次首尾交换元素后,最后一个元素就是数的最大元素,因此在向下调整的过程中,child的限制条件为
child<end
,也就说明child移动的过程中是不会到划定范围内的最后一个元素,因为那样child已经越界了,这样也就保证了首尾每次交换的都是划定范围内的最大元素,同理child的兄弟节点child+1<end
,也要保证不能越界,end每次向下调整过后要-1
void HeapSort(HeapDataType* arr, int sz)
{
for (int i = (sz - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(arr, i, sz);
}
int end = sz - 1;
while (end)
{
Swap(&arr[0], &arr[end]);
AdjustDown(arr, 0, end);
end--;
}
}
int main()
{
int arr[] = { 34,18,88,23,67,45 };
int sz = sizeof(arr) / sizeof(arr[0]);
HeapSort(arr,sz);
return 0;
}
- 向下调整算法的时间复杂度:O(logn),n为元素个数
- 堆排序的时间复杂度:n*O(logn),n为元素个数,因为堆排序在建堆时要执行(sz-2)次向下调整算法
void AdjustUp(HeapDataType* arr, HeapDataType child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (arr[child] > arr[parent])
{
Swap(&arr[child], &arr[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
break;
}
}
void HeapSort(HeapDataType* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
AdjustUp(arr, i);
}
int end = sz - 1;
while (end)
{
Swap(&arr[0], &arr[end]);
AdjustDown(arr, 0, end);
end--;
}
for (int j = 0; j < sz; j++)
{
printf("%d ", arr[j]);
}
}
- 总结
- 向上调整算法建堆的时间复杂度为O(n*logn)
- 向下调整算法建堆的时间复杂度为O(n)
- 堆排序的时间复杂度为O(nlogn)
3.4 Top K问题
void CreateNdata()
{
int n = 100000;
srand((unsigned int)time(NULL));
FILE* fin = fopen("data.txt", 'w');
if (fin == NULL)
{
perror("fopen fail!\n");
return;
}
for (int i = 0; i < n; i++)
{
int x = (rand() + i) % 1000000;
fprintf(fin, "%d\n", x);
}
fclose(fin);
}
void TopK()
{
int k = 0;
printf("请输入k:");
scanf("%d", &k);
const char* file = "data.txt";
FILE* fout = fopen(file, 'r');
if (fout == NULL)
{
perror("fout");
exit(2);
}
int* minHeap = (int*)malloc(k * sizeof(int));
if (minHeap == NULL)
{
perror("malloc");
exit(1);
}
for (int i = 0; i < k; i++)
{
fscanf(fout, "%d", &minHeap[i]);
}
for (int i = (k - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(minHeap, i, k);
}
int x = 0;
while (fscanf(fout, "%d", &x) != EOF)
{
if (x > minHeap[0])
{
minHeap[0] = x;
AdjustDown(minHeap, 0, k);
}
}
for (int i = 0; i < k; i++)
{
printf("%d ", minHeap[i]);
}
fclose(fout);
free(minHeap);
minHeap = NULL;
}