完全二叉树的性质:如果i=1,则节点i是二叉树的根,无双亲;如果i>1,则其双亲节点是i/2向下取整,
堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值,称为大顶堆;或者每个节点的值都小于或等于其左右孩子节点的值,称为小顶堆。
按照层序遍历的方式给节点编号则满足ki>=k2i且ki>=k2i+1或者ki<=k2i且ki<=k2i+1。
堆排序就是利用堆(假设利用大顶堆)进行排序的算法,将待排序序列构造成一个大顶堆。此时整个序列的最大值就是堆顶的根节点,将它移走,然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次小值。如此反复执行便能得到一个有序的序列。
如何构造一个堆?
public void buildBigHeap(int []num) {
int mid=num.length/2;
for(int i=mid;i>0;i--) {
heapAdjust(num,i,num.length);
}
}
public void heapAdjust(int []num,int i,int len) {
int temp=num[i-1];
for(int j=2*i;j<=len;j*=2) {//遍历其子树,找到最大的节点放在根节点处
if(j<len&&num[j-1]<num[j]) {
j++;
}
if(temp>=num[j-1])
break;
num[i-1]=num[j-1];//将较大的节点放在根节点处
i=j;
}
num[i-1]=temp;//将原来存放较大节点的位置存放较小的根节点
}
下标之所以从mid开始,是因为编号mid~1之间对应的元素都存在孩子节点。
我们所谓的将待排序序列构建一个大顶堆其实就是从下往上,从右往左将每个非终端节点看做根节点,将其和其子树调整成大顶堆的过程。
堆排序完整实现
public void buildBigHeap(int []num) {
int mid=num.length/2;
for(int i=mid;i>0;i--)
heapAdjust(num,i,num.length);
for(int i=num.length;i>1;i--) {
int temp=num[i-1]; //将堆顶记录和当前未排序的子序列的最后一个记录交换
num[i-1]=num[0];
num[0]=temp;
heapAdjust(num,1,i-1);//重新调整为大顶堆
}
}
public void heapAdjust(int []num,int i,int len) {
int temp=num[i-1];//存储根节点的信息
for(int j=2*i;j<=len;j*=2) {
if(j<len&&num[j-1]<num[j]) {
j++;//j存储左右孩子节点中最大的那个
}
if(temp>=num[j-1])//如果根节点数值大于孩子节点则结束遍历
break;
num[i-1]=num[j-1];//找到当前最大的数值赋给根节点
i=j;//i记录最大数值原始的存储位置
}
num[i-1]=temp;
}
###题目描述
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
public int findKthLargest(int[] nums, int k) {
int mid=nums.length/2;
for(int i=mid;i>0;i--)
heapAdjust(nums,i,nums.length);
//System.out.println(nums[0]);
if(k==1)
return nums[0];
int count=k;
for(int i=nums.length;i>1;i--) {
if(count==1)
break;
int temp=nums[i-1];
nums[i-1]=nums[0];
nums[0]=temp;
heapAdjust(nums,1,i-1);
count--;
}
return nums[0];
}
public void heapAdjust(int []num,int i,int len) {
int temp=num[i-1];//存储根节点的信息
for(int j=2*i;j<=len;j*=2) {
if(j<len&&num[j-1]<num[j]) {
j++;//j存储左右孩子节点中最大的那个
}
if(temp>=num[j-1])//如果根节点数值大于孩子节点则结束遍历
break;
num[i-1]=num[j-1];//找到当前最大的数值赋给根节点
i=j;//i记录最大数值原始的存储位置
}
num[i-1]=temp;
}