问候,
我被要求写一周小贴士; 因此,这里有很多主题
在此论坛(以及许多其他论坛)的此处开始,提到了
有关数据排序的问题。
两种主要问题方案的示例如下:
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