Java堆排序(大顶堆小顶堆及应用实例)

自己理解所得,如有错误欢迎随时指摘;

目录:

  1. 堆概念
  2. 堆结构
  3. 堆排序步骤
  4. 大顶堆代码、小顶堆代码
  5. 实际应用及实例代码
  6. 小顶堆删除图解代码、插入代码
  7. 小顶堆插入图解
  8. 时间复杂度分析

1、百度-》概念:堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大顶堆和小顶堆,是完全二叉树。(任何一个子节点都小于父节点,左右无必须顺序。就是左边不一定比右边小)。

      1、满二叉树:除了叶子结点之外的每一个结点都有两个孩子,每一层(当然包含最后一层)都被完全填充

      2、完全二叉树: 除了最后一层之外的其他每一层都被完全填充,并且所有结点都保持向左对齐

      3、完满二叉树:除了叶子结点之外的每一个结点都有两个孩子结点。

什么是大顶堆:根结点(亦称为堆顶),大顶要求根节点既大于或等于左子树,又大于或等于右子树。跟普通二叉树的区别就是,每个父节点的值都大于子节点的值。

2、结构 :假设某有父子结构的节点为i,那么它的左子节点为2*i+1,右子节点为2*i+2,它的父节点为( i - 1)/ 2

比如说如果是25对应的位置是1这个节点。那么它的父亲就是(1-1)/2=0 0节点对应的是16.而25它的左子节点就是2*1+1=3 -》32右子孩子为2*1+2=4-》6.

                                                 0                     1                  2              3                  4               5

162573269

3、步骤:

1、初始化一个堆型数据结构,调整堆结构,调整为大顶堆(从最后一个非叶子节点进行调整7比9小,交换7与9 -》32比25小,交换32和25-》16与32比较小,则32与16交换位置,16和25比较小,交换16和25的位置,形成了初始堆)。

 

2、将首未进行交换(将32与7进行交换)

 

3、将交换完的堆(7在最顶端不符合大顶堆),继续调整为大顶堆。并将首与倒第二个进行交换,然后以此类推直到根节点(建堆、交换、建堆。。。。直到要与根节点进行交换)

 

4、代码

4.1大顶堆排序

public class HeapSort1 {

    public static void main(String[] args) {
        int[] a = new int[]{16, 25, 7, 32, 6, 9};
        heapSort(a);
        System.out.println(Arrays.toString(a));
    }

    /**
     * 构造大顶堆
     * @param arr 待调整数组
     * @param size 调整多少
     * @param index 调整哪一个 最后一个叶子节点的父节点开始调整
     */
    public static void maxHeap(int arr[], int size, int index) {

        //左子节点
        int leftNode = 2 * index + 1;
        //右子节点
        int rightNode = 2 * index + 2;

        int max = index;//假设自己最大

        //分别比较左右叶子节点找出最大
        if(leftNode < size && arr[leftNode] > arr[max]) {//如果左侧叶子节点大于max则将最大位置换成leftNode并且递归需要限定范围为数组长度,
            max = leftNode;//将最大位置改为左子节点
        }

        if(rightNode < size && arr[rightNode] > arr[max]) {//如果左侧叶子节点大于max则将最大位置换成rightNode
            max = rightNode;//将最大位置改为右子节点
        }

        //如果不相等就需要交换
        if(max != index) {
            int tem = arr[index];
            arr[index] = arr[max];
            arr[max] = tem;
            //如果下边还有叶子节点并且破坏了原有的堆。需要重新调整
            maxHeap(arr, size, max);//位置为刚才改动的位置;
        }
    }

    /**
     * 需要将最大的顶部与最后一个交换
     * @param arr
     */
    public static void heapSort(int arr[]) {
        int start = (arr.length - 1)/2;//开始位置最后一个非叶子节点,最后一个叶子节点的父节点
        for(int i = start; i>=0; i--) {
            maxHeap(arr, arr.length, i);
        }

        //最后一个跟第一个进行调整
        for(int i = arr.length-1; i>0; i--) {//因为数组从零开始的,所以最后一个是数组长度减一
            int temp = arr[0];//最前面的一个
            arr[0] = arr[i];//最后一个
            arr[i] = temp;
            //调整后再进行大顶堆调整
            maxHeap(arr, i, 0);
        }
    }
}

4.2 小顶堆排序

public class HeapSortMin {
    public static void main(String[] args) {
        int[] a = new int[]{16, 25, 7, 32, 6, 9};
        heapSort(a);//小顶堆
        System.out.println(Arrays.toString(a));
    }

    /**
     * 构造小顶堆
     * @param arr 待调整数组
     * @param size 调整多少
     * @param index 调整哪一个 最后一个叶子节点的父节点开始调整
     */
    public static void minHeap(int arr[], int size, int index) {

        //左子节点
        int leftNode = 2 * index + 1;
        //右子节点
        int rightNode = 2 * index + 2;

        int min = index;//假设自己最小

        //分别比较左右叶子节点找出最小
        if(leftNode < size && arr[leftNode] < arr[min]) {//如果左侧叶子节点小于min则将最小位置换成leftNode并且递归需要限定范围为数组长度,
            min = leftNode;//将最小位置改为左子节点
        }

        if(rightNode < size && arr[rightNode] < arr[min]) {//如果左侧叶子节点小于min则将最小位置换成rightNode
            min = rightNode;//将最小位置改为右子节点
        }

        //如果不相等就需要交换
        if(min != index) {
            int tem = arr[index];
            arr[index] = arr[min];
            arr[min] = tem;
            //如果下边还有叶子节点并且破坏了原有的堆。需要重新调整
            minHeap(arr, size, min);//位置为刚才改动的位置;
        }
    }

    /**
     * 需要将最小的顶部与最后一个交换
     * @param arr
     */
    public static void heapSort(int arr[]) {
        int start = (arr.length - 1)/2;//开始位置最后一个非叶子节点,最后一个叶子节点的父节点
        for(int i = start; i>=0; i--) {
            minHeap(arr, arr.length, i);
        }

        //最后一个跟第一个进行调整
        for(int i = arr.length-1; i > 0; i--) {
            int temp = arr[0];//最前面的一个
            arr[0] = arr[i];//最后一个
            arr[i] = temp;
            //调整后再进行小顶堆调整
            minHeap(arr, i, 0);
        }
    }
}

 

 

5、实际应用(将上述堆排序代码进行改造,对方法heapSort进行改造)

5.1、从N个元素中查找最大的a个元素。也就是传说中的topK问题。

public class HeapSort2 {
    public static void main(String[] args) {
        int[] a = new int[]{16, 25, 7, 32, 6, 9};
        int b [] = heapSort(a, 3);
        System.out.println(Arrays.toString(b));
    }

    /**
     * 构造大顶堆
     * @param arr 待调整数组
     * @param size 调整多少
     * @param index 调整哪一个 最后一个叶子节点的父节点开始调整
     */
    public static void maxHeap(int arr[], int size, int index) {

        //左子节点
        int leftNode = 2 * index + 1;
        //右子节点
        int rightNode = 2 * index + 2;

        int max = index;//假设自己最大

        //分别比较左右叶子节点找出最大
        if(leftNode < size && arr[leftNode] > arr[max]) {//如果左侧叶子节点大于max则将最大位置换成leftNode并且递归需要限定范围为数组长度,
            max = leftNode;//将最大位置改为左子节点
        }

        if(rightNode < size && arr[rightNode] > arr[max]) {//如果左侧叶子节点大于max则将最大位置换成rightNode
            max = rightNode;//将最大位置改为右子节点
        }

        //如果不相等就需要交换
        if(max != index) {
            int tem = arr[index];
            arr[index] = arr[max];
            arr[max] = tem;
            //如果下边还有叶子节点并且破坏了原有的堆。需要重新调整
            maxHeap(arr, size, max);//位置为刚才改动的位置;
        }
    }

    /**
     * 需要将最大的顶部与最后一个交换
     * @param arr
     */
    public static int[] heapSort(int arr[], int a) {
        int start = (arr.length - 1)/2;//开始位置最后一个非叶子节点,最后一个叶子节点的父节点
        for(int i = start; i>=0; i--) {
            maxHeap(arr, arr.length, i);
        }
        int count = 0;
        //最后一个跟第一个进行调整
        for(int i = arr.length-1; i > 0 && count < a; i--) {
            int temp = arr[0];//最前面的一个
            arr[0] = arr[i];//最后一个
            arr[i] = temp;
            //调整后再进行大顶堆调整
            maxHeap(arr, i, 0);
            count++;
        }
        int[] result = new int[a];//定义一个新数组,长度为a
        //其中:src表示源数组,srcPos表示源数组要复制的起始位置,desc表示目标数组,length表示要复制的长度。
        System.arraycopy(arr, arr.length - a, result, 0, a);//将旧数组arr.length-a的位置到末尾进行复制到新数组中
        return result;//返回值为所求最大a个元素
    }
}

5.2、同时快速得到最大的n个元素和最小的n个元素倒叙

public class HeapSort2 {
    public static void main(String[] args) {
        int[] a = new int[]{16, 25, 7, 32, 6, 9};
        int n = 3;
        Map<String, Object> map = getMaxAndMinArray(a, n);
        System.out.println("max n:"+Arrays.toString((int[]) map.get("max")));
        System.out.println("min n:"+Arrays.toString((int[]) map.get("min")));
    }

    public static Map<String, Object> getMaxAndMinArray(int arr[], int n) {
        int max[] = heapSort(arr, n, true);
        int min[] = heapSort(arr, n, false);//最小堆写法
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("max", max);
        map.put("min", min);
        return map;
    }

    /**
     * 构造大、小顶堆
     * @param arr 待调整数组
     * @param size 调整多少
     * @param index 调整哪一个 最后一个叶子节点的父节点开始调整
     */
    public static void maxHeap(int arr[], int size, int index, Boolean isMax) {

        //左子节点
        int leftNode = 2 * index + 1;
        //右子节点
        int rightNode = 2 * index + 2;

        int max = index;//假设自己最大、小
        if(isMax) {
            if(leftNode < size && arr[leftNode] > arr[max]) {//如果左侧叶子节点大于max则将最大位置换成leftNode并且递归需要限定范围为数组长度,
                max = leftNode;//将最大位置改为左子节点
            }
            if(rightNode < size && arr[rightNode] > arr[max]) {//如果左侧叶子节点大于max则将最大位置换成rightNode
                max = rightNode;//将最大位置改为右子节点
            }
        }else {
            if(leftNode < size && arr[leftNode] < arr[max]) {//如果左侧叶子节点小于max则将最小位置换成leftNode并且递归需要限定范围为数组长度,
                max = leftNode;//将最小位置改为左子节点
            }
            if(rightNode < size && arr[rightNode] < arr[max]) {//如果左侧叶子节点小于max则将最小位置换成rightNode
                max = rightNode;//将最小位置改为右子节点
            }
        }
        //如果不相等就需要交换
        if(max != index) {
            int tem = arr[index];
            arr[index] = arr[max];
            arr[max] = tem;
            //如果下边还有叶子节点并且破坏了原有的堆。需要重新调整
            maxHeap(arr, size, max, isMax);//位置为刚才改动的位置;
        }
    }

    /**
     * 需要将最大、最小的顶部与最后一个交换
     * @param arr
     */
    public static int[] heapSort(int arr[], int a, Boolean isMax) {
        int start = (arr.length - 1)/2;//开始位置最后一个非叶子节点,最后一个叶子节点的父节点
        for(int i = start; i>=0; i--) {
            maxHeap(arr, arr.length, i, isMax);
        }
        int count = 0;
        //最后一个跟第一个进行调整
        for(int i = arr.length-1; i > 0 && count < a; i--) {
            int temp = arr[0];//最前面的一个
            arr[0] = arr[i];//最后一个
            arr[i] = temp;
            //调整后再进行大、小顶堆调整
            maxHeap(arr, i, 0, isMax);
            count++;
        }
        int[] result = new int[a];
        System.arraycopy(arr, arr.length - a, result, 0, a);
        return result;
    }
}

 

6、小顶堆删除代码

删除定义(弹出最小值):小顶堆删除一个元素:删除总是发生在根A[0]处。最后一个元素被用来填补空缺位置,结果树被更新以恢复堆条件。(说的挺感人的,其实就是删除最小堆的第一个元素,然后将最后一个元素放到数组第一个的位置,然后再调换位置成功小顶堆。)

可能有点low,没想到更改的解决办法。原理:1、删除顶点,2、将最后一个变为顶点 3、调整为小顶堆 4、数组长度减一

public class HeapSortMin {
    public static void main(String[] args) {
        int[] a = new int[]{16, 25, 7, 32, 6, 9};
        int start = (a.length - 1)/2;//开始位置最后一个非叶子节点,最后一个叶子节点的父节点
        System.out.println("未排序之前:"+Arrays.toString(a));
        minHeap(a, start);//小顶堆
        System.out.println("小顶堆:"+Arrays.toString(a));
        //System.out.println("开始删除-----------");
        //int[] b = delete(a, start);
        //System.out.println("删除后小顶堆:"+Arrays.toString(b));

        System.out.println("开始插入-----------X");
        int[] c = insert(a, start, 1);
        System.out.println("插入后小顶堆:"+Arrays.toString(c));
    }

    /**
     * 构造小顶堆
     * @param arr 待调整数组
     * @param size 调整多少
     * @param index 调整哪一个 最后一个叶子节点的父节点开始调整
     */
    public static void minHeap(int arr[], int size, int index) {

        //左子节点
        int leftNode = 2 * index + 1;
        //右子节点
        int rightNode = 2 * index + 2;

        int min = index;//假设自己最小

        //分别比较左右叶子节点找出最小
        if(leftNode < size && arr[leftNode] < arr[min]) {//如果左侧叶子节点小于min则将最大位置换成leftNode并且递归需要限定范围为数组长度,
            min = leftNode;//将最小位置改为左子节点
        }

        if(rightNode < size && arr[rightNode] < arr[min]) {//如果左侧叶子节点小于min则将最小位置换成rightNode
            min = rightNode;//将最小位置改为右子节点
        }

        //如果不相等就需要交换
        if(min != index) {
            int tem = arr[index];
            arr[index] = arr[min];
            arr[min] = tem;
            //如果下边还有叶子节点并且破坏了原有的堆。需要重新调整
            minHeap(arr, size, min);//位置为刚才改动的位置;
        }
    }

    /**构造小顶堆
     * @param arr
     */
    public static void minHeap(int arr[], int start) {
        for(int i = start; i>=0; i--) {
            minHeap(arr, arr.length, i);
        }
    }

    public static int[] delete(int[] arr, int start) {
        int[] a = new int[arr.length - 1];
        //现在开始是个小顶堆,1、删除顶点,2、将最后一个变为顶点 3、调整为小顶堆 4、数组长度减一
        arr[0] = arr[arr.length - 1];
        arr[arr.length - 1] = -1;
        for(int i = start; i >= 0; i--) {
            minHeap(arr, arr.length - 1, i);//最后一个不进行小顶堆交换,因为是负一,比较无意义,最后要删除的
        }
       for(int i = 0; i < arr.length - 1 ;i++) {//获取除了最后一个的arr数组的所有元素
           a[i] = arr[i];
       }
        return a;
    }

    /**
     * 插入
     * @param arr 
     * @param start 开始位置
     * @param x 待插入的新值
     * @return 
     */
    public static int[] insert(int arr[], int start, int x) {
        //1、创建一个新数组长度比arr大一2、复制值到新的数组里面3、最后一位是要插入的补上去,4、交换位置为小顶堆结构
        int[] a = new int[arr.length + 1];//扩容1
        for(int i = 0; i <= arr.length - 1; i++) {
            a[i] = arr[i];
        }
        a[arr.length] = x;
        for(int i = start; i >= 0; i--) {
            minHeap(a, a.length , i);
        }
        return a;
    }
}

 

程序结果图:

 

 

7、小顶堆插入图解

小顶堆的插入是在堆尾部处插入。

逻辑是:1、创建一个新数组长度比arr大一2、复制值到新的数组里面3、最后一位是要插入的补上去,4、交换位置为小顶堆结构

代码在上边删除代码之下显示。

8、复杂度 未完待续。。。

 

 

 

  • 10
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要将大顶堆转换为小顶堆,可以按照以下步骤进行操作: 1. 首先,将大顶堆的根节点(最大值)与最后一个叶子节点进行交换。 2. 然后,将交换后的最后一个叶子节点移出。 3. 接下来,对根节点进行下沉操作(与其子节点中较小的那个进行交换),以维持小顶堆的性质。 4. 重复步骤2和3,直到所有节点都被移出。 具体的实现过程如下: 1. 假设要将大顶堆存储在数组中,根节点的索引为0。 2. 交换根节点与最后一个叶子节点,即将根节点的值与数组末尾元素交换。 3. 对根节点进行下沉操作,找到它与子节点中较小值的索引,然后将其交换。 4. 重复步骤2和3,直到所有节点都被移出。 以下是一个示例实现的伪代码: ``` # 将大顶堆转换为小顶堆 def convert_to_min_heap(heap): n = len(heap) # 从最后一个非叶子节点开始,依次向前处理 for i in range(n // 2 - 1, -1, -1): # 将当前节点下沉到合适位置 heapify(heap, n, i) return heap # 下沉操作 def heapify(heap, n, i): smallest = i left = 2 * i + 1 right = 2 * i + 2 # 找到左子节点和右子节点中的最小值 if left < n and heap[left] < heap[smallest]: smallest = left if right < n and heap[right] < heap[smallest]: smallest = right # 如果最小值不是当前节点,则将其与当前节点交换,并继续下沉 if smallest != i: heap[i], heap[smallest] = heap[smallest], heap[i] heapify(heap, n, smallest) ``` 此伪代码通过调用 `convert_to_min_heap` 函数,将输入的大顶堆转换为小顶堆。注意,此实现假设中已经存在数据,并且的大小为 n。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值