Java-堆排序

1.概念

    是一种树形选择排序,在排序过程中,将待排序的记录r[1..n]看成是一颗完全二叉树的顺序存储结构,
    利用完全二叉树中的双节点和孩子节点之间的内在关系,在当前无序的序列中选择关键字最大或最小的记录。

2.思路

    实现堆排序需解决两个问题:
    (1)如何将n 个待排序的数建成堆;
    (2)输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆。

    建堆方法(小根堆):
    对初始序列建堆的过程,就是一个反复进行筛选的过程。
    n 个结点的完全二叉树,则最后一个结点是第n/2个结点的子树。
    筛选从第n/2个结点为根的子树开始(n/2是最后一个有子树的结点),使该子树成为堆。
    之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。

3.代码实现

/**
 * 调整为小根堆(排序后结果为从大到小)
 * @param arr 待调整的堆数组
 * @param index   是待调整的数组元素的位置
 * @param length 数组的长度
 */
public void minHeap(int[] arr, int length,int index){

    int temp = arr[index];
    //左孩子节点的位置
    int child = 2 * index + 1;
    System.out.println("待调整父节点:arr["+index+"] ="+temp);
    while (child < length){
        //右孩子节点的位置 child+1
        // 如果有右孩子
        // 如果右孩子小于左孩子,使用右孩子与父节点进行比较,否则使用左孩子与父节点进行比较
        if(child + 1 < length && arr[child + 1] < arr[child]){
            child = child + 1;
        }

        System.out.println("将与子孩子 array[" + child + "] = " + arr[child] + " 进行比较");

        // 如果较小的子孩子比此结点小
        if(arr[child] < arr[index]){

            System.out.println("子孩子比父节点小,交换位置");
            //交换数据 保证父节点是小于子孩子节点
            int flag = arr[child];
            arr[child] = arr[index];
            arr[index] = flag;
            // 待调整父结点移动到较小孩子原来的位置
            index = child;

            // 继续判断待调整父结点是否需要继续调整
            child = 2 * index + 1;

            if(child >= length){
                System.out.println("没有子孩子了,调整结束");
                break;
            }else{
                System.out.println("继续与新的子孩子进行比较");
            }
        }else{
            System.out.println("子孩子都比父节点大,调整结束");
            break;
        }
    }
}

/**
 * 调整为大根堆(排序后结果为从小到大)
 * @param arr 待调整的堆数组
 * @param index   是待调整的数组元素的位置
 * @param length 数组的长度
 */
public void maxHeap(int[] arr, int length,int index){
    int temp = arr[index];
    //左孩子节点的位置
    int child = 2 * index + 1;
    System.out.println("待调整父结点为:array[" + index + "] = " + temp);
    while (child < length){
        //右孩子节点的位置 child + 1
        // 如果有右孩子
        // 如果右孩子大于左孩子,使用右孩子与结点进行比较,否则使用左孩子
        if(child + 1 < length && arr[child + 1] > arr[child]){
           child = child + 1;
        }

        System.out.println("将与子孩子 array[" + child + "] = " + arr[child] + " 进行比较");

        // 如果较大的子孩子比此父节点大
        if(arr[child] > arr[index]){

            System.out.println("子孩子比父节点大,交换位置");
            //交换数据 保证父节点是大于子孩子节点的
            int flag = arr[child];
            arr[child] = arr[index];
            arr[index] = flag;
            // 待调整父结点移动到较大孩子原来的位置
            index = child;
            // 继续判断待调整父结点是否需要继续调整
            child = 2 * index + 1;

            if(child >= length){
                System.out.println("没有子孩子了,调整结束");
                break;
            }else{
               System.out.println("继续与新的子孩子进行比较");
            }

        }else{
            System.out.println("子孩子都比父节点小,调整结束");
            break;
        }
    }
}

/**
 * 堆排序算法
 * @param arr
 * @param inverse true 为倒序排列,false 为正序排列
 */
public int[] heapSort(int[] arr,boolean inverse){
    // 初始堆
    // 最后一个子孩子的结点位置 i = (length - 1) / 2, 以此向上调整各结点使其符合堆
    System.out.println("初始化堆开始");
    for (int i = (arr.length - 1) / 2; i>=0; i--) {
        if(inverse){
           minHeap(arr,arr.length,i);
        }else{
           maxHeap(arr,arr.length,i);
        }
    }
    System.out.println("初始化堆结束");

    for (int i = arr.length - 1; i > 0 ; i--) {
        // 交换堆顶元素arr[0]和堆中最后一个元素
        int temp = arr[i];
        arr[i] = arr[0];
        arr[0] = temp;
        //每次交换堆顶元素和堆中最后一个元素之后,都要对堆进行调整
        if(inverse){
           minHeap(arr,i,0);
        }else{
           maxHeap(arr,i,0);
        }
    }
    return arr;
}

4.测试

@Test
public void test(){
    //int arr[] = {0,1,1,2,3,3,4,8,7,6,12,22,65};
    int arr[] = {6,3,8,2,9,1};
    //int arr[] = { 49, 38, 65, 97, 76, 13, 27, 49,1,222,3,112,2,333,44,4,5,32,1234,5 };
    System.out.println("排序前");
    printArr(arr);
    int[] data = heapSort(arr,false);
    System.out.println("排好序后");
    printArr(data);
}

public void printArr(int[] arr){
    for (int i = 0; i < arr.length; i++) {
        System.out.print(arr[i]+" ");
    }
    System.out.println();
}

5.总结

    特点:
    (1)是不稳定的排序
    (2)只能用于顺序结构,不能用于链式结构
    (3)初始建堆所需要的比较次数较多,因此记录数较少时不宜采用。
    堆排序在最坏情况下的时间复杂度是O(nlog2n),相对于快速最坏情况下的O(n*n)而言是一个优点,当记录较多时较为高效。

猜你喜欢

转载自blog.csdn.net/rjgcszlc/article/details/79779484