Java堆排序

概念储备

堆排序

堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。

堆是一棵顺序存储完全二叉树

每个结点的值都**大于**或等于其左右孩子结点的值,称为大顶堆(小根堆);
每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆(大根堆)。

如下图:
在这里插入图片描述

二叉树的种类(了解):满,完全,平衡
1、满二叉树
   所有叶结点同处于最底层(非底层结点均是内部结点),一个深度为k(>=-1)且有2^(k+1) - 1个结点。:
2、完全二叉树
   叶结点只能出现在最底层的两层,且最底层叶结点均处于次底层叶结点的左侧。规模为n的完全二叉树,高度为
3、平衡二叉树
   平衡二叉树(Self-balancing binary search tree)又被称为AVL	树(有别于AVL算法),且具有以下性质:
   它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
   平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。 
   最小二叉平衡树的节点的公式如下 F(n)=F(n-1)+F(n-2)+1 这个类似于一个递归的数列,
   可以参考Fibonacci(斐波那契)数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量。(百度百科)
   对于平衡二叉树要特别注意的是,不要求非叶节点都有两个子结点,仅要求两个子树的高度差的绝对值不超过1,或者为空树。

同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子
在这里插入图片描述

该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

ok,了解了这些定义。接下来,我们来看看堆排序的基本思想及基本步骤:

堆排序基本思想及步骤

堆排序的基本思想是:
  将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。
  将其与末尾元素进行交换,此时末尾就为最大值。
  然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

简单图解:

以上思想可归纳为两个操作:

(1)根据初始数组去构造初始堆(构建一个完全二叉树,保证所有的父结点都比它的孩子结点数值大)。
(2)每次交换第一个和最后一个元素,输出最后一个元素(最大值),然后把剩下元素重新调整为大根堆。

当输出完最后一个元素后,这个数组已经是按照从小到大的顺序排列了。

先通过详细的实例图来看一下,如何构建初始堆。

设有一个无序序列 { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 }。

在这里插入图片描述

构造了初始堆后,我们来看一下完整的堆排序处理:
还是针对前面提到的无序序列 { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 } 来加以说明。

在这里插入图片描述

相信,通过以上两幅图,应该能很直观的演示堆排序的操作处理,如果不能理解请看详细图解。

详细图解

步骤一: 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。
a.假设给定无序序列结构如下
在这里插入图片描述

2.此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。

在这里插入图片描述

4.找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。
在这里插入图片描述

这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。
在这里插入图片描述

此时,我们就将一个无需序列构造成了一个大顶堆。

步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。

a.将堆顶元素9和末尾元素4进行交换

在这里插入图片描述

b.重新调整结构,使其继续满足堆定义

在这里插入图片描述

c.再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.

在这里插入图片描述

后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序
在这里插入图片描述

再简单总结下堆排序的基本思路

a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

代码:
/**
 * 堆排序例子:
 *      堆底层就是一个完全二叉树
 *      时间复杂度为o(nLogn)
 * 		理解参考博客:
 *      http://www.cnblogs.com/chengxiao/p/6129630.html
 *      http://www.cnblogs.com/jingmoxukong/p/4303826.html
 */
public class _08HeapSort {
    public static void main(String []args){
        int []arr = {9,8,7,6,5,4,3,2,1};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void sort(int []arr){
        //1.构建大顶堆
        for(int i=arr.length/2-1;i>=0;i--){
            //从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(arr,i,arr.length);
        }
        //2.调整堆结构+交换堆顶元素与末尾元素
        for(int j=arr.length-1;j>0;j--){
            swap(arr,0,j);//将堆顶元素与末尾元素进行交换
            adjustHeap(arr,0,j);//重新对堆进行调整
        }

    }

    /**
     * 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
     * @param arr
     * @param i
     * @param length
     */
    public static void adjustHeap(int []arr,int i,int length){
        int temp = arr[i];//先取出当前元素i
        for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
            if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
                k++;
            }
            if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
                arr[i] = arr[k];
                i = k;
            }else{
                break;
            }
        }
        arr[i] = temp;//将temp值放到最终的位置
    }

    /**
     * 交换元素
     * @param arr
     * @param a
     * @param b
     */
    public static void swap(int []arr,int a ,int b){
        int temp=arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}

其他算法:

Java二分查找法
Java冒泡排序
Java选择排序
Java插入排序
Java希尔排序
Java计数排序
Java快排算法
Java归并排序
Java堆排序
动图演示

### 回答1: Java 中的堆排序是一种比较快速的排序算法,它的时间复杂度为 O(nlogn),其中 n 是要排序的数组的长度。它的基本思想是:将数组中的元素构建成一个大根堆,然后依次将堆顶元素(也就是最大的元素)与末尾元素交换,再重新构建大根堆,以此类推,直到整个数组有序。 以下是 Java 中的堆排序代码: ``` public void heapSort(int[] arr) { // 构建大根堆 for (int i = arr.length / 2 - 1; i >= 0; i--) { adjustHeap(arr, i, arr.length); } // 堆排序 for (int j = arr.length - 1; j > 0; j--) { // 将堆顶元素与末尾元素交换 swap(arr, 0, j); // 重新调整为大根堆 adjustHeap(arr, 0, j); } } /** * 调整大根堆 */ public void adjustHeap(int[] arr, int i, int len) { // 将当前节点的值保存到临时变量中 int temp = arr[i]; // k 指向当前节点的左子节点 for (int k = i * 2 + 1; k < len; k = k * 2 + 1) { // 如果当前节点的右子节点存在,并且大于左子节点,则指向右子节点 if (k + 1 < len && arr[k] < arr[k + 1]) { k++; } // 如果子节点大于父节点,则交换 if (arr[k] > temp) { arr[i] = arr[k]; i = k; } ### 回答2: 堆排序(Heap Sort)是一种通过构建最大堆或最小堆来实现的排序算法,它的平均时间复杂度为O(nlogn)。 下面是Java语言实现堆排序的代码: ``` public class HeapSort { 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); } } // 调整以root为根节点的子树成为最大堆 public static void heapify(int arr[], int size, int root) { int largest = root; // 初始化最大元素为根节点 int left = 2 * root + 1; // 左子节点的索引 int right = 2 * root + 2; // 右子节点的索引 // 如果左子节点大于根节点,将最大元素索引更新为左子节点 if (left < size && arr[left] > arr[largest]) { largest = left; } // 如果右子节点大于根节点,将最大元素索引更新为右子节点 if (right < size && arr[right] > arr[largest]) { largest = right; } // 如果最大元素不是根节点,则交换根节点和最大元素,并继续调整子树 if (largest != root) { int temp = arr[root]; arr[root] = arr[largest]; arr[largest] = temp; heapify(arr, size, largest); } } public static void main(String args[]) { int arr[] = {56, 23, 12, 78, 45, 10, 45}; heapSort(arr); System.out.println("排序后的数组:"); for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } } } ``` 以上代码定义了一个`HeapSort`类,其中包含了一个`heapSort`方法和一个`heapify`方法。`heapSort`方法用于执行堆排序算法,而`heapify`方法用于调整以某个节点为根节点的子树,使其成为最大堆。 在`heapSort`方法中,首先从最后一个非叶子节点开始构建最大堆。然后,依次将最大的元素与数组末尾交换,并重新构建最大堆。最后,输出排序后的数组。 在`main`方法中,我们定义了一个待排序的数组,并调用`heapSort`方法对其进行排序。最后,输出排序后的数组。 以上是Java中实现堆排序的代码。 ### 回答3: Java 堆排序是一种使用堆数据结构进行排序的算法。下面是一个简单的Java实现: ```java import java.util.Arrays; public class HeapSort { public static void heapSort(int[] array) { int n = array.length; // 构建最大堆 for (int i = n / 2 - 1; i >= 0; i--) heapify(array, n, i); // 从堆顶开始不断将最大元素移至数组末尾 for (int i = n - 1; i >= 0; i--) { // 交换堆顶元素与当前末尾元素 int temp = array[0]; array[0] = array[i]; array[i] = temp; // 对剩余元素重新构建最大堆 heapify(array, i, 0); } } // 将数组中的元素构建为最大堆 public static void heapify(int[] array, int n, int i) { int largest = i; // 初始化堆顶元素为最大值 int left = 2 * i + 1; // 左子节点的索引位置 int right = 2 * i + 2; // 右子节点的索引位置 // 如果左子节点大于根节点,将largest设置为左子节点 if (left < n && array[left] > array[largest]) largest = left; // 如果右子节点大于当前最大值,将largest设置为右子节点 if (right < n && array[right] > array[largest]) largest = right; // 如果largest不是根节点,将largest与根节点交换,并继续构建最大堆 if (largest != i) { int swap = array[i]; array[i] = array[largest]; array[largest] = swap; heapify(array, n, largest); } } public static void main(String[] args) { int[] array = {10, 7, 8, 9, 1, 5}; heapSort(array); System.out.println(Arrays.toString(array)); } } ``` 上述代码实现了堆排序。首先,它使用构建最大堆的函数 `heapify` 将输入数组构建为最大堆。然后,在每次循环中,将堆顶元素(即最大值)与当前数组末尾元素交换,然后对剩余元素重新构建最大堆。通过这样的迭代过程,最终得到一个有序的数组。 在主函数中,我创建了一个测试数组并调用堆排序函数 `heapSort`。最后,通过使用 `Arrays.toString()` 函数将排序结果打印出来。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值