zoukankan      html  css  js  c++  java
  • 基本排序算法之堆排序

    1、堆的概念

    堆排序依赖的数据结构是完全二叉树,要想是完全二叉树,前提必须是二叉树(废话),二叉树就要求父亲结点至多有两个孩子,即可以有一个、两个或者没有孩子。

    完全二叉树则是在二叉树的基础上多了一些限制条件,那就是:

      1、要么二叉树的每一层都是满的,即除叶子结点之外,其他结点都必须拥有左右孩子;

      2、要么最后一层可以不满,但是最后一层的叶子结点必须位于靠左边放置;

    满足上述条件之一就是一棵标准的完全二叉树。然后在满足完全二叉树的基础上,这个二叉树的还要满足堆属性,才能称之为一个堆,这里我们以大顶堆作为例子,要求就是:父亲结点的值大于或者等于它的任一孩子。

    2、堆的存储

    我们存储一个堆,通过数组或者ArrayList进行存储,其中存储的时候,元素在数组中的位置也是有讲究的,要求:

      索引值为i的结点,它的左孩子位于数组的2i+1位置,右孩子位于数组的2i+2位置

      其父亲结点位于(i-1)/2位置,i>=1

      数组的首个元素,是堆的根结点,也是当前堆中最大的元素。

    3、向堆添加一个新结点

    给堆添加新结点,是构建堆和扩充堆的基础工作,基本的思路就是,先将新结点放到堆的最后位置,然后由底向上逐个进行判断是否符合大顶堆的属性,如果符合堆的属性则完成添加任务。

    具体思想如下:

      新结点设置为当前结点

      while 当前结点大于其父亲结点

        交换当前结点和其父亲结点的值

        当前结点向上攀登了一层

    4、删除根节点

    删除根节点就是删除堆中的最大元素,删除完成之后,就需要现有的堆不一定符合大顶堆的属性,所以需要对删除根节点之后的堆进行调整,那我们具体如何完成呢,具体步骤如下:

      将堆的最后一个结点赋值给根结点,赋值完成之后,设置根节点为当前结点

      while 当前结点含有孩子结点 && 当前结点小于它的子结点

        将当前结点的值最大的孩子结点的值和当前结点进行交换

        当前结点向下掉了一层

    将上面的添加和删除结点的算法思想封装成一个完整的类,实现的代码如下:

    import java.util.ArrayList;
    
    /*
        二叉堆满足两个条件:
            1.是一个完全二叉树,完全二叉树就是所有层都是满的,或者最后一层可以不满,但是所有叶子节点必须在左边
            2.父亲节点的值大于等于任意一个孩子节点的值
     */
    
    public class Heap<E extends Comparable<E>>{
        private ArrayList<E> list = new ArrayList<>();
    
        public Heap(){
    
        }
        public Heap(E[] objects){
            for (E object : objects) {
                add(object);
            }
        }
    
        // 向堆增加一个新节点,首先添加到堆的末尾,然后自底向上根据堆的属性调整堆
    
        public void add(E newObject){
            list.add(newObject);
            // 数组列表中最后一个节点的索引,也即是刚插入的节点的索引
            int curIndex = list.size() - 1;
    
            while (curIndex > 0){
                // 求出当前节点的父亲节点的索引值
                int parentIndex = (curIndex - 1) / 2;
                // 将当前节点的值和其父亲节点的值进行比较,如果大于父亲节点的值则二者进行交换
                if (list.get(curIndex).compareTo(list.get(parentIndex)) > 0){
                    E temp = list.get(curIndex);
                    list.set(curIndex,list.get(parentIndex));
                    list.set(parentIndex,temp);
                }else {
                    break;
                }
                curIndex = parentIndex;
            }
        }
    
        // 删除根节点,首先将最后一个节点替换掉根节点,然后按照堆的属性调整重建二叉树
    
        public E remove(){
            if (list.size() == 0){
                return null;
            }
            E removeObject = list.get(0);
            // 将最后一个节点替换根节点
            list.set(0,list.get(list.size()-1));
            list.remove(list.size()-1);
            int curIndex = 0;
            while (curIndex < list.size()){
                int leftChildIndex = 2 * curIndex +1;
                int rightChildIndex = 2 * curIndex + 2;
                if (leftChildIndex >= list.size()){
                    break;
                }
                int maxIndex = leftChildIndex;
                if (rightChildIndex < list.size()){
                    if (list.get(maxIndex).compareTo(list.get(rightChildIndex)) < 0){
                        maxIndex = rightChildIndex;
                    }
                }
    
                if (list.get(curIndex).compareTo(list.get(maxIndex)) < 0){
                    E temp = list.get(maxIndex);
                    list.set(maxIndex,list.get(curIndex));
                    list.set(curIndex,temp);
                    curIndex = maxIndex;
                }else {
                    break;
                }
            }
            return removeObject;
        }
    
        public int getSize(){
            return list.size();
        }
    
    }

    5、堆排序(基于构建堆和删除根节点)

      堆排序的思想就是借助于大顶堆的特点,每次将堆的根节点删除即获得当前堆中的最大值,直接将堆中所有元素删除完,那么获得就是一个降序的元素列表。因此要实现堆排序,首先将给定的一组元素列表构建成堆,那么就按照添加新结点的方式进行,然后开始排序,即按照删除根节点的方式进行(获得的结果是降序的)。

    最终的实现代码如下:

    public class HeapSort {
        public <E extends Comparable<E>> void heapSort(E[] list){
    
            Heap<E> heap = new Heap<>();
            for (E e : list) {
                heap.add(e);
            }
    
            for (int i = list.length-1; i >=0 ; i--) {
                list[i] = heap.remove();
            }
        }
    
        public static void main(String[] args) {
            Integer[] list = {-44,-5,-3,3,3,1,-4,0,1,2,4,5,53};
            HeapSort heapSort = new HeapSort();
            heapSort.heapSort(list);
            for (Integer integer : list) {
                System.out.print(integer + " ");
            }
        }
    }

    参考自《Java语言程序设计》(进阶篇)

  • 相关阅读:
    centos7上安装JupyterHub
    我的测试第一篇博客
    TCP,SYN,FIN扫描
    nmap使用帮助翻译
    MySQL 常用函数
    MySQL 视图
    MySQL 索引
    JAVA 注解
    JAVA 反射
    spring boot MySQL极简封装
  • 原文地址:https://www.cnblogs.com/yxym2016/p/12952400.html
Copyright © 2011-2022 走看看