排序算法(三)堆排序及有界堆排序Java实现及分析

news/2024/5/19 7:13:35 标签: 堆排序, 有界堆排序, 排序算法

1.堆排序

基数排序适用于大小有界的东西,除了他之外,还有一种你可能遇到的其它专用排序算法:有界堆排序。如果你在处理非常大的数据集,你想要得到前 10 个或者前k个元素,其中k远小于n,它是很有用的。

例如,假设你正在监视一 个Web 服务,它每天处理十亿次事务。在每一天结束时,你要汇报最大的k个事务(或最慢的,或者其它最 xx 的)。一个选项是存储所有事务,在一天结束时对它们进行排序,然后选择最大的k个。需要的时间与nlogn成正比,这非常慢,因为我们可能无法将十亿次交易记录在单个程序的内存中。我们必须使用“外部”排序算法

我们首先了解一下堆,这是一个类似于二叉搜索树(BST)的数据结构。有一些区别:

  • 在 BST 中,每个节点x都有“BST 特性”:x左子树中的所有节点都小于x,右子树中的所有节点都大于x
  • 在堆中,每个节点x都有“堆特性”:两个子树中的所有节点都大于x
  • 堆就像平衡的 BST;当你添加或删除元素时,他们会做一些额外的工作来重新使树平衡。因此,可以使用元素的数组来有效地实现它们。

现在讨论的是小根堆。如果子树中的节点都小于根节点,则为大根堆。

堆中最小的元素总是在根节点,所以我们可以在常数时间内找到它。在堆中添加和删除元素需要的时间与树的高度h成正比。而且由于堆总是平衡的,所以hlog n成正比。

JavaPriorityQueue使用堆实现。PriorityQueue提供Queue接口中指定的方法,包括offerpoll

  • offer:将一个元素添加到队列中,更新堆,使每个节点都具有“堆特性”。需要logn的时间。
  • poll:从根节点中删除队列中的最小元素,并更新堆。需要logn的时间。

给定一个PriorityQueue,你可以像这样轻松地排序的n个元素的集合 :

  • 使用offer,将集合的所有元素添加到PriorityQueue
  • 使用poll从队列中删除元素并将其添加到List

因为poll返回队列中剩余的最小元素,所以元素按升序添加到List。这种排序方式称为堆排序

向队列中添加n个元素需要nlogn的时间。删除n个元素也是如此。所以堆排序的运行时间是O(n logn)



2.代码实现:
	/**
     * @Author Ragty
     * @Description 堆排序
     * @Date 19:15 2019/6/12
     **/
    public void heapSort(List<T> list,Comparator<T> comparator) {
        PriorityQueue<T> heap = new PriorityQueue<T>(list.size(),comparator);
        heap.addAll(list);
        list.clear();
        while(!heap.isEmpty()) {
            list.add(heap.poll());
        }
    }

测试代码:

list = new ArrayList<Integer>(Arrays.asList(3, 5, 1, 4, 2));
sorter.heapSort(list, comparator);
System.out.println(list);



3.有界堆排序

有界堆是一个限制为最多包含k个元素的堆。如果你有n个元素,你可以跟踪这个最大的k个元素:

最初堆是空的。对于每个元素x

  • 分支 1:如果堆不满,请添加x到堆中。
  • 分支 2:如果堆满了,请与堆中x的最小元素进行比较。如果x较小,它不能是最大的k个元素之一,所以你可以丢弃它。
  • 分支 3:如果堆满了,并且x大于堆中的最小元素,请从堆中删除最小的元素并添加x

使用顶部为最小元素的堆,我们可以跟踪最大的k个元素。我们来分析这个算法的性能。对于每个元素,我们执行以下操作之一:

  • 分支 1:将元素添加到堆是O(log k)
  • 分支 2:找到堆中最小的元素是O(1)
  • 分支 3:删除最小元素是O(log k)。添加x也是O(log k)

在最坏的情况下,如果元素按升序出现,我们总是执行分支 3。在这种情况下,处理n个元素的总时间是O(n log k),对于n是线性的。


4.代码实现:
	/**
     * @Author Ragty
     * @Description 有界堆排序
     * @Date 19:49 2019/6/12
     **/
    public List<T> topK(int k,List<T> list,Comparator<T> comparator) {
        PriorityQueue<T> heap = new PriorityQueue<T>(list.size(),comparator);
        for (T element : list) {
            if (heap.size() < k) {
                heap.offer(element);
                continue;
            }
            int cmp = comparator.compare(element,heap.peek());
            if (cmp>0) {
                heap.poll();
                heap.offer(element);
            }
        }
        List<T> res = new LinkedList<T>();
        while (!heap.isEmpty()) {
            res.add(heap.poll());
        }
        return res;
    }

测试代码:

list = new ArrayList<Integer>(Arrays.asList(6, 3, 5, 8, 1, 4, 2, 7));
List<Integer> queue = sorter.topK(4, list, comparator);
System.out.println(queue);



5.空间复杂性

到目前为止,我们已经谈到了很多运行时间的分析,但是对于许多算法,我们也关心空间。例如,归并排序的一个缺点是它会复制数据。在我们的实现中,它分配的空间总量是O(n log n)。通过优化,可以将空间降至O(n)

相比之下,插入排序不会复制数据,因为它会原地排序元素。它使用临时变量来一次性比较两个元素,并使用一些其它局部变量。但它的空间使用不取决于n

我们的堆排序实现创建了新PriorityQueue,来存储元素,所以空间是O(n); 但是如果你能够原地对列表排序,则可以使用O(1)的空间执行堆排序

刚刚实现的有界堆栈算法的一个好处是,它只需要与k成正比的空间(我们要保留的元素的数量),而k通常比n小得多 。

软件开发人员往往比空间更加注重运行时间,对于许多应用程序来说,这是适当的。但是对于大型数据集,空间可能同等或更加重要。例如:

  • 如果一个数据集不能放入一个程序的内存,那么运行时间通常会大大增加,或者根本不能运行。如果你选择一个需要较少空间的算法,并且这样可以将计算放入内存中,则可能会运行得更快。同样,使用较少空间的程序,可能会更好地利用 CPU 缓存并运行速度更快。
  • 在同时运行多个程序的服务器上,如果可以减少每个程序所需的空间,则可以在同一台服务器上运行更多程序,从而降低硬件和能源成本。

http://www.niftyadmin.cn/n/1661929.html

相关文章

快速搭建一个“微视”类短视频 App

欢迎大家前往腾讯云社区&#xff0c;获取更多腾讯海量技术实践干货哦~ 本文由腾讯云视频发表于云社区专栏 关注公众号“腾讯云视频”&#xff0c;一键获取 技术干货 | 优惠活动 | 视频方案 “爱就像蓝天白云晴空万里&#xff0c;突然暴风雨……”偷偷在上班期间看微视里美丽的小…

在 Confluence 6 中连接一个 LDAP 目录

希望将 Confluence 连接到一个 LDAP 目录&#xff1a; 在屏幕的右上角单击 控制台按钮 &#xff0c;然后选择 General Configuration 链接。在左侧的面板中单击 用户目录&#xff08;User Directories&#xff09;。 添加&#xff08;Add&#xff09;一个目录&#xff0c;并且选…

使用Charles对移动设备抓包

1.Charles简介 Charles是一款代理服务器&#xff0c;通过成为电脑或者浏览器的代理&#xff0c;然后截取请求和请求结果达到分析抓包的目的。该软件是用Java写的&#xff0c;能够在Windows&#xff0c;Mac&#xff0c;Linux上使用&#xff0c;安装Charles的时候要先装好Java环…

Javascript -- 精通Date

方法 Date对象本身有三个方法&#xff0c;常用的是Date.now()&#xff0c;这个适用于IE9&#xff0c;移动端通行。 Date.now ie9&#xff0c;方法返回自1970年1月1日 00:00:00 UTC到当前时间的毫秒数&#xff0c;类型为Number。不支持的浏览器可以这样&#xff1a; if (!Date.n…

spring拦截器-过滤器的区别

1. 理解 拦截器 &#xff1a;是在面向切面编程的时候&#xff0c;在你的 service 或者一个方法前调用一个方法&#xff0c;或者在方法后调用一个方法&#xff1b;比如动态代理就是拦截器的简单实现&#xff0c;在你调用方法前打印出字符串&#xff08;或者做其它业务逻辑的操作…

朋友圈投票活动-刷票案例实现与分析

1.声明 本文只讨论技术范畴内的刷票行为。 2.案例描述 某商城&#xff08;以下简称A商城&#xff09;在微信平台上举办了一场在线投票活动&#xff0c;微信用户可通过活动链接访问到投票页面&#xff0c;对喜欢的作品进行投票&#xff1b;每个微信帐号每天只能给单个作品投1张…

Spring Boot升级到2.2.x数据连接报错及分析

1.问题 最近需要升级下项目&#xff0c;我之前版本是1.5.6&#xff0c;升级到最新的2.2.x时&#xff0c;发现会报以下错误&#xff1a; 2019-11-21 20:36:42.998 WARN 224 --- [ restartedMain] com.zaxxer.hikari.util.DriverDataSource : Registered driver with driver…