通用堆排序算法A

问候,

我被要求写一周小贴士; 因此,这里有很多主题

在此论坛(以及许多其他论坛)的此处开始,提到了

有关数据排序的问题。

两种主要问题方案的示例如下:

1)包含名字的数组和包含名字的另一个数组

可能是包含一个人年龄的第三个数组; 如何排序

根据名字来排列。

2)对象列表,其中每个对象都包含名字,姓氏和

年龄。 该列表必须根据姓氏进行排序。

如果实现比较器,则第二种情况很容易解决。

像下面这样的东西会很好:


public class PersonComparator implements Comparator<Person> {
    public int compare(Person l, Person r) {
        return l.getLastName().compareTo(r.getLastName());
    }
}
请注意,根据Comparable <T>接口的约定,

单一方法需要实现。 可以忽略equals()方法,

可能会付出少量性能损失的代价。

您需要将这样的PersonComparator的实例传递给以下任何一个:

a)Collections.sort()

b)Arrays.sort()

方法,即第一种方法使用Comparator对列表进行排序

严格的比较工作,或者第二种方法使用此方法对数组进行排序

比较器。

但是情形1呢? 当然,方案1开始时设计很差

当然,这不是您的设计,而是一些遗留代码

部署设计的第一个实现时您就不在身边。

更糟糕的是,方案2的解决方案仅适用于数组或列表。

场景2与场景1相似,需要其他数据结构

根据一些排序标准进行排序。 这两种方法(虽然有用)

对于需要分类的其他内容没有多大帮助。

同样,这里有两种可能的情况(我分别称为A和B):

A)将您的数据迁移到数组或列表中,并使用上述方法

以上; 之后,将已排序的数据重新迁移为原始数据,

继续。

要么

B)想出别的东西。

这篇小文章是关于场景B的,即我们提出了一些建议

其他。 关于要分类的任何类型的数据,我们到底需要知道什么?

首先,我们需要知道要排序多少个数据项。 我们还需要

知道一个数据项是否大于,等于或小于另一个数据

项目。 第三,我们希望能够交换(或交换)两个数据项。 给定

我们希望能够想到的第一个概念,即数据项0,

数据项1等

上面的概念只是乞求一个接口定义。 这是一个简单的例子:


public interface Sortable {
    int length();
    int compare(int i, int j);
    void swap(int i, int j);
}
请注意,无论采用哪种排序方式,此界面看起来多么琐碎

能够对Sortable进行排序,它完全不需要了解

它排序的数据类型,并且不在乎如何交换两个数据项

并不在乎为什么将一个数据项视为小于,等于或等于

大于其他数据项。

这篇小文章展示了一种“堆排序”排序方法,它非常有能力

做这份工作。 堆排序方法并不比快速传播的方法差

sort方法,它可以在任何其他幼稚的sorting方法周围运行。

堆排序方法的big-Oh等于或优于快速排序方法

另一个好处是,它在著名的big-Oh数中是稳定的。

在我忘记之前,它不需要任何额外的内存

到要排序的数据的大小。

本文根据Sortable接口介绍了堆排序算法。

因此,当需要比较两个数据项时,该算法将调用compare()

方法,当需要交换它们时,Sortable可以这样做等等。

这是一个定义:

堆是二叉树,因此对于任何节点P,其子L和R(如果

存在)不小于P,即compare(P,L)和compare(P,R)都返回

值> = 0。

请注意,叶子本身就是堆(叶子没有孩子)

出于(几乎)没有特殊原因,这是一个带有十个节点的二叉树:


.                 0
               /     \
              1       2        
            /   \   /   \
           3    4   5    6
          / \  / 
          7 8  9
请原谅我(缺乏)ASCII艺术。 这棵树确实显示出一些有趣的地方

属性(无证明):

1:节点i的左孩子编号为2 * i + 1;

2:节点i的右子编号为2 * i + 2;

3:如果节点i> 0有k个孩子,则节点i-1至少有k个孩子;

4:每个节点都有尽可能多的孩子。

这四个概念定义了一个“完整的”二叉树。 那些数学家

定义。 因此,序列0、1、2 ... n-1可以看作是一个完整的二进制

树。

再次回到堆排序问题:假设节点P大于

树中每个节点的子L和R都如上所述(如上所示)。

然后,树的根是所有树中最大的元素。

根据定义,一棵树上的所有叶子都是堆,但是其余的呢

树节点(靠近树的根)? 解决方案很简单:我们可以

考虑到左边和左边的事实,用二叉树构建一个堆

正确的子树已经是堆; 它的工作方式如下:

令P为父节点,L和R为P的左右子节点。

1:如果P> = L并且P> = R,那么我们已经有一个堆,否则:

2:令M为L和R的最大值;

3:将P与该最大M交换,然后再次从步骤1开始。

对于每个具有n个节点的完整二叉树,我们知道的最后n / 2个节点

那棵树是叶子,所以它们已经是堆了。 (用我的坏东西检查一下

上面的ASCII小技巧)。

因此,我们可以使用上面的三行算法将(子)树变成堆。

让我们使用前面定义的Sortable接口来完成; 开始:


// given a Sortable, build a heap starting at node p. There a n nodes in total
private static void heapify(Sortable s, int p, int n) { 
    for (int r, l= (p<<1)+1; l < n; p= l, l= (p<<1)+1) { 
        // l is the maximum of l and r, the two subnodes of p 
        if ((r= l+1) < n && s.compare(l, r) < 0) l= r; 
        // check if parent p is less than maximum l
        if (s.compare(p, l) < 0) s.swap(p, l);
        else break;
    }
} 
一切都很好,但是如何将Sortable变成堆呢? 我们将只使用

上面的小方法。 传统上,这部分称为“阶段1”

堆排序算法。 为什么要打破传统?


private static void phase1(Sortable s) { 
    // heapify all the non-leaf nodes         
    for (int n= s.length(), p= n/2; p >= 0; p--)
        heapify(s, p, n);
} 
第二阶段,传统上称为“阶段2” * ahem *,按如下方式使用堆

整理一切。 树的根是所有元素中最大的元素

节点。 如果我们将根与Sortable的最后一个元素交换,

这个最大的元素就位(最后)。 但是现在树可能不是

一堆了。 使用第一种方法,我们可以再次将树变成堆

但是我们忽略了最后一个节点(已经存在)。

我们重复以上步骤,直到堆仅包含一个节点。 这个

节点必须是Sortable中所有元素中的最小元素。 开始:


// sort the Sortable
private static void phase2(Sortable s) { 
    for (int n= s.length(); --n > 0; ) {
        s.swap(0, n);         // put the root element in its place
        heapify(s, 0, n);     // and restore the heap again
    }        
} 
我不认为堆排序方法本身简单就不足为奇

看起来像这样:


// driver for the worked methods
public static Sortable sort(Sortable s) {  
    phase1(s);     // build initial heap
    phase2(s);     // sort the sortable given the heap 
    return s;     // return the Sortable for convenience
} 
我们将在第2部分中继续这篇小文章; 所有这一切,因为文章不能

长度超过10,000个字符。 待会见

亲切的问候,

乔斯

From: https://bytes.com/topic/java/insights/642119-generic-heap-sort-algorithm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值