栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

高级排序-堆排序

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

高级排序-堆排序

一、堆的数据结构

堆也是一种二叉树数据结构,也可以说是一种完全二叉树,但不一定是一颗满二叉树。

  • 满二叉树,当一个树的深度为n(n >= 1)时,该棵树共有2^n - 1个节点的树称为满二叉树

  • 完全二叉树,当一个树的深度为n(n >= 1)时,需满足该课树前 n-1层为2^(n-1) - 1个节点,如果最后一层的节点数不等于2^(n-1)次方个节点,则为完全二叉树,且最后一层的节点需要从左往右排列,则称为完全二叉树。


图二为不完全二叉树,因为最后一层的叶子节点没有满足从左往右排列。

  • 堆不同于树,在树结构中,父节点一般要大于左子节点,小于其右子节点,也就是说比某个节点小的,存放于该节点的左子树上,比该节点大的,存放于该节点的右子树上。而在堆数据结构中,要求父节点要大于等于两个子节点,而对两个子节点的顺序没有要求。

  • 堆用数据实现示意图如下:


从第一个元素开始存储数据,第一个元素就是整个树的根节点,
2和3下标处的元素为下标1处元素的两个子节点,同理,下标4,5的两个元素为下标2元素的两个节点。得出下标k节点的两个子节点的位置为2k(左子节点)和2k+1(右子节点)。一个节点k的父节点的位置为 k/2。

  • 堆数据存储

1.定义一个指针,指向数组中最后一个元素位置 + 1,将新元素插入数组,此时为了保证堆的有序性,需要使用上浮算法,将新插入的元素替换到指定位置。

  1. 定义一个指针,记录当前新插入元素的位置p,找到其父元素的位置p/2,比较当前元素与其父元素的大小,如果比父元素大,则交换位置。更新p的值,p = p / 2 再次找到父元素的父元素的位置,当发现p处元素小于p/2处元素或p > 1(根节点的位置为1)时就退出循环。循环完成,新插入的元素就被替换到了合适位置。
  • 删除堆中最大一个元素
  1. 由于下标为1处元素的值是最大的,交换数组第一个元素与最后一个元素的值,让数组最后一个元素等于null。此时相当于删除了最大元素。但是最后一个元素到了第一个位置,为了保证该元素能去到合适位置,需要使用下沉算法将该元素替换到指定位置。
  2. 设当前元素的下标为k,找到k的两个子节点2k和2k+ 1,比较两者中较大一个,如果k比较大一个小,则交换位置,k = 2k 或 k = 2k + 1,当k比其中一个大或者2k大于数组的length时,则退出循环。此时该元素通过下沉法存入了指定位置。
二、堆的API实现
public class Heap> {
    //存储堆中的元素
    private T[] items;
    //记录堆中元素的个数
    private int N;

    public Heap(int capacity) {
        this.items= (T[]) new Comparable[capacity+1];
        this.N=0;
    }

    //判断堆中索引i处的元素是否小于索引j处的元素
    private boolean less(int i,int j){
        return items[i].compareTo(items[j])<0;
    }

    //交换堆中i索引和j索引处的值
    private void exch(int i,int j){
        T temp = items[i];
        items[i] = items[j];
        items[j] = temp;
    }

    //往堆中插入一个元素
    public void insert(T t){
        items[++N]=t;
        swim(N);
    }

    //使用上浮算法,使索引k处的元素能在堆中处于一个正确的位置
    private void swim(int k){
        //通过循环,不断的比较当前结点的值和其父结点的值,如果发现父结点的值比当前结点的值小,则交换位置
        while(k>1){
            //比较当前结点和其父结点

            if (less(k/2,k)){
                exch(k/2,k);
            }

            k = k/2;
        }

    }

    //删除堆中最大的元素,并返回这个最大元素
    public T delMax(){
        T max = items[1];

        //交换索引1处的元素和最大索引处的元素,让完全二叉树中最右侧的元素变为临时根结点
        exch(1,N);
        //最大索引处的元素删除掉
        items[N]=null;
        //元素个数-1
        N--;
        //通过下沉调整堆,让堆重新有序
        sink(1);
        return max;
    }

    //使用下沉算法,使索引k处的元素能在堆中处于一个正确的位置
    private void sink(int k){
        //通过循环不断的对比当前k结点和其左子结点2*k以及右子结点2k+1处中的较大值的元素大小,如果当前结点小,则需要交换位置
        while(2*k<=N){
            //获取当前结点的子结点中的较大结点
            int max;//记录较大结点所在的索引
            if (2*k+1<=N){
                if (less(2*k,2*k+1)){
                    max=2*k+1;
                }else{
                    max=2*k;
                }
            }else {
                max = 2*k;
            }


            //比较当前结点和较大结点的值
            if (!less(k,max)){
                break;
            }

            //交换k索引处的值和max索引处的值
            exch(k,max);

            //变换k的值
            k = max;

        }


    }

    public static void main(String[] args) {
        Heap heap = new Heap(20);
        heap.insert("A");
        heap.insert("B");
        heap.insert("C");
        heap.insert("D");
        heap.insert("E");
        heap.insert("F");
        heap.insert("G");


        String del;
        while((del=heap.delMax())!=null){
            System.out.print(del+",");
        }

    }
}
三、堆排序

给定数组A;Integer[] arr = {19,22,157,465,22,95,23,44,531,107,2,11};使用堆数据结构对数组进行排序。

  1. 创建一个新数组B,将数组A 0-length-1处的值拷贝到数组B 1-length处,然后对B数组length / 2前的元素依次下沉,下沉完成以后,此时B数组就是一个合理的堆。
  2. 定义一个指针k,k为每次下沉范围,交换数组1与数组最后一个元素的位置,此时最大的元素来到了数组最后。然后将第一个元素下沉n,此时的下沉条件是2n < k,每次下沉一个元素,k–。最终是一个有序的堆。

堆排序实现

import java.util.Arrays;

public class HeapSortV2 {

    public static void sortByHeap(Comparable[] source){
        Comparable[] temp = new Comparable[source.length + 1];
        System.arraycopy(source,0,temp,1,source.length);
        //修正temp为合理的堆结构
        for (int i = temp.length / 2;i >0; i--) {
            sink(temp,i,temp.length - 1);
        }

        //开始交换数组元素
        sort(temp);
        //拷贝回原数组
        for (int i = 0; i < source.length; i++) {
            source[i] = temp[i + 1];
        }
    }

    private static void sort(Comparable[] temp) {
        int p = temp.length - 1;
        while (p > 1){
            exchange(temp,1,p);//交换第一个元素和最后一个元素的位置,此时最大元素来到了最后
            //下沉第一个元素.此时注意,下沉的范围只要 p - 1即可
            sink(temp,1,p-1);
            p--;
        }
    }


    //比较i处的值是否小于j处的值
    private static boolean less(Comparable[] arr,int i,int j){
        return arr[i].compareTo(arr[j]) < 0;
    }

    private static void exchange(Comparable[] arr,int i,int j ){
        Comparable temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    //下沉元素
    private static void sink(Comparable[] temp, int i,int range) {
        while (i * 2 <= range){
            //不能这样判断,造成数组下标越界异常
//            if (temp[(i * 2) + 1] == null){
            if ((i * 2) + 1 > range){
                //没有右节点,直接比较左节点
                if (less(temp,i,i * 2)){
                    exchange(temp,i,i * 2);
                    i = i * 2;
                }else {
                    break;
                }
            }else {
                //比较左右节点的大者
                int max = 0;
                if (less(temp,i * 2,(i * 2) + 1)){
                    max = (i * 2) + 1;
                }else {
                    max = i * 2;
                }
                if (less(temp,i,max)){
                    //交换位置
                    exchange(temp,i,max);
                    i = max;
                }else {
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {

        Integer[] arr = {19,22,157,465,22,107,2,11};//[null, 465, 22, 157, 19, 22, 107, 2, 11]
        //[null, 465, 22, 157, 19, 22, 107, 2, 11]
        sortByHeap(arr);

        System.out.println("arr " + Arrays.toString(arr));

    }

}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/883285.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号