数据结构小白之堆排序

谈谈堆排序

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

什么是堆?

堆是由以下两种不同性质的完全二叉树组成:

(1): 每个节点的值大于等于其左右孩子节点值称为大顶堆 (arr[i]>=arr[2*i+1] && arr[i]>=arr[a*i+2])

(2): 每个节点小于等于其左右孩子节点的值称为小顶堆 (arr[i]<=arr[2*i+1] && arr[i]<=arr[a*i+2])

图示:
 

 

注: 一般来说,大顶堆用于升序排列,小顶堆用于降序排列

堆排序的思路:(在这里以升序为例)

tip1.将待排序的序列构建为一个大顶堆(此时整个序列中最大的元素就是堆顶的根节点)【代码演示的时候会详细说这一段】

tip2.将数组的头元素和末尾元素进行交换,此时末尾为最大值(将最大的值沉到了末端)

tip3.将剩余的元素重新构建一个堆,反复执行上述1,2操作,直到得到了整个有序序列

康康代码:

1.准备待操作的数组: 4,6,8,5,9

2.开始进行将序列构建为大顶堆的过程

/**
     * 功能: 完成将以i为非叶子节点的子树调整为大顶堆
     * 4 6 8 5 9 => i=1 => adjustHeap => 4 9 8 5 6
     * 如果再次调用   i=0 => adjustHeap => 9 6 8 5 4
     *
     * <p>
     * arr: 待调整的数组实例(从左到右 从下到上进行调整)
     * i: 表示非叶子节点的索引
     * length: 对多少个元素进行调整,length在逐渐减少
     */
    public static void adjustHeap(int[] arr, int i, int length) {
        count++;
        //先取出当前元素的值,保存在临时变量中
        int temp = arr[i];
        //开始调整
        //1.k=i*2+1 说明k是i节点的左子节点;k=k*2+1 说明了k的下一轮是k节点的当前子节点

        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
            //说明当前左子节点小于右子节点的值
            if (k + 1 < length && arr[k] < arr[k + 1]) {
                k++;//k指向右子节点
            }
            if(arr[k]>temp){//如果子节点大于父节点
                arr[i]=arr[k];//把较大的值赋给当前节点
                i=k;//!! 让i指向k(找到下一组子树进行比较)
            }else{
                break;//! 从小到大 从下到上进行调整
            }
        }
        //循环结束后,将以i为子树的最大值已经放在了最顶部,此时的i不再是原来的i了
        arr[i]=temp;//将temp放置到调整后的位置
    }

注: 由于这里是对子树进行操作,比较难以理解,所以画了一个图示帮助理解(在这里是第一轮代码结束后的过程,即是对于6 5 9元素的大顶堆转换操作,但是其余的操作和上述类似,大家可以获取完整代码后debug一下)

3.分步操作【针对本次需求而言,可以这么弄,但是还是需要弄出统一的方式进行分析操作

 public static void heapSort(int[] arr) {
        int temp=0;
        System.out.println("堆排序");
        adjustHeap(arr,1,arr.length);
        //第一次处理子树
       System.out.println("第一次调整过后:"+ Arrays.toString(arr));// 4 9 8 5 6 ??
        //在原来的基础上进行调整
       adjustHeap(arr,0,arr.length);
        //第二次处理子树
        System.out.println("第二次调整过后:"+ Arrays.toString(arr));// 9,6,8,5,4
}

4.数组转化为大顶堆统一方案

  public static void heapSort(int[] arr) {
        int temp=0;
        System.out.println("堆排序");
        //调整子树
        for(int i=arr.length/2-1;i>=0;i--){
            adjustHeap(arr,i,arr.length);
        }
        System.out.println("调整过后:"+Arrays.toString(arr));

        /**
         * 将堆底的元素和堆顶元素进行交换
         * */
        for(int j=arr.length-1;j>0;j--){
            //交换
            temp=arr[j];
            arr[j]=arr[0];
            arr[0]=temp;
            //将一棵树调整为大顶堆
           
            adjustHeap(arr,0,j);
        }
        System.out.println("调整过后:"+Arrays.toString(arr));
    }

注:此时tip1已经完成

5.堆顶元素和堆底元素进行交换(日常交换)

  for(int j=arr.length-1;j>0;j--){
            //交换
            temp=arr[j];
            arr[j]=arr[0];
            arr[0]=temp;
            //将一棵树调整为大顶堆
            adjustHeap(arr,0,j);
        }

此时在循环中反复调用adjustHeap,完成tip2和tip3

6.处理结果

完整代码:

package tree_op.heap_sort;

import java.util.Arrays;
/**
 * 800万个数据 大概3s左右排完 O(nlogn)
 * */
public class HeapSort {
    public static void main(String[] args) {
        //操作数组,将数组进行升序排序
        int arr[] = {4, 6, 8, 5, 9};
        heapSort(arr);
    }

    //编写一个堆排序的方法
    public static void heapSort(int[] arr) {
        int temp=0;
        System.out.println("堆排序");
//        adjustHeap(arr,1,arr.length);
//        //第一次处理子树
//        System.out.println("第一次调整过后:"+ Arrays.toString(arr));// 4 9 8 5 6 ??
//        //在原来的基础上进行调整
//        adjustHeap(arr,0,arr.length);
//        //第二次处理子树
//        System.out.println("第二次调整过后:"+ Arrays.toString(arr));// 9,6,8,5,4

        //第1棵子树调整过后可得
        for(int i=arr.length/2-1;i>=0;i--){
            adjustHeap(arr,i,arr.length);
        }
        System.out.println("调整过后:"+Arrays.toString(arr));
        /**
         * 将堆底的元素和堆顶元素进行交换
         * */
        for(int j=arr.length-1;j>0;j--){
            //交换
            temp=arr[j];
            arr[j]=arr[0];
            arr[0]=temp;
            //将一棵树调整为大顶堆
            adjustHeap(arr,0,j);
        }
        System.out.println("调整过后:"+Arrays.toString(arr));
    }

    /**
     * 功能: 完成将以i为非叶子节点的子树调整为大顶堆
     * 4 6 8 5 9 => i=1 => adjustHeap => 4 9 8 5 6
     * 如果再次调用   i=0 => adjustHeap => 9 6 8 5 4
     *
     * <p>
     * arr: 待调整的数组实例(从左到右 从下到上进行调整)
     * i: 表示非叶子节点的索引
     * length: 对多少个元素进行调整,length在逐渐减少
     */
    public static void adjustHeap(int[] arr, int i, int length) {
        //先取出当前元素的值,保存在临时变量中
        int temp = arr[i];
        //开始调整
        //1.k=i*2+1 说明k是i节点的左子节点;k=k*2+1 说明了k的下一轮是k节点的当前子节点

        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
            //说明当前左子节点小于右子节点的值
            if (k + 1 < length && arr[k] < arr[k + 1]) {
                k++;//k指向右子节点
            }
            if(arr[k]>temp){//如果子节点大于父节点
                arr[i]=arr[k];//把较大的值赋给当前节点
                i=k;//!! 让i指向k(找到下一组子树进行比较)
            }else{
                break;//! 从小到大 从下到上进行调整
            }
        }
        //循环结束后,将以i为子树的最大值已经放在了最顶部,此时的i不再是原来的i了
        arr[i]=temp;//将temp放置到调整后的位置
    }
}
发布了193 篇原创文章 · 获赞 70 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/Lzinner/article/details/103257423