排序---堆排序

堆排序

堆的概念

  堆中某个节点的值总是大于等于其子节点的值,并且堆是一颗完全二叉树。

  堆可以用数组来表示,这是因为堆是完全二叉树,而完全二叉树很容易存储在数组中,位置看k的节点的父节点位置为(k-1)/2,而它的两个子节点的位置分别为2k+1和2k+2。

上浮和下沉

  在堆中,当一个节点比父节点大,那么需要交换这两个节点。交换后可能比它新的父节点还大,因此需要不断的进行比较和交换操作,把这种操作称为上浮。

  类似的,当一个节点比子节点小,也需要不断的向下进行比较和交换,把这种操作称为下沉。一个节点如果有两个子节点,应当与两个子节点中最大的那个节点进行交换。

堆排序

  把最大元素和当前堆中数组的最后一个元素交换位置,并且不删除它,那么就可以得到一个从头到尾的递增序列,这就是堆排序。

1.构建大顶堆

  无序数组建立堆最直接的方法就是从左到右遍历数组进行上浮操作。

2.交换堆顶元素和最后一个元素

  交换之后需要对堆顶元素进行下沉操作,重新保持大顶堆状态。

代码实现:

public class Sort{
    public static void heapSort(int[]arr){
        if(arr==null||arr.length==0)
            return;
        for(int i=0;i<arr.length;i++){
            heapInsert(arr,i);  //插入元素构成大顶堆
        }
        int size=arr.length; //堆的大小
        swap(arr,0,--size);//将堆顶元素和最后一个元素交换,堆的大小减1
        while(size>0){//调整当前堆,使其变成大顶堆,下沉过程
            heapify(arr,0,size);
            swap(arr,0,--size);//将堆顶元素和堆里最后一个元素交换,然后堆的大小减1。
        }
        
    }
    public static void heapInsert(int[]arr,int i){
        while(arr[i]>arr[(i-1)/2]){//当前节点大于其父节点,上浮
            swap(arr,i,(i-1)/2);
            i=(i-1)/2;   
        }
    }
    public static void heapify(int []arr,int index,int size){
        int left=2*index+1; //左孩子坐标
        while(left<size){
            int largest=left+1<size&&arr[left]>arr[left+1]?left:left+1;
            largest=arr[largest]>arr[index]?largest:index;
            if(largest==index){
                break;//如果三个数中最大值还是父节点,那么就退出,不用下沉
            }
            swap(arr,largest,index);
            index=largest; //和前面相同的操作,迭代往下沉,直到越界
            left=index*2+1;
        }
    }
    public static void swap(int[]arr,int index,int largest){
        arr[index]=arr[index]^arr[largest];
        arr[largest]=arr[index]^arr[largest];
        arr[index]=arr[index]^arr[largest];
    }
}

  一个堆的高度为logN,因此在堆中插入元素和删除最大元素的复杂度都为logN。

  对于堆排序,由于要对N个节点进行下沉操作,所以复杂度为O(NlogN)。

  堆排序是原地排序,没有利用额外的空间。

原文地址:https://www.cnblogs.com/yjxyy/p/11103731.html