JAVA_Heap_Sort

本文详细介绍JAVA堆排序的算法思想及实现过程,包括如何构建大顶堆、调整堆结构以实现排序,通过具体代码实例展示了堆排序的具体步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JAVA 堆排序

堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
这里写图片描述

算法思想:

将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次最大值。如此反复执行,就能得到一个有序序列了。

问题:

  • 如何由一个无序序列构建成一个堆?

  • 如何在输出堆顶元素后,调整剩余元素成为一个新的堆?

package algstest;


public class HeapSort {

    public static void main(String[] args) {
        int[] arr = { 50, 10, 90, 30, 70, 40, 80, 60, 20 };
        System.out.println("排序之前:");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }

        // 堆排序
        heapSort(arr);

        System.out.println();
        System.out.println("排序之后:");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }

    /**
     * 堆排序
     */
    private static void heapSort(int[] arr) { 
        // 将待排序的序列构建成一个大顶堆
        // arr.length/2 是第一个叶子节点的下标
        // 在[0, arr.length/2 -1]之间的节点都有左右子树的
        // 在[arr.length/2, arr.length]之间都是叶子节点
        for (int i = arr.length / 2 -1; i >= 0; i--){ 
            heapAdjust(arr, i, arr.length); 
        }

        // 逐步将每个最大值的根节点与末尾元素交换,并且再调整二叉树,使其成为大顶堆
        for (int i = arr.length - 1; i > 0; i--) { 
            swap(arr, 0, i); // 将堆顶记录和当前未经排序子序列的最后一个记录交换
            heapAdjust(arr, 0, i); // 交换之后,需要重新检查堆是否符合大顶堆,不符合则要调整
        }
    }

    /**
     * 构建堆的过程
     * @param arr 需要排序的数组
     * @param i 需要构建堆的根节点的序号
     * @param n 数组的长度
     */
    private static void heapAdjust(int[] arr, int i, int n) {
        int child;
        int father; 
        for (father = arr[i]; leftChild(i) < n; i = child) {
            child = leftChild(i);

            // 如果左子树小于右子树,则需要比较右子树和父节点
            if (child != n - 1 && arr[child] < arr[child + 1]) {
                child++; // 序号增1,指向右子树
            }

            // 如果父节点小于孩子结点,则需要交换
            if (father < arr[child]) {
                arr[i] = arr[child];
            } else {
                break; // 大顶堆结构未被破坏,不需要调整
            }
        }
        arr[i] = father;
    }

    // 获取到左孩子结点
    private static int leftChild(int i) {
        return 2 * i + 1;
    }

    // 交换元素位置
    private static void swap(int[] arr, int index1, int index2) {
        int tmp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = tmp;
    }
}

转载https://blog.csdn.net/zdp072/article/details/44227317

<think>好的,我现在需要帮用户解释堆排序在Python和Java中的实现。首先,我得回忆一下堆排序的基本原理,确保自己理解正确。堆排序是一种基于二叉堆(通常是最大堆)的排序算法,主要步骤包括构建堆、然后反复取出堆顶元素(最大值),再调整剩余部分维持堆结构,直到所有元素排序完成。时间复杂度是O(n log n),属于不稳定排序。 接下来,用户要求用Python和Java分别展示实现,我需要考虑两种语言的不同语法特性。比如在Python中,列表(list)是动态数组,而Java中使用数组。然后,我要确定如何表示堆结构。通常在数组中,父节点和子节点的索引关系是:对于节点i,左子节点在2i+1,右子节点在2i+2,父节点在(i-1)//2。这个部分需要正确应用,避免出错。 对于Python的实现,首先需要写一个heapify函数,用来调整某个节点及其子树,使其满足最大堆的性质。然后构建最大堆,从最后一个非叶子节点开始向前遍历,对每个节点调用heapify。之后进行排序,每次将堆顶元素(最大值)与当前末尾元素交换,缩小堆的大小,再调整堆顶元素。需要注意的是Python中的列表索引是从0开始的,所以在计算父节点和子节点时要准确。 Java的实现逻辑类似,但因为Java是静态类型语言,需要明确数组的类型,比如int[]。同样需要heapify方法,但Java中参数传递是值传递,所以需要传递数组、堆的大小和当前节点索引。构建堆的过程同样是从最后一个非叶子节点开始,循环调用heapify。排序阶段同样是交换元素,调整堆结构。这里要注意数组索引的处理,和Python类似,都是从0开始。 在编写示例代码时,需要确保语法正确,比如Python中的缩进,Java中的大括号和分号。同时,要提供测试用例,展示代码的正确性。比如在Python中用示例数组进行排序,输出结果;在Java中同样创建数组,调用堆排序方法后打印结果。 需要检查是否有常见的错误,比如在heapify函数中,比较左右子节点时是否越界,递归或循环终止条件是否正确。例如,在heapify过程中,需要找到左右子节点中的最大值,然后与父节点交换,并继续向下调整。这一步容易出错,尤其是在处理索引时。 另外,要考虑用户可能的疑问,比如为什么堆排序是不稳定的?例如,相同元素在排序后的相对位置可能改变,因为堆的结构调整过程中可能会交换不相邻的元素。这一点是否需要解释?但用户可能更关注实现,暂时可以不深入,除非用户后续提问。 最后,要确保解释清晰,分步骤说明堆排序的过程,让用户能够理解代码的逻辑。同时比较Python和Java实现的异同,指出语法上的差异,比如Java的类型声明和数组处理,而Python的简洁性。可能用户想了解两种语言在实现同一算法时的不同之处,所以需要明确对比。 总结下来,我需要分步骤解释堆排序的流程,给出两种语言的代码示例,并确保代码正确无误,语法正确,注释清晰。同时,验证代码的正确性,通过示例数组测试,确保输出正确排序的结果。这整个过程需要逻辑严谨,避免遗漏关键步骤或出现语法错误。</think>堆排序Heap Sort)是一种基于二叉堆数据结构的排序算法,其核心思想是通过构建最大堆(或最小堆)来实现排序。以下是分步解释及Python和Java的实现对比: --- ### **一、堆排序原理** 1. **构建堆**:将无序数组调整为最大堆(父节点 ≥ 子节点)。 2. **排序阶段**: - 交换堆顶(最大值)与末尾元素 - 缩小堆范围,重新调整剩余元素为最大堆 - 重复直到所有元素有序 时间复杂度:$O(n \log n)$,空间复杂度:$O(1)$(原地排序) --- ### **二、Python实现** ```python def heapify(arr, n, i): largest = i left = 2 * i + 1 right = 2 * i + 2 if left < n and arr[left] > arr[largest]: largest = left if right < n and arr[right] > arr[largest]: largest = right if largest != i: arr[i], arr[largest] = arr[largest], arr[i] heapify(arr, n, largest) def heap_sort(arr): n = len(arr) # 构建最大堆(从最后一个非叶子节点开始) for i in range(n // 2 - 1, -1, -1): heapify(arr, n, i) # 逐个提取最大值 for i in range(n - 1, 0, -1): arr[i], arr[0] = arr[0], arr[i] # 交换 heapify(arr, i, 0) return arr # 示例 arr = [12, 11, 13, 5, 6, 7] print(heap_sort(arr)) # 输出 [5, 6, 7, 11, 12, 13] ``` --- ### **三、Java实现** ```java public class HeapSort { public static void heapify(int[] arr, int n, int i) { int largest = i; int left = 2 * i + 1; int right = 2 * i + 2; if (left < n && arr[left] > arr[largest]) { largest = left; } if (right < n && arr[right] > arr[largest]) { largest = right; } if (largest != i) { int swap = arr[i]; arr[i] = arr[largest]; arr[largest] = swap; heapify(arr, n, largest); } } public static void heapSort(int[] arr) { int n = arr.length; // 构建最大堆 for (int i = n / 2 - 1; i >= 0; i--) { heapify(arr, n, i); } // 逐个提取最大值 for (int i = n - 1; i > 0; i--) { int temp = arr[0]; arr[0] = arr[i]; arr[i] = temp; heapify(arr, i, 0); } } public static void main(String[] args) { int[] arr = {12, 11, 13, 5, 6, 7}; heapSort(arr); System.out.println(Arrays.toString(arr)); // 输出 [5, 6, 7, 11, 12, 13] } } ``` --- ### **四、Python与Java的关键差异** | 特性 | Python | Java | |--------------------|-------------------------------------|-------------------------------------| | 数组操作 | 动态列表(灵活大小) | 静态数组(需预先定义长度) | | 类型系统 | 动态类型 | 静态类型(需声明变量类型) | | 递归实现 | 更简洁的语法 | 需显式传递数组和参数 | | 交换元素 | 直接使用元组交换 `a, b = b, a` | 需临时变量辅助交换 | --- ### **五、核心步骤对比** 1. **`heapify`函数**: - 作用:调整子树为最大堆 - 逻辑:比较父节点与子节点,递归调整 2. **构建堆**: - 起点:最后一个非叶子节点(索引为 `n//2 - 1` 或 `n/2 - 1`) 3. **排序阶段**: - 每次将堆顶元素交换到末尾,缩小堆范围后重新调整 --- 通过以上实现可以看出,堆排序的核心逻辑在两种语言中高度一致,差异主要体现在语法细节和数据类型处理上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值