数据结构—堆

一、定义

堆:实际上是一颗 完全二叉树 ,但是它还满足父结点大于(或小于)子结点特性。父结点大于子结点称为最大堆(或大顶堆,array[i]>=array[2i+1] && array[i]>=array[2i+2],i从0开始),父结点小于子结点称为最小堆(或小顶堆,array[i]<=array[2i+1] && array[i]<=array[2i+2] ,i从0开始)源码地址 GitHub

源码位置

注意:本文全部已最大堆为例,之后不再说明,最小堆与最大堆条件完全相反,不再详细介绍

二、调整

这里先简单介绍一下,稍后详细演示调整过程

向下调整:从父结点、左孩子、右孩子三个结点中选择最大的与父结点进行交换,交换后可能造成孩子结点不满满足堆的性质,因此每次交换后需要从新对交换后的孩子结点进行调整

//交换
void swap(int *x, int *y){
    *x ^= *y;
    *y ^= *x;
    *x ^= *y;
}
// 向下调整

// 非递归实现
// array 是待调整的堆数组,i是待调整的数组元素的位置,nlength是数组的长度
// 本函数功能是:根据数组array构建大根堆
void heapDownAdjust(int array[],int i,int nLength) {
    int nChild;
    while(2*i+1 < nLength) {
        nChild = 2*i+1;                 // 左孩子(2*(父节点位置)+ 1)
        if(nChild<nLength-1 && array[nChild+1]>array[nChild])
            ++nChild;                   // 得到子节点中较大的节点
        if(array[i] >= array[nChild]) {
            break;
        }
        swap(array+i,array+nChild);     // 较大子节点大于父节点向上移动
        i=nChild;
    }
}

// 递归实现
void heapDownRecursiveAdjust(int array[],int i,int nLength) {
    int nChild;
    if (2*i+1 < nLength) {
        nChild = 2*i+1;   // 左孩子(2*(父节点位置)+ 1)
        if((nChild < nLength-1) && (array[nChild+1] > array[nChild])) // 得到子节点中较大的节点
            ++nChild;
        if(array[i] < array[nChild]){
            swap(array+i,array+nChild);
            heapDownRecursiveAdjust(array, nChild, nLength);
        }
    }
}

向上调整:当前结点与其父结点进行比较,如果比父结点大进行交换,否则退出,依次比较上移直到根结点

// 向上调整
// 非递归实现
void heapUpAdjust(int *array, int index, int nLength) {
    int i = index;
    int j = (i-1)/2;            // 父节点
    int temp = array[i];
    while(i > 0){
        if(temp <= array[j]) break;
        array[i] = array[j];    // 比交换高明
        i = j;
        j = (j-1)/2;            // 移动到下一节点
    }
    array[i] = temp;
}

// 递归实现
void heapUpRecursiveAdjust(int array[], int index, int nLength) {
    int i = index;
    int j = (i-1)/2;
    if(i > 0){
        if(array[i] <= array[j]) return;
        swap(array+i, array+j);
        heapUpRecursiveAdjust(array, j, nLength);
    }
}

三、创建

  • 创建堆就是从最后一个非叶子结点到根结点不断进行调整的过程

举例说明:给定数组 array[6]={4,5,6,9,8,7} ,创建最大堆
1.构建完全二叉树:


1.构建完全二叉树

2.调整结点6(6与7交换)

2.调整结点6

3.调整结点5(5与9交换)


调整结点5

4.调整结点4(4与9交换,交换后由于子节点不满足性质,4与8交换)


调整结点4

此时最大堆以构建完毕!

// 创建堆
void createHeap(int *array,int length) {
    int i;
    // 调整序列的前半部分元素,调整完之后第一个元素是序列的最大的元素
    // length/2-1 是最后一个非叶节点,此处"/"为整除
    for(i = length/2-1; i >= 0; --i)
        heapDownAdjust(array,i,length);
        //heapDownRecursiveAdjust(array,0,i);
}

四、插入

  • 把新数据放在数组最后,然后再进行向上调整

以我们创建的最大堆为例


插入结点10:
1.将结点10插入最后


将结点10插入最后

2.子结点10与父结点7交换


子结点10与父结点7交换

3.子结点10与父结点9交换


子结点10与父结点9交换

交换到根结点完成!!!

// 插入元素
int insertElement(int *array, int length, int element) {
    if (length) {
        array[length] = element;  // 放入元素,这里注意数组长度要大于length+1
        ++length;
        heapUpAdjust(array, length-1, length);
        //heapUpRecursiveAdjust(array, length-1, length);
        return length;
    }
    return -1;
}

五、删除(根据堆得性质,只能删除根结点)

  • 将根结点与最后一个结点交换后,再对根结点向下调整

以我们创建的最大堆为例


删除过程:
1.将根结点9删除,最后的结点6移至根结点(排序时将这两个结点交换)


根结点9删除,结点6根结点

2.由于根结点6不满足堆的性质,调整根结点

调整根结点
// 删除堆元素(堆只能删除根元素)
int deleteElement(int *array, int length) {
    if (length) {
        swap(array,array+length-1);         // 根节点与最后一个节点交换
        heapDownAdjust(array,0,length-1);   // 向下交换
        return length-1;
    }
    return 0;
}

六、排序

将根结点与最后一个结点进行交换,再对根结点进行向下调整,最后的元素向前移动一个位子继续做上述过程,直到根结点

以上边创建的堆为例,我们来进行排序


已创建好的最大堆

排序过程:
1.将根结点9与最后一个结点6进行交换


9与6交换(然后调整6)

2.将根结点8与最后一个结点4进行交换


8与4交换(然后调整4)

3.将根结点7与最后一个结点5进行交换


7与5交换(然后调整5)

4.将根结点6与最后一个结点4进行交换

6与4交换(然后调整4)

5.将根结点5与最后一个结点4进行交换

5与4交换

6.已剩最后一个结点,排序完成 {4,5,6,7,8,9}

排序完成
// 堆排序算法(传入 array 已是堆)
void heapSort(int *array,int length) {
    int i;
    //从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
    for(i = length-1; i > 0; --i) {
        // 把第一个元素和当前的最后一个元素交换,
        // 保证当前的最后一个位置的元素都是在现在的这个序列之中最大的
        swap(array,array+i);
        heapDownAdjust(array,0,i); // 不断缩小范围,每一次调整完毕第一个元素是当前序列的最大值
        // heapDownRecursiveAdjust(array,0,i);
    }
}

七、测试

    int  array[] = {12, 14, 54, 5, 6, 3, 9, 8, 47, 89, -1};
    for (int i=0; i< 10; i++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");

    printf("创建堆:");
    createHeap(array,10);
    for (int i=0; i < 10; i++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");

    printf("删除元素:");
    int length = deleteElement(array,10);
    for (int i=0; i < length; i++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");

    printf("插入元素:");
    length = insertElement(array,length,99);
    for (int i=0; i < length; i++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");

    printf("堆排序:");
    heapSort(array, length);
    for (int i=0; i < length; i++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");

关于堆先介绍到这里,如有疑问或是错误之处,欢迎留言!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容