堆排序
完全二叉树:要求数据必须从上到下,从左到右依次进行平铺满。特殊的二叉树。
1.堆:大顶堆 小顶堆
在完全二叉树基础之上,每个节点的值都大于或等于其左右孩子的值。
在完全二叉树基础之上,每个节点的值都小于或等于其左右孩子的值。
堆排序过程
1构建大顶堆
2维护大顶堆
3
想要3的子节点,
详细讲述:
定义两个标:parent Child
首先入parent一个一个去找,
父节点和孩子当中最大值进行对比,大的值存在父节点当中,小的值存在子节点当中。
父节点移动6、1、3、0都没有子节点,直到指向2有了Child,看局部是一个大顶堆;
然后指到4,找子节点的最大值对比,3<4所以不用换;
接着指向7,显然也是7大;
最后指向5,7>5所以互换。
发现不是大顶堆,那怎么办呢?
首先让parent减一步,指向Child:5这,然后Child退到6,5和6进行对比互换。parent再退再退退到5,2和5对比互换。
现在构成大顶堆了。
parent指针从后向前移动,目的是保证后边的数据 先后构建大顶堆。
实践当中用数组完成。而不存在树 树跟数组映射关系
p指向最下面的数据6,
然后让它不断减减 减到2这里,靠它的子节点映射过来。当前的树并不存在。
那么怎么找到 一个数 的子树?
- 一个节点的左子树:2*p+1
- 一个节点的右子树:2*p+2
- 一个节点的父节点:(p-1)/2
直接利用公式就能找的父节点和子节点。
2的左子树:2的下标3,2*3+1=7 即6。
假如指到4,4的左右子树为,4的下标为2,它的左子树:2*2+1=5,右子树2*2+2=6 。
所以有以上结论就可以不需要树就可以找到任意一个数的父节点和子节点。
落实到数组:
定义一个指针p从后往前移动,移动到2,C就会指到6
做一个判断
if(Child < arr.length){
//父子节点的值进行对比
}
对比完成2和6互换。还不能直接对比,再找到右节点
while(Child < arr.length){
指向左右孩子当中最大值
int rChild = Child + 1;
if(rChild < length && arr[Child] < arr[rChild]){
Child++;
}
}
int temp = arr[parent];
int Child = 2*parent+1;
while(arr[Child]
//指向左右孩子当中最大值
int rChild = Child+1;
//父子节点的值进行对比,如果子节点的值大于父节点就要交换。交换完之后,就要回退,
if(temp >= arr[Child]){
break;
}
arr[parent] = arr[Child];
parent = Child;
Child = 2*Child +1;
}
int temp = arr[parent];
int Child = 2*parent+1;
while(Child < arr.length){
指向左右孩子当中最大值
int rChild = Child + 1;
if(rChild < length && arr[Child] < arr[rChild]){
Child++;
}
if(temp >= arr[Child]){
break;
}
arr[parent] = arr[Child];
parent = Child;
Child = 2*Child +1;
}
while循环 以上逻辑一直进行回退。
走一遍:
假设6和2已结排好位置,
定义一个临时空间temp,此时temp=2,Child:2 * 7 + 1 =15,Child < arr.length吗?
不小于 15>8。所以继续减减,直到满足条件,3*2+1=7,满足while循环开始。
rChild = Child+1 即 8 不满足条件,往下走,如果temp >= arr[Child],即 此时temp=6,6>2,满足,就走if循环,走break,跳出循环。继续减减
找左右孩子最大值为3,temp=4,对比 满足条件break,就又减减
temp=7,7>6,,满足。
此时temp = 5,5不大于7,不走if循环。
arr[Child] 赋到 arr[parent],
Child = 2*Child + 1, = 3
走完之后,判断while循环 Child < arr.length,满足
之后再进行 此时 temp=5,
6会到p这里,之后
接着判断。到temp=5 >2,break停止。一旦停止while循环,需要再写一句
arr[parent] = temp;
最后构建成大顶堆。
通过一个for循环
for(int p = arr.length-1;p>0;p--){
sort(arr,p,arr.length);
}
说一下堆栈内存:
运行的时候有一个栈内存一个堆内存.
首先main方法入栈,会生成一个数组arr,之后进行一个for循环p=7开始,之后不断调用sort这个方法,有一些局部变量分别叫arr,由于我们当前用值传递 把地址传递过去,两个arr指向同一块内存区域,parent就是刚定义的游标,也就是现在这个游标是7指向6,画出来,length长度8,temp定义出来等于6,Child定义出来等于2*7+1=15,不对,无法执行while循环,
sort执行完后出栈,出栈后for循环继续,p=7变成p=6,继续sort入栈,temp变为1,Child变为13,依旧不满足 继续,直到p=3,然后temp=1,Child=7,Child指针指向6了,temp现在变成2,继续,Child=6小于length,执行while循环,定义一个变成rChild = 7+1=8>=length,不满足条件,走下面if,temp>=Child,2<6不满足,往下走,2的位置变为6,
p变为2,pr到4位置,temp为4,Child为5, 直到p=1,temp=7,Child=3,
sort出栈,p=0,temp=5,Child=1,
while继续,继续7把5覆盖,......直到构建大顶堆。
维护
在有效排序过程中数组-1,原本是8现在变成7,所以7不在
length变为7,temp变为2,Child为1,
然后走代码,
之之后2和5对比
Child继续往下走 Child指向空,没有下标为7的数据了
while循环不再进行
重新构建完大顶堆 堆顶元素和堆底元素互换。
又是一轮回退......
以上就是整个堆排序。
总体代码:
import java.util.Arrays;
public class AdjestSort {
public static void main(String[] args){
int[] arr = {16,5,1,0,7,9,4,2};
//构建完大顶堆
for(int p = arr.length-1;p>=0;p--) {
sort(arr, p,arr.length);
}
for(int i = arr.length-1;i>0;i--) {
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
sort(arr, 0,i);
}
System.out.println(Arrays.toString(arr));
}
private static void sort(int[] arr, int parent, int length) {
int temp = arr[parent];
int Child = 2 * parent + 1;
while (Child < length) {
//指向孩子最大值
int rChild = Child+1;
if(rChild < length && arr[Child] < arr[rChild]) {
Child++;
}
if(temp >= arr[Child]) {
break;
}
arr[parent] = arr[Child];
parent = Child;
Child = 2 * Child+1;
}
arr[parent] = temp;
}
}