网站首页 > java教程 正文
简介
堆是一种数据结构,一种叫做完全二叉树的数据结构。
堆又分为大顶堆、小顶堆。
大顶堆:每个节点的值都大于或者等于它的左右子节点的值。
小顶堆:每个节点的值都小于或者等于它的左右子节点的值。
如果我们把这种逻辑结构映射到数组中,就是下边这样
9 | 5 | 8 | 2 | 3 | 4 | 7 | 1 |
1 | 3 | 5 | 4 | 2 | 8 | 9 | 7 |
从这里我们可以得出以下性质(重点)其中i表示树形结构的下标,顶级节点从0开始
对于大顶堆:arr[i] >= arr[2i + 1] && arr[i] >= arr[2i + 2]
对于小顶堆:arr[i] <= arr[2i + 1] && arr[i] <= arr[2i + 2]
分析
堆排序的基本思想是:
1、将带排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;
3、重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,就得到一个有序的序列了。
假设给定的无序序列arr是:
4 | 5 | 8 | 2 | 3 | 9 | 7 | 1 |
1、将无序序列构建成一个大顶堆。
首先我们将现在的无序序列看成一个堆结构,一个没有规则的二叉树,将序列里的值按照从上往下,从左到右依次填充到二叉树中。
根据大顶堆的性质,每个节点的值都大于或者等于它的左右子节点的值。所以我们需要找到所有包含子节点的节点,也就是非叶子节点,然后调整他们的父子关系,非叶子节点遍历的顺序应该是从下往上,这比从上往下的顺序遍历次数少很多,因为,大顶堆的性质要求父节点的值要大于或者等于子节点的值,如果从上往下遍历,当某个节点即是父节点又是子节点并且它的子节点仍然有子节点的时候,因为子节点还没有遍历到,所以子节点不符合大顶堆性质,当子节点调整后,必然会影响其父节点需要二次调整。但是从下往上的方式不需要考虑父节点,因为当前节点调整完之后,当前节点必然比它的所有子节点都大,所以,只会影响到子节点二次调整。相比之下,从下往上的遍历方式比从上往下的方式少了父节点的二次调整。
那么,该如何知道最后一个非叶子节点的位置,也就是索引值?
对于一个完全二叉树,在填满的情况下(非叶子节点都有两个子节点),每一层的元素个数是上一层的二倍,根节点数量是1,所以最后一层的节点数量,一定是之前所有层节点总数+1,所以,我们能找到最后一层的第一个节点的索引,即节点总数/2(根节点索引为0),这也就是第一个叶子节点,所以第一个非叶子节点的索引就是第一个叶子结点的索引-1。那么对于填不满的二叉树呢?这个计算方式仍然适用,当我们从上往下,从左往右填充二叉树的过程中,第一个叶子节点,一定是序列长度/2,所以第一个非叶子节点的索引就是arr.length / 2 -1。
1.现在找到了最后一个非叶子节点,即元素值为2的节点,比较它的左右节点的值,是否比他大,如果大就换位置。这里因为1<2,所以,不需要任何操作
2.继续比较下一个,即元素值为8的节点,它的左节点值为9比它本身大,所以需要交换
交换后的序列为:
4 | 5 | 9 | 2 | 3 | 8 | 7 | 1 |
3.因为元素8没有子节点,所以继续比较下一个非叶子节点,元素值为5的节点,它的两个子节点值都比本身小,不需要调整;
4.然后是元素值为4的节点,也就是根节点,因为9>4,所以需要调整位置
交换后的序列为:
9 | 5 | 4 | 2 | 3 | 8 | 7 | 1 |
5.此时,原来元素值为9的节点值变成4了,而且它本身有两个子节点,所以,这时需要再次调整该节点
交换后的序列为:
9 | 5 | 8 | 2 | 3 | 4 | 7 | 1 |
到此,大顶堆就构建完毕了。满足大顶堆的性质。
6、排序序列,将堆顶的元素值和尾部的元素交换
交换后的序列为:
1 | 5 | 8 | 2 | 3 | 4 | 7 | 9 |
然后将剩余的元素重新构建大顶堆,其实就是调整根节点以及其调整后影响的子节点,因为其他节点之前已经满足大顶堆性质。
交换后的序列为:
8 | 5 | 7 | 2 | 3 | 4 | 1 | 9 |
然后,继续交换,堆顶节点元素值为8与当前尾部节点元素值为1的进行交换
交换后的序列为:
1 | 5 | 7 | 2 | 3 | 4 | 8 | 9 |
重新构建大顶堆
交换后的序列为:
7 | 5 | 4 | 2 | 3 | 1 | 8 | 9 |
继续交换
交换后的序列为:
1 | 5 | 4 | 2 | 3 | 7 | 8 | 9 |
重新构建大顶堆
构建后的序列为:
5 | 3 | 4 | 2 | 1 | 7 | 8 | 9 |
继续交换
交换后的序列为:
1 | 3 | 4 | 2 | 5 | 7 | 8 | 9 |
重新构建大顶堆
构建后的序列为:
4 | 3 | 1 | 2 | 5 | 7 | 8 | 9 |
继续交换
交换后的序列为:
2 | 3 | 1 | 4 | 5 | 7 | 8 | 9 |
重新构建大顶堆
构建后的序列为:
3 | 2 | 1 | 4 | 5 | 7 | 8 | 9 |
继续交换
交换后的序列为:
1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
重新构建大顶堆
继续交换
交换后的序列为:
1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
此时,序列排序完成。以上就是整个堆排序的逻辑。
代码实现
package com.zyp.query;
import java.util.Arrays;
/**
* 堆排序
* @author zyp
* @create 2022/3/17
*/
public class HeapSortDemo {
public static void main(String[] args){
int[] array = new int[]{4,5,8,2,3,9,7,1};
heapSort(array,array.length-1);
System.out.println(Arrays.toString(array));
}
/**
*
* @param array 待排序数组
* @param length 需要排序的长度
*/
public static void heapSort(int[] array,int length){
//构建大顶堆
int nodeIndex = array.length/2 -1;
while(nodeIndex>=0){
buildBigTopHeap(array,nodeIndex,length);
nodeIndex--;
}
//交换位置并重新构建大顶堆
for(int i = length;i>0;i--){
//将第一个和最后一个互换位置
int temp = array[0];
array[0] = array[i];
array[i] = temp;
buildBigTopHeap(array,0,i);
}
}
/**
* 重新构建大顶堆
* @param array 待排序数组
* @param nodeIndex 父节点
* @param length 数组的长度
*/
public static void buildBigTopHeap(int[] array,int nodeIndex,int length){
int leftNodeIndex = nodeIndex * 2 + 1;
int rightNodeIndex = nodeIndex * 2 + 2;
int maxValueIndex = -1;
if(leftNodeIndex < length && rightNodeIndex < length ){
if(array[leftNodeIndex] < array[rightNodeIndex]){
maxValueIndex = rightNodeIndex;
}else{
maxValueIndex = leftNodeIndex;
}
}else if(leftNodeIndex < length){
maxValueIndex = leftNodeIndex;
}else if(rightNodeIndex < length){
maxValueIndex = rightNodeIndex;
}
if(maxValueIndex != -1){
if(array[nodeIndex] < array[maxValueIndex]){
int temp = array[nodeIndex];
array[nodeIndex] = array[maxValueIndex];
array[maxValueIndex] = temp;
}
//发生交换的节点,它的子节点可能需要重新排序
buildBigTopHeap(array,maxValueIndex,length);
}
}
}
猜你喜欢
- 2024-10-22 Java几种排序方式(java排序的方法有哪些)
- 2024-10-22 Java排序算法——归并排序(Merge Sort)
- 2024-10-22 Java 集合中的排序算法浅析(java集合排序工具类)
- 2024-10-22 数组排序与二分查找法(二分查找排序树)
- 2024-10-22 LeetCode基础算法题第85篇:求有序数组的平方再排序
- 2024-10-22 Java中Arrays的两种排序方法(sort和parallelSort)比较
- 2024-10-22 深入理解Java中Comparable和Comparator排序
- 2024-10-22 Java常见知识之冒泡排序#冒泡排序
- 2024-10-22 Java数组之Arrays方法(java array数组)
- 2024-10-22 常用集合的排序方法——Java进阶知识讲义系列(七)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)