1. 定义
1. 堆的含义:堆的本质是二叉树;
2. 大根堆:根节点是整颗树的最大值;
3. 小根堆:根节点是整棵树的最小值;
堆排序步骤:
Step1:将给定的数组构建除二叉树。
Step2:对该数组(二叉树)进行调整。
Step3:对调整后数组(二叉树)进行排序、调整。
相关概念:
1. 与排序二叉树的区别:排序二叉树不能有重复的数值,且根的左侧小于根节点的数值,根右侧大于根节点的数值。
2. 满二叉树:所有的终端节点都在同一层上,并除根结点外都有两个孩子;
3. 完全二叉树:除第 m 层外,其它各层 (1~m-1) 的结点数都达到最大个数,第 m 层所有的结点都连续集中在最左边,从左到右依次排列,如下图;
以上两图为例,i为根的数值;
左孩子是(2*i+1);
例:2的左孩子是:2*2+1=5;
右孩子是(2*i+2);
例:1的右孩子是:1*2+2=4;
所以把一颗二叉树可以写成一个数组;
2. 数组构建二叉树
建立一个长度为7的数组,找到其对应二叉树性质。
由数组构建一个二叉树的方法:找到最后一个孩子,总后往前依次推出来(最后一个有孩子的节点 (i / 2-1)=(7/2-1)=2;所以2位置的数为最后一个有孩子的节点),如下图;
所以需要两步骤:1. 调整 2. 排序
2.1 调整
首先判断,需要调整的根节点有几个孩子;
2.1.1有两个孩子
2.1.2有一个孩子
2.1.3没有孩子
调整函数的详细代码:
void Adjust(int arr[], int nLen, int index) { while (1) { int i = index; //1. 两个孩子---左、右孩子 if (2 * index + 2 < nLen) { //判断是否调整,左、右孩子 > 根的值,进行调整 if (arr[index] < arr[2 * index + 1] || arr[index] < arr[2 * index + 2]) { //找到左、右孩子的最大值max int max = arr[2 * index + 1] > arr[2 * index + 2] ? 2 * index + 1 : 2 * index + 2; //交换 int t = arr[index]; arr[index] = arr[max]; arr[max] = t; } else { break; } } //2. 一个孩子---左孩子 else if (2 * index + 1 < nLen) { if (arr[index] < 2 * index + 1) { int t = arr[2 * index + 1]; arr[2 * index + 1] = arr[index]; arr[index] = t; } else { break; } } //3. 没有孩子 else { break; } } }
2.2 排序
获取调整后的数值,调整后的第一个数值一定是最小的,0位置的数和nLen-1位置的数交换,在进行调整,不断循环操作,直至排序完成。
void HeapSort(int arr[], int nLen) { if (arr == nullptr || nLen <= 0) return; //构建大根堆 //调整 for (int i = nLen/2-1; i >=0; i--)//从最后一个有孩子的节点开始 { Adjust(arr, nLen, i);// } //排序 for (int i = nLen - 1; i > 0; i--) { int nTemp = arr[0]; arr[0] = arr[i]; arr[i] = nTemp; Adjust(arr, i, 0); } }
3. 验证例子
int arr[] = { 2,6,3,654,34,57,35 }; HeapSort(arr, 7); for (int n : arr) { cout << n << " "; } cout << endl;