堆,堆排序

本文深入探讨了堆数据结构的实现原理,包括完全二叉树的定义、堆的插入与删除操作,以及如何通过堆实现优先队列。同时,介绍了堆排序算法,并提供了构建堆的优化方法和原地排序的实现。

很久没写博客了 还是抽时间写一写~

堆用来做优先队列 插入与删除平均时间复杂度都为O(n),以链表实现插入是O(1),删除是O(n),数组实现反之

JUC包就有优先队列的实现(后面深入并发后再补充)

判断:叶子节点为左节点时是完全二叉树,叶子节点为右节点时,不连续紧密排列,非完全二叉树

堆的定义:

堆是一颗完全二叉树。堆的基本要求是堆中所有结点的值必须大于(或小于)其孩子结点的值。

完全二叉树:

若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层有叶子结点,并且叶子结点都是从左到右依次排布。

堆是数据结构(以大根堆为例)

public class Heap {
    // 容量
    private int capbility;
    // 元素大小
    private int count;
    // 元素数组
    private int[] data;

    Heap(int capbility) {
        this.data = new int[capbility+1];
        this.capbility = capbility;
        this.count = 0;
    }

}

注意 这里是capbility+1; 因为我们堆结点是从索引1开始,从1到capbility+1,data[0]是空

介绍两个性质

获取父结点 i/2

获取子结点 2*i 和 2*i+1

(如果是capbility则只是计算不同,获取父结点 (i-1)/2,子结点 2*i +1和 2*i+2 )

然后是插入操作

插入到是插入到最后的结点位置,容量++,为了保持堆的性质,开始上升shiftUp操作,不断与父结点比较,如果大于父结点则交换,到一个节点的时候或者满足堆的性质

 public void insert(int item) {
        // FIXME 可改为扩容操作
        assert(count + 1 <= capbility);
        count++;
        data[count] = item;
        shiftUp(count);
    }

    private void shiftUp(int k) {
        while (k > 1 && data[k] > data[k/2])
        {
            // 与父结点交换
            SortHelper.swap(data,k,k/2);
            // 继续比较
            k = k/2;
        }
    }

删除操作

把data[1](ps:data[0]为空)删除,容量--,为了保持堆的性质,把data[count]换到data[1],开始shiftDown操作,不断与子节点比较,与子节点中大的那个交换位置,到无子节点或者已经满足大于堆的性质

   public int delete(){
        assert( count > 0 );
        int item = data[1];
        data[1] = data[count];
        count --;
        shiftDown(1);
        return item;
    }

    private void shiftDown(int k) {
        // 如果有左节点
        while (2*k <= count){
            int j = 2*k;
            // data[j] 是 data[2*k]和data[2*k+1]中的最大值
            if (j+1 <= count && data[j] < data[j+1]) {
                j = j+1;
            }
            // 与子节点最大的进行比较 如果大于子节点 就直接跳出
            if( data[k] >= data[j] ) break;
            // 交换位置和索引继续比较
            SortHelper.swap(data,k,j);
            k = j;
        }
    }

测试结果

 

堆排序

由上面得出结论 我们按照顺序每次把删除的元素就保存就是堆排序 是不是很简单~

   public static void main(String[] args) {
        int n = 10;
        Heap heap = new Heap(n);
        for (int i = 0; i < 10; i++) {
            heap.insert((i+1)*2);
        }
        System.out.println("构建的堆:"+Arrays.toString(heap.getData()));
        int[] arr = new int[n];
        for (int i = 0; i < n ; i++) {
            arr[i] = heap.delete();
        }
        System.out.println("排序后的数组:"+Arrays.toString(arr));
    }

 构建堆优化

其实构建堆不用每次都加一个元素就shiftUp操作

叶子节点一开始就一个 无需进行任何操作,所以我们从第一个非叶子节点进行,也就是22(索引5)和之前的元素进行shiftDown操作 ,

获取第一个非叶子节点:获取最后一个叶子节点(data[count])的父结点(data[count/2])

public void heapify(int[] arr, int n) {
        for( int i = 0 ; i < n ; i ++ )
            data[i+1] = arr[i];
        count = n;
        for (int i = n/2; i >= 1; i--) {
            shiftDown(i);
        }
    }

注意我们这里是>= 1,因为我们从索引1开始计算

 

原地排序(不需要借助辅助空间) 

把最大的data[0]一直往数组最后放,放完该排序的数组大小-1,再重复进行这个操作

  public static void localSort(int[] arr,int n) {
        heapify(arr, n);
        for (int i = 0; i < n ; i++) {
            SortHelper.swap(arr,0,n-(i+1));
            __shiftDown(arr, n-(i+1), 0);
        }
    }

代码优化

(1)普通用户端(全平台) 音乐播放核心体验: 个性化首页:基于 “听歌历史 + 收藏偏好” 展示 “推荐歌单(每日 30 首)、新歌速递、相似曲风推荐”,支持按 “场景(通勤 / 学习 / 运动)” 切换推荐维度。 播放页功能:支持 “无损音质切换、倍速播放(0.5x-2.0x)、定时关闭、歌词逐句滚动”,提供 “沉浸式全屏模式”(隐藏冗余控件,突出歌词与专辑封面)。 多端同步:自动同步 “播放进度、收藏列表、歌单” 至所有登录设备(如手机暂停后,电脑端打开可继续播放)。 音乐发现与管理: 智能搜索:支持 “歌曲名 / 歌手 / 歌词片段” 搜索,提供 “模糊匹配(如输入‘晴天’联想‘周杰伦 - 晴天’)、热门搜索词推荐”,结果按 “热度 / 匹配度” 排序。 歌单管理:创建 “公开 / 私有 / 加密” 歌单,支持 “批量添加歌曲、拖拽排序、一键分享到社交平台”,系统自动生成 “歌单封面(基于歌曲风格配色)”。 音乐分类浏览:按 “曲风(流行 / 摇滚 / 古典)、语言(国语 / 英语 / 日语)、年代(80 后经典 / 2023 新歌)” 分层浏览,每个分类页展示 “TOP50 榜单”。 社交互动功能: 动态广场:查看 “关注的用户 / 音乐人发布的动态(如‘分享新歌感受’)、好友正在听的歌曲”,支持 “点赞 / 评论 / 转发”,可直接点击动态中的歌曲播放。 听歌排行:个人页展示 “本周听歌 TOP10、累计听歌时长”,平台定期生成 “全球 / 好友榜”(如 “好友中你本周听歌时长排名第 3”)。 音乐圈:加入 “特定曲风圈子(如‘古典音乐爱好者’)”,参与 “话题讨论(如‘你心中最经典的钢琴曲’)、线上歌单共创”。 (2)音乐人端(创作者中心) 作品管理: 音乐上传:支持 “无损音频(FLAC/WAV)+ 歌词文件(LRC)+ 专辑封面” 上传,填写 “歌曲信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值