前言:
当前你们对“java排序后获取原位置”大概比较关心,咱们都需要学习一些“java排序后获取原位置”的相关文章。那么小编也在网络上网罗了一些有关“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); } }}
标签: #java排序后获取原位置