排序&&堆排序

package Sort;

import java.util.Arrays;

/**
 * 堆排序
 *1,堆排序也是选择排序的一种,根据堆的特性,每次把最大或最小值(本次以最大值为例)
 *拿出来,按序排列;
2,堆排序是对普通选择排序的一种优化:如果是一个稳定堆,每次在选择最大值时,
只用沿着二叉树其中一个分叉去交换即可,其他分叉符合堆得特性(因是排好的稳定堆),
可以看作是稳定的,不用重排交换,省去了绝大多数的比较交换步骤,数组的数越多,分
支越多,该算法的优势就越明显;

3,第一步,将数组初始化为稳定堆,稳定堆的特性:二叉树之父节点总比其左右孩子节点大!
初始化稳定堆有很多方法,可以从堆顶向堆底方向初始化,也可以从堆底向堆顶方向排列初
始化,也可以通过小三角递归调等完成初始化,本例为理解方便,选用从堆底向堆顶方向初始
化,即,每次从小三角里找到最大值放在父节点,从最后一个小三角向前循环,第一遍,找到
所有数的最大值,第二遍循环找到次最大值放在第二层节点上,依次类推,完成稳定堆得初始化;

4,初始化完稳定堆之后,将选出的最大值与最后一个数字交换放在数组的最后固定下来
(以后循环不再用到此数字),此时除了顶部三角不稳定,下面都是稳定的,根据稳定堆
得特性(父节点总是大于其左右孩子节点),从顶部往底部寻找交换,只沿着变动的那个
分叉交换下去(只用单线一次循环即可),其他分叉不用动,交换完毕后,次最大数又被移到
顶部,此时的堆仍然是一个稳定堆,再将顶部的最大值交换的数组的后面固定下来,重复这个
步骤,依次类推,即可完成;
排序趟数:
1,初始化堆所用趟数:不确定,最大趟数是二叉树的层数,最小一趟

2,初始化堆后所用趟数:length-2次

排序原理:

稳定堆特性,二叉树排序,具体见上述排序思想
 * */
public class HeapSort {
    static int[] a;
    public HeapSort() {
        a=new int[] {8,19,2,3,100,99,1000,888,-1,0};
//      a=new int[] {6,5,3,1,8,7,2,4};
    }
    public HeapSort(int[] a) {
        this.a=a;
    }
//堆排序
    public static void heap_Sort(int[] array) {
        int length=array.length;
        inteHeap(array, length);//初始化稳定堆
        System.out.println("初始化稳定堆"+Arrays.toString(array));
        switchData(array, 0, length-1);//交换第一个元素和本轮最后一个元素
        //二叉排序
        for(int length2=length-1; length2>1; length2--) {//循环length-2次
            int index=0;
            while(2*index+1<length2) {//只要有左孩子节点就能产生交换,进入循环
                if(2*index+2<length2) {//左右孩子都有
                    int max=index;  //默认是父节点的下标
                    if(array[max]<array[2*index+1]) {
                        max=2*index+1;//返回左孩子的下标
                    }
                    if(array[max]<array[2*index+2]) {
                        max=2*index+2;//返回右孩子的下标
                    }
                    if(max!=index) {
                        switchData(array, index, max);
                        index=max;
                    }else {
                        break;//max=index;表示节点最大,下面的堆都是稳定的,不用继续循环
                    }

                }
                if(2*index+1<length2 &&2*index+2>=length2)//只有左孩子没有右孩子
                    if(array[index]<array[2*index+1]) {
                        switchData(array, index, 2*index+1);

                    }else {
                        break;//2*index+1==index表示节点最大,下面的对是最稳定的,不用继续循环
                    }

            }
            put();
            System.out.println();
            switchData(array, 0, length2-1);
            put();
            System.out.println();
        }


    }

//初始化堆 所有的根都大于他的孩子
    public static  void  inteHeap(int[] array,int length) {
        int high=length-1;//最大下标
        boolean isChange=false;

        //K的初始值就是最大的一个非终端节点
        for(int k=(high-1)/2;k>=0;k--) {
            //找到最后一个父节点
            int left=2*k+1; //左孩子的下标
            int right=left+1;//右孩子的下标
            //判断是否有节点
            if(left<=high) {
                int max=left;
                if(right<=high) {
                    if(array[left]<array[right]) {
                        max=right; //返回左孩子或右孩子中值最大的下标
                    }
                }
                //将最大值根父节发交换
                if(array[max]>array[k]) {
                    isChange=true;
                    switchData(array,max,k);
                }
            }
        }
        if(isChange) {//如果一次就能完成稳定堆的初始化,则不需要递归调用,以达到优化算法
            inteHeap(array, length);
            System.out.println("递归"+Arrays.toString(array));
        }
    }
    //交换数组中俩个数的方法因为要多次用到,所以封装成一个方法
    public static void  switchData(int[] array,int index1,int index2) {
        array[index1]^=array[index2];
        array[index2]^=array[index1];
        array[index1]^=array[index2];
    }
    //遍历
    public static void put() { 
        for (int i : a) {
            System.out.print(i+"\t");
        }
    }
    public static void main(String[] args) {
        HeapSort hp=new HeapSort();
        hp.heap_Sort(a);
        hp.put();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值