堆排序算法分析

1.堆结构

1.1这里需要用到的数据结构是二叉堆,二叉堆的性质如下:

  • 是一个完全二叉树
  • 父结点大于(或者小于两个子结点)
  • 两个子结点是一个二叉堆

1.2二叉堆的表示方法

使用数组来表示二叉堆,i的子结点是2*i+1 2*i+2 i的父结点是(i-1)/2

1.3二叉堆的插入和删除

为了满足上面二叉堆的性质,二叉堆在插入和删除的时候需要调整整个堆的数据

具体的办法:

增加:把元素插入末尾,然后不断和父结点比较,比较之后判断有没有必要交换,直到某个父结点大于两个子结点

删除:删除根结点,然后把最后一个添加到根结点,然后进行下沉操作,把父结点和两个子结点比较,不满足性质就交换,直到,父元素和两个子元素都满足大小关系

总的来说二叉堆的操作可以看做上浮(增加),下沉(删除)

 

2.代码实践

总的来说,堆排序包括插入和删除两个部分,插入的时候先插入到最末,然后上浮,删除的时候,删除根元素,然后把最末的提到第一个,然后执行下沉操作。

执行上浮的时候,不断找父结点,然后调整两个子结点和父结点之间的关系,直到某个父结点满足关系。

执行下沉的时候,也是比较父结点和子结点,但是这里,父结点下一步会被赋值成小的那个子结点(最小堆),然后继续递归,到父子关系正常为止

代码里面需要注意的地方:

  • 使用currentIndex来指向最后一个元素的下一位,因此需要小心边界元素
  • 注意跳出的条件,要么满足父子关系,要么到达边界,到达上边界应该是2*parent<currentIndex-2
/**
 * Created by kisstheraik on 16/8/14.
 * Description 堆排序的算法实践
 *  堆:
 *      一棵二叉树,父结点大于所有的子结点,父结点的左右子结点都是二叉堆,
 *      一般用数组表示堆,i的父结点是(i-1)/2,i的左右子结点分别是2*i+1 2*i+2
 *      每次插入和删除都需要调整
 */
public class Heap {

    public int[] list;//用来表示堆的数组
    public int currentIndex=0;

    public static void main(String[] args){
        int dataSize=5;
        Heap heap=new Heap(dataSize);

        for (int i=0;i<dataSize;i++)
        heap.add((int)(Math.random()*100));


        int index=0;

        while(index<heap.currentIndex){
            System.out.print(heap.list[index] + " ");
            index++;
        }
        System.out.println();
        index=0;
        while(index<dataSize){

            System.out.print(heap.delete() + " ");

            index++;
        }

    }

    public Heap(int size){
        this.list=new int[size];

        for(int i=0;i<size;i++){
            list[i]=Integer.MAX_VALUE;
        }
    }

    /**
     *
     * @param i int 新加入的结点
     */
    public void add(int i){

        //插入一个新的结点,然后下标递增
        list[currentIndex++]=i;

        //上浮
        //获取当前指针
        int cp=currentIndex-1;
        int parent=(cp-1)/2;
        while(parent>=0&&2*parent<currentIndex-2){

            int leftChild=2*parent+1;
            int rightChild=2*parent+2;

            if(list[parent]<=list[leftChild]&&list[parent]<=list[rightChild])break;

            if(list[parent]>list[leftChild]){

                int tmp=list[parent];
                list[parent]=list[leftChild];
                list[leftChild]=tmp;

            }

            if(list[parent]>list[rightChild]){
                int tmp=list[parent];
                list[parent]=list[rightChild];
                list[rightChild]=tmp;
            }

            parent=(parent-1)/2;


        }

    }

    /**
     * @description 删除根部的结点
     */
    public int delete(){
        if(currentIndex==0)return Integer.MAX_VALUE;
        int result=list[0];
        
        //第一个赋值成最后一个
        list[0]=list[currentIndex-1];

        int parent=0;
        
        while(2*parent<currentIndex-2){

            int leftChild=2*parent+1;
            int rightChild=2*parent+2;

            if(list[leftChild]>=list[parent]&&list[rightChild]>=list[parent])break;

            int child=0;
            if(list[leftChild]<list[rightChild]){
                child=leftChild;
            }else child=rightChild;

                int tmp=list[parent];
                list[parent]=list[child];
                list[child]=tmp;


            parent=child;



        }
        currentIndex--;
        

        return result;

    }


}

4.性能分析

最好,最坏和平均都是nlogn,其中建立堆的复杂度为O(n)排序为O(nlogn)

转载于:https://my.oschina.net/lovezfy/blog/732412

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值