堆排序_C#

了解堆排序前,需要先了解二叉树(请自行了解)

首先堆排序是完全二叉树,大根或者小根,随便,然后父节点得到左孩子2i+1,右孩子2i+2,孩子得到父亲(i-1)/2

堆排序——堆的向下调整性质

假设根节点的左右子树都是堆,但根节点不满足堆的性质,也就是不满足大根或者小根

可以通过一次向下的调整来将其变成一个堆。

以大根堆举例,先把取下来,比如2,就需要一个元素填上去,找左右孩子最大的移上去,比如左孩子是9右孩子8,把9移上去,那么9这个位置空着,如果2满足比9孩子都大,比如0,1,直接2填上去,不满足,比如6,4,继续重复之前操作,把6移上去,最后发现4是叶子节点,那么把2填到6的位置,结束排序。

现在,我们经过排序得到了一个大根堆 ,我们需要挨个出数,从根开始,9移出来,把堆得最后一个元素比如3填上去,然后进行向下调整,调整结束后继续移出根,比如8,重复直到全部移出,最后得到一个有序列表。

那么首先我们需要构造堆,把根想象成省长,孩子是市长,接着是县长,村长,如果我们要让省长管好省,那么我们需要先让市长管理好市,也就是要让最下级首先管理好自身,往上调整,直到市长。也就是先找到最后一个非叶子节点,将它向下调整为大根堆,接着找前一个村,接着找县,接着前一个县(注意是从右往左找,因为完全二叉树的特性)。。。。。

首先来实现最关键的向下调整 

 public void Sift(List<int> li, int low, int high)//low代表最上层,high代表最下层
    {
        int i = low;//i指向移动留下的空位,开始指向根节点
        int j = 2 * i + 1;    //j指向左孩子
        int tmp = li[low];//取出并记录需要调整的元素,开始时根节点
        while (j<=high)//只要还存在孩子节点,也就是不超出最下层
        {
            if (j+1<=high&&li[j+1]>li[j])//如果有右孩子,并且右孩子比较大
            {
                j = j + 1;//j指向右孩子
            }
            if (li[j]>tmp)//如果孩子比需要调整的元素大
            {
                li[i] = li[j];//孩子移上去,空出位置
                i = j;//i往下一层移,也就是移动到j层
                j = 2 * i + 1;//j也往下移一层
            }
            else//如果孩子小,直接把需要调整的元素填到空位上,也就是i
            {
                li[i] = tmp;//其实这一步可以省了,反正结束循环后也会执行一次
                break;//结束调整
            }
        }
        li[i] = tmp;//填回去
    }

 接着是堆排序的主体部分,包括构造和出数

public void HeapSort(List<int> li)
    {
        int n = li.Count;//列表长度
        for (int i = ((n-1-1)/2); i > -1; i--)//从列表最后一个元素的父节点开始向上调整
        {
            Sift(li,i,n-1);//最上层就是i,最下层一直指向最后一个元素
        }
        for (int i = n-1; i >-1; i--)//开始出数,i指向堆的最后一个元素
        {
            int tmp = li[i];//交换根和最后一个元素
            li[i] = li[0];
            li[0] = tmp;
            Sift(li,0,i-1);//交换结束后向下调整,注意i需要往前移
        }
    }

可以分别测试一下构造

9孩子84,8孩子73,4还是12,7孩子65,没问题 

接着出数

很明显,没问题 

堆排序的时间效率O(nlogn) 

PS:如果要小根堆改下sift函数的判断符号就好了

输出就会是降序的 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值