大家可以看一下我上一篇文章使用的堆排序
堆排序
我之前使用的堆排序是要开 一个长度为 N的数组,事实上,我们可以把原数组看成一个堆,也就是说我们不用开一个数组,而是在原地排序
怎么说呢…
还是给我一个数组,我对这个数组进行Heapify 操作,这样就得到了一个大根堆(脑袋是最大的) ,然后我们把 最大的那个数 和数组的最后一个元素交换,这样一来,数组末尾最大那个数就不用考虑了
至此,堆排序的一个子过程已经完成,是不是觉得很迷
比如说冒泡排序吧
每一轮排序,我都会把最大的那个元素堆在数组的末尾,也就是说下一次排序我就不看数组末尾了,
因为已经排好序了
好,我们再回来看这个堆排序,也就是说我在Heapify后,把数组头和数组尾进行交换,那么这个大根堆的结构就被破坏了,但是,只是0号元素的结构被破坏了而已,该元素的左右子树还是大根堆
这个时候我们再对0 号元素进行shiftDown 操作,这样一来,0号小的下去
下面大的上来,就又构建了一个大根堆【 注意:上一次Heapify操作完了以后,我把最大的元素放在了 第 (arr.length -1)为,也就是说,在下一轮shiftDown后,我把第0位和 第(arr.length-2)位,也就是倒数第二位进行交换,这样,我们就把最大的数和次大的数放在了倒数第一位和倒数第二位】
也就是说:倒数两位的数都被我排序好了,然后我们继续shiftDown ,再交换0,和 (len-3)位
继续shiftDown ,交换 0和(len-4)位
…
继续shiftDown,交换0 和2位元素,这样,整一个堆排序也就完成了
继续shiftDown ,然后交换0 和1 为元素,这样这个数组也就有序了
画图不易,望理解
每一次shiftDown 都在调整和维护一个大根堆,并且这个堆的size是上一次堆的(size-1),每一次swap就相当于一次 poll,也就是优先队列的出队操作
下面是代码演示:
public class Main {
static void swap(int [] arr,int l,int r)
{
int t = arr[l];
arr[l]=arr[r];
arr[r]=t;
}
public static void main(String[] args) {
int[] arr={
1,3,7,5,4,-1,999,58,5,8,2
};
HeapSort(arr);
for (int a:
arr) {
System.out.println(a);
}
}
public static void HeapSort(int[]arr) {
int N = arr.length;
for(int i=(N-1)/2;i>=0;i--)
{
shiftDown(arr,N,i);
}
for(int i=arr.length-1;i>0;i--)
{
swap(arr,0,i);
shiftDown(arr,i,0);
}
}
static void shiftDown(int[] arr,int heapSize,int i)
{
while (leftChild(i)<heapSize)
{
int j=leftChild(i);
if(rightChild(i)<heapSize&&arr[rightChild(i)]>arr[j])
{
j+=1;//j=rightChild(i),选择左孩子还是选择右孩子,看哪个更强
}
if(arr[i]>=arr[j])
{
break;
}
swap(arr,i,j);
i=j;
}
}
static int parent(int i)
{
return (i-1)/2;
}
static int leftChild(int i)
{
return 2*i+1;
}
static int rightChild(int i)
{
return 2*i+2;
}
static int lastNodeForHeapify(int N)
{
return (N-1)/2;
}
}
/**
* 堆排序工具类
*/
private static class HeapSortHelper{
private static void swap(int[] p,int l,int r)
{
if(p[l]==p[r]) return;
p[l]^=p[r];
p[r]^=p[l];
p[l]^=p[r];
}
private static void heapify(int[] p,int cur,int heapSize)
{
int next = (cur<<1)+1;
while (next<heapSize)
{
//默认选中左孩子
if((next+1)<heapSize&&p[next+1]>p[next])
{
++next;//选择右孩子
}
if(p[cur]>=p[next])
{
break;
}
swap(p,cur,next);
cur = next;
next = 1+(next<<1);
}
}
private static void build(int[] p,int rightEnd)
{
if(p.length<=1) return;
for(int i=((rightEnd>>1)-1);i>=0;--i)
{
heapify(p,i,rightEnd);
}
}
private static void heapSort(int[] p)
{
if(p==null||p.length==0)return;
build(p,p.length);
for(int i=(p.length-1);i>=0;--i)
{
swap(p,0,i);
heapify(p,0,i);
}
}
}