堆排序
准备知识:
完全二叉树:它在二叉树的基础上多了“完全”这个限制条件,通俗的讲,就是在给一颗二叉树添加结点的时候,添加的次序只能从上到下,从左到右。例如:
完全二叉树的特性:
- 下标为index结点的父结点的下标为(index-1)/2
- 下标为index结点的左孩子的下标为index*2+1
- 下标为index结点的右孩子的下标为index*2+2
大根堆:
完全二叉树中,根结点的关键字大于左右两个孩子的关键字,并且它的所有子树都要满足这个条件。
思路:
先遍历整个数组,将它变成整个大根堆(不需要用到树结构,只需要用到思想即可),即beBigHeap操作(高级一点叫heapInsert)这个过程完成后,无序数组的最大值就在数组的第一个位置;
然后将它与数组的最后一个位置互换,无序部分的长度减1,这个时候无序部分不满足大根堆了,此时需要在将它进行move操作(高级一点叫heapify),变换后又变成了一个大根堆,重复本步操作;
举个例子:
9,2,1,8,4,6
beBigHeap(即heapInsert):
初始:
index=0:
index=1:
index=2:
index=3:
index=4:
index=5:
move(即heapify):
将数组首尾位置的值互换,得:
此时仅仅变换跟结点即可,其子树都是大根堆
通过将根结点(下标index=0)与1其左右孩子中的较大者进行交换,得:
此时整体还不是大根堆,继续交换,此时index=1(下标),通过将结点(下标index=1)与其左右孩子中的较大者进行交换,得:
此时整体是一个大根堆,重复进行move(heapify)操作,直到无序部分的长度为0;
代码:
``
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
String s = sc.next().toString();
String[] arr = s.split(",");
int[] b=new int[arr.length];
System.out.println("排序前:");
for (int i =0;i<b.length;i++) {
b[i]=Integer.parseInt(arr[i]);
System.out.print(b[i]+",");
}
myHeap(b);
System.out.println("排序后:");
for (int i =0;i<b.length;i++) {
System.out.print(b[i]+",");
}
}
public static void myHeap(int b[]){
// c为有序无序部分的分界线
int c=b.length-1;
// 复杂度为O(n)
for (int i=0;i<b.length;i++){
beBigHeap(b,i);
}
while (c>0){ // 复杂度为O(n)
swap(b,0,c);
c=c-1;
move(b,c); //复杂度为O(log(n))
}
}
private static void beBigHeap(int[] b, int index) {
// 通过不断与父结点进行比较交换,变为大根堆
while (b[index]>b[(index-1)/2]){
swap(b,index,(index-1)/2);
index=(index-1)/2;
}
}
private static void swap(int[] b,int i,int j){
int t=b[i];
b[i]=b[j];
b[j]=t;
}
private static void move(int[] b,int last){
int index=0;
int left=index*2+1;
int right=index*2+2;
int flag=left;
// last为无序部分的最后一个
while (left<=last){
// 右孩子存在,并且右孩子大于左孩子
if(right<=last&&b[left]<b[right]){
flag=right;
}
// 左孩子大于右孩子
else {
flag=left;
}
// 如果当前的index的关键字已经比左右孩子中的较大者的关键字大,说明其子树已经是个大根堆,不需要再往下进行
if(b[index]>=b[flag]){
break;
}
swap(b,index,flag);
// 继续将子树也变成大根堆
index=flag;
left=index*2+1;
right=index+2+2;
}
}
}
小结:
,flag);
// 继续将子树也变成大根堆
index=flag;
left=index*2+1;
right=index+2+2;
}
}
}
### 小结:
堆排序的复杂度为O(n*log(n)),重新复习数据结构与算法,堆排序以前课上讲过,不过不是重点,所以也没太关注,现在大三巩固,这个算法确实妙啊,我是先了解堆排序的思想后再自己写代码,其中在heapify中试错了几次,学spring去了,现在回到算法感觉好生疏,还是要坚持学习算法,加油共勉!