堆排序(理解二叉树的节点遍历 )
- 关于二叉树 递归这一块 参考归并排序那一篇
堆的定义
- 堆是一种数据结构,一种叫做完全二叉树的数据结构
堆的性质
-
大顶堆:每个节点的值都大于或者等于它的左右子节点的值
-
小顶堆:每个节点的值都小于或者等于它的左右子节点的值
-
对于大顶堆:arr[i] >= arr[2i + 1] && arr[i] >= arr[2i + 2] 对于小顶堆:arr[i] <= arr[2i + 1] && arr[i] <= arr[2i + 2]
堆排序的步骤与图解
- 堆排序的基本思想是:
- 将带排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素
- 将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆
- 重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,就得到一个有序的序列了
1. 初次构建大顶堆
2. 进行大顶堆排序
3. 堆顶元素下沉
4. 剩余元素再次构建大顶堆
5. 重复3-4
6. 排序完毕
图解
-
如图 初始序列为{33,56,87,112,114,252,96,919}
-
将其化为二叉树表示 {33,56,87,112,114,252,96,919}
-
首先对 i=8/2-1=3 的节点 也就是从112这个节点进行初次的大顶堆构建
-
交换919–112 [33, 56, 87, 919, 114, 252, 96, 112]
-
对2号节点 即87 进行排序操作
-
交换252–87 [33, 56, 252, 919, 114, 87, 96, 112]
-
对1号节点 56 进行操作
-
交换919–56 [33, 919, 252, 56, 114, 87, 96, 112]
-
子节点发生变化 重复判定子节点
-
对当前3号节点 进行操作
-
交换114–56 [33, 919, 252, 112, 114, 87, 96, 56]
-
对0号节点 33 进行操作
-
交换919–33 [919, 33, 252, 112, 114, 87, 96, 56]
-
子节点发生变化 重复判定子节点
-
对当前1号节点 进行操作
-
交换114–33 [919, 114, 252, 112, 33, 87, 96, 56]
-
至此 初次大顶堆构建完毕
-
接着将交换根节点919与末节点56 进行堆节点下沉 [56, 114, 252, 112, 33, 87, 96, 919]
-
除去 当前末节点 对剩余元素再次进行大顶堆操作
-
交换根节点与末节点 进行堆节点下沉
-
重复19-20
-
堆排序结束 最终结果为:[33, 56, 87, 96, 112, 114, 252, 919]
代码及运行过程
import java.util.Arrays;
public class DuiPaiXu {
public static void main(String[] args) {
int[] a = {33,56,87,112,114,252,96,919};
GouJian(a);
System.out.println("最终结果为:"+Arrays.toString(a));
}
public static void GouJian(int[] a) {
int ll = a.length;
DaDingDui(a, ll);//大顶堆的初次构建
System.out.println(Arrays.toString(a));
for (int i = a.length - 1; i > 0; i--) {//确保除去最后一个元素外的所有元素都循环到
JiaoHuan(a, 0, i);//将堆顶元素与末节点元素进行交换
System.out.println("将堆顶元素与末节点元素进行交换" + Arrays.toString(a));
ll--;//末节点元素剔除
PaiXu(a, 0, ll);//除去末节点 剩余元素再次进行大顶堆的构建
System.out.println("对交换过后重新得到的二叉树进行大顶堆排序操作:" + Arrays.toString(a));
}
}
public static void DaDingDui(int[] a, int ll) {
for (int i = a.length / 2 - 1; i >= 0; i--) {
PaiXu(a, i, ll);//从最后一个非叶节点进行排序
System.out.println("第"+i+"号节点的排序交换结果"+ Arrays.toString(a));
}
}
public static void PaiXu(int[] a, int dq, int ll) {
//下沉
int left = 2 * dq + 1; //该节点的左节点
int right = 2 * dq + 2;//该节点的右节点
int max = dq;//记录最大值索引
if (left < ll && a[max] < a[left]) {
//如果有左节点 比较大小 大的话更新最大值索引
System.out.println("左节点大");
max = left;
}
if (right < ll && a[max] < a[right]) {
//如果有右节点 比较大小 大的话更新最大值索引
System.out.println("右节点更大");
max = right;
}
if (max != dq) {//如果最大值不是当前索引 (代表在上述的步骤更新过)
JiaoHuan(a, dq, max);//进行元素交换
System.out.println("最大值交换" + Arrays.toString(a));
PaiXu(a, max, ll);//对交换过后的子节点进行再次排序 以免混乱
System.out.println("节点交换后的排序:" + Arrays.toString(a));
}
}
public static void JiaoHuan(int[] a, int ding, int di) {
//交换元素
int z = a[di];
a[di] = a[ding];
a[ding] = z;
}
}
运行过程:
左节点大
最大值交换[33, 56, 87, 919, 114, 252, 96, 112]
节点交换后的排序:[33, 56, 87, 919, 114, 252, 96, 112]
第3号节点的排序交换结果[33, 56, 87, 919, 114, 252, 96, 112]
左节点大
最大值交换[33, 56, 252, 919, 114, 87, 96, 112]
节点交换后的排序:[33, 56, 252, 919, 114, 87, 96, 112]
第2号节点的排序交换结果[33, 56, 252, 919, 114, 87, 96, 112]
左节点大
最大值交换[33, 919, 252, 56, 114, 87, 96, 112]
左节点大
最大值交换[33, 919, 252, 112, 114, 87, 96, 56]
节点交换后的排序:[33, 919, 252, 112, 114, 87, 96, 56]
节点交换后的排序:[33, 919, 252, 112, 114, 87, 96, 56]
第1号节点的排序交换结果[33, 919, 252, 112, 114, 87, 96, 56]
左节点大
最大值交换[919, 33, 252, 112, 114, 87, 96, 56]
左节点大
右节点更大
最大值交换[919, 114, 252, 112, 33, 87, 96, 56]
节点交换后的排序:[919, 114, 252, 112, 33, 87, 96, 56]
节点交换后的排序:[919, 114, 252, 112, 33, 87, 96, 56]
第0号节点的排序交换结果[919, 114, 252, 112, 33, 87, 96, 56]
[919, 114, 252, 112, 33, 87, 96, 56]
将堆顶元素与末节点元素进行交换[56, 114, 252, 112, 33, 87, 96, 919]
左节点大
右节点更大
最大值交换[252, 114, 56, 112, 33, 87, 96, 919]
左节点大
右节点更大
最大值交换[252, 114, 96, 112, 33, 87, 56, 919]
节点交换后的排序:[252, 114, 96, 112, 33, 87, 56, 919]
节点交换后的排序:[252, 114, 96, 112, 33, 87, 56, 919]
对交换过后重新得到的二叉树进行大顶堆排序操作:[252, 114, 96, 112, 33, 87, 56, 919]
将堆顶元素与末节点元素进行交换[56, 114, 96, 112, 33, 87, 252, 919]
左节点大
最大值交换[114, 56, 96, 112, 33, 87, 252, 919]
左节点大
最大值交换[114, 112, 96, 56, 33, 87, 252, 919]
节点交换后的排序:[114, 112, 96, 56, 33, 87, 252, 919]
节点交换后的排序:[114, 112, 96, 56, 33, 87, 252, 919]
对交换过后重新得到的二叉树进行大顶堆排序操作:[114, 112, 96, 56, 33, 87, 252, 919]
将堆顶元素与末节点元素进行交换[87, 112, 96, 56, 33, 114, 252, 919]
左节点大
最大值交换[112, 87, 96, 56, 33, 114, 252, 919]
节点交换后的排序:[112, 87, 96, 56, 33, 114, 252, 919]
对交换过后重新得到的二叉树进行大顶堆排序操作:[112, 87, 96, 56, 33, 114, 252, 919]
将堆顶元素与末节点元素进行交换[33, 87, 96, 56, 112, 114, 252, 919]
左节点大
右节点更大
最大值交换[96, 87, 33, 56, 112, 114, 252, 919]
节点交换后的排序:[96, 87, 33, 56, 112, 114, 252, 919]
对交换过后重新得到的二叉树进行大顶堆排序操作:[96, 87, 33, 56, 112, 114, 252, 919]
将堆顶元素与末节点元素进行交换[56, 87, 33, 96, 112, 114, 252, 919]
左节点大
最大值交换[87, 56, 33, 96, 112, 114, 252, 919]
节点交换后的排序:[87, 56, 33, 96, 112, 114, 252, 919]
对交换过后重新得到的二叉树进行大顶堆排序操作:[87, 56, 33, 96, 112, 114, 252, 919]
将堆顶元素与末节点元素进行交换[33, 56, 87, 96, 112, 114, 252, 919]
左节点大
最大值交换[56, 33, 87, 96, 112, 114, 252, 919]
节点交换后的排序:[56, 33, 87, 96, 112, 114, 252, 919]
对交换过后重新得到的二叉树进行大顶堆排序操作:[56, 33, 87, 96, 112, 114, 252, 919]
将堆顶元素与末节点元素进行交换[33, 56, 87, 96, 112, 114, 252, 919]
对交换过后重新得到的二叉树进行大顶堆排序操作:[33, 56, 87, 96, 112, 114, 252, 919]
最终结果为:[33, 56, 87, 96, 112, 114, 252, 919]
进程已结束,退出代码0