堆(数据结构),这看了,还能忘?_review_面试
Posted 黑桃_K_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了堆(数据结构),这看了,还能忘?_review_面试相关的知识,希望对你有一定的参考价值。
文章目录
一、堆得概念
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储。
在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
理解:每一个父节点小于(大于)子节点,为最小堆(最大堆)。如下图:
**堆的性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树;
图
下面介绍的两种调整算法,对于建堆,插入,删除等十分重要 |
文章中的堆以小堆为例:
二、堆向下调算法
概念:
现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。
向下调整算法有一个前提:左右子树必须是一个堆,才能调整。
图:(小堆)
int a[] = {27,15,19,18,28,34,65,49,25,37};
找到最大子节点,进行交换,然后继续向下进行上述;
代码:
//向下调整(小堆)
void shiftDown(int* arr, int n, int cur){
//找到孩子的位置
//左孩子
int child = 2 * cur + 1;
while (child < n){
//从左孩子中找一个最小的
if (child + 1 < n && arr[child] > arr[child + 1])
++child;
//和当前数据比较
//1.调整
if (arr[child] < arr[cur]){
int tmp = arr[child];
arr[child] = arr[cur];
arr[cur] = tmp;
//更新
cur = child;
child = cur * 2 + 1;
}
else//不调整,孩子》=当前
break;
}
}
三、堆向上调整算法
概念:
将一个节点插入堆时,需要根据大小向调整位置,将插入节点,与父节点比较大小,小则交换,依次向上进行判断是否交换操作,不小与则不动;
图:(大堆)
代码:
//向上更新(小堆)
void shiftUp(int* arr, int n, int cur){
//父节点下标
int parent = (cur - 1) / 2;
while (cur > 0){
//交换
if (arr[cur]<arr[parent]){
int tmp = arr[cur];
arr[cur] = arr[parent];
arr[parent] = tmp;
//更新
cur = parent;
parent = (cur - 1) / 2;
}
//不交换
else{
break;
}
}
}
四、建堆
概念:
下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?
这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。
图:(大堆)
int a[] = {1,5,3,8,7,6};
代码:
//建堆
void test(){
int arr[] = {10,5,8,3,2,1};
int n=sizeof(arr) / sizeof(arr[0]);
//构建一个小堆
for (int i = ((n - 2) / 2); i >= 0; --i)
shiftDown(arr,n, i);
}
五、堆插入:
概念:插入一个数据放在数组的尾部,然后向上调整
图:
先插入一个80到数组的尾上,再进行向上调整算法,直到满足堆。
代码:
//尾插
void heapPush(heap* hp, HDataType val){
//检查空间
checkCapacity(hp);
//尾插
hp->_data[hp->_size++] = val;
//向上调整
shiftUp(hp->_data, hp->_size, hp->_size - 1);
}
六、堆的删除
概念:
删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
图:
代码:
//删除
void heapPop(heap* hp){
if (hp->_data == NULL)
return;
//交换
int tmp = hp->_data[0];
hp->_data[0]=hp->_data[hp->_size - 1];
hp->_data[hp->_size - 1] = tmp;
//尾删
--hp->_size;
//向下调整
shiftDown(hp->_data, hp->_size,0);
}
七、全部代码:
#include<stdlib.h>
#include<stdbool.h>
//假设小堆
typedef int HDataType;
void shiftDown(int* arr, int n, int cur);
void shiftUp(int* arr, int n, int cur);
typedef struct heap{
HDataType* _data;
int _size;
int _capacity;
}heap;
//初始化
void heapInit(heap* hp){
if (hp == NULL)
return;
//空堆
hp->_data = NULL;
hp->_size = 0;
hp->_capacity = 0;
}
//检查空间
void checkCapacity(heap* hp){
if (hp->_size == hp->_capacity){
int newC = hp->_capacity == 0 ? 1 : 2 * hp->_capacity;
hp->_data = (HDataType*)realloc(hp->_data, sizeof(HDataType)*newC);
hp->_capacity = newC;
}
}
//小堆顶点
HDataType heapTop(heap* hp)
{
return hp->_data[0];
}
//尾插
void heapPush(heap* hp, HDataType val){
//检查空间
checkCapacity(hp);
//尾插
hp->_data[hp->_size++] = val;
//向上调整
shiftUp(hp->_data, hp->_size, hp->_size - 1);
}
//删除
void heapPop(heap* hp){
if (hp->_data == NULL)
return;
//交换
int tmp = hp->_data[0];
hp->_data[0]=hp->_data[hp->_size - 1];
hp->_data[hp->_size - 1] = tmp;
//尾删
--hp->_size;
//向下调整
shiftDown(hp->_data, hp->_size,0);
}
//判断为空
bool heapEmpty(heap* hp){
if (hp == NULL || hp->_data == NULL)
return true;
return false;
}
//向下调整
void shiftDown(int* arr, int n, int cur){
//找到孩子的位置
//左孩子
int child = 2 * cur + 1;
while (child < n){
//从左孩子中找一个最小的
if (child + 1 < n && arr[child] > arr[child + 1])
++child;
//和当前数据比较
//1.调整
if (arr[child] < arr[cur]){
int tmp = arr[child];
arr[child] = arr[cur];
arr[cur] = tmp;
//更新
cur = child;
child = cur * 2 + 1;
}
else//不调整,孩子》=当前
break;
}
}
//向上更新(小堆)
void shiftUp(int* arr, int n, int cur){
//父节点下标
int parent = (cur - 1) / 2;
while (cur > 0){
//交换
if (arr[cur]<arr[parent]){
int tmp = arr[cur];
arr[cur] = arr[parent];
arr[parent] = tmp;
//更新
cur = parent;
parent = (cur - 1) / 2;
}
//不交换
else{
break;
}
}
}
//堆排序
void test(){
int arr[] = {16,27,4,3,5,16};
int n = sizeof(arr) / sizeof(arr[0]);
//建堆
for (int i = (n - 2) / 2; i >=0; --i){
shiftDown(arr, n, i);
}
int end = n - 1;
while (end > 0){
//交换
int tmp = arr[end];
arr[end] = arr[0];
arr[0] = tmp;
//下调整
shiftDown(arr, end, 0);
--end;
}
}
/*
void test(){
int arr[] = { 1, 5, 10, 7, 8, 6,3,9,4,2 };
int n=sizeof(arr) / sizeof(arr[0]);
heap hp;
heapInit(&hp);
for (int i = 0; i < n; ++i){
heapPush(&hp, arr[i]);
}
for (int i = 0; i < n - 1; ++i){
heapPop(&hp);
}
}
*/
建堆
//void test(){
// int arr[] = {10,5,8,3,2,1};
// int n=sizeof(arr) / sizeof(arr[0]);
//构建一个小堆
// for (int i = ((n - 2) / 2); i >= 0; --i)
// shiftDown(arr,n, i);
// }
int main(){
test();
return 0;
}
以上是关于堆(数据结构),这看了,还能忘?_review_面试的主要内容,如果未能解决你的问题,请参考以下文章
Java Review - 并发编程_PriorityBlockingQueue原理&源码剖析
Java Review - 并发编程_LinkedBlockingQueue原理&源码剖析