败者树 java_败者树的Java实现

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Comparator;

import java.util.Iterator;

import java.util.List;

/**

* 败者树,对多个有序的数据源进行归并排序

* 原理:2个子结点比较后的败者(较大值)放入它们的父结点,而胜者(较小值)送到它们父结点的父节点去再作比较

*

* @author Anthony

* @param

*/

public class LoserTree {

/**

* 数据源列表,为叶子节点提供数据,数据源的输出顺序必须有序

*/

private Iterator[] datas;

/**

* 叶子节点,存的是实际的数据

* 叶子节点和数据源是一一对应的,即第一个叶子节点记录第一个数据源的当前数据

*/

private Object[] leafs;

/**

* 非叶子节点,记录叶子节点的下标, 根据节点的值可以定位到叶子节点所指向的数据

* 根据这个数组得到有序的数据

*/

private int[] nodes;

/**

* 叶子节点数据的比较对象

*/

private Comparator comparator;

/**

* 构造方法,按照元素的Comparable接口实现进行排序

*

*/

public LoserTree(List> branches) {

this(branches, new Comparator() {

@SuppressWarnings("unchecked")

@Override

public int compare(T o1, T o2) {

return ((Comparable) o1).compareTo(o2);

}

});

}

/**

* 构造方法, 指定数据源分支的迭代器和元素比较对象

* 迭代器的输出必须有序并且与Comparator对象的比较结果保持一致

*

* @param brs

* @param comparator

*/

@SuppressWarnings("unchecked")

public LoserTree(List> brs, Comparator comparator) {

this.datas = brs.toArray(new Iterator[0]);

this.comparator = comparator;

this.init();

}

/**

* 初始化构建败者树

*/

private void init() {

int size = this.datas.length;

this.leafs = new Object[size];

this.nodes = new int[size];

// 为叶子节点赋值

for (int i = 0; i < size; i++) {

this.put(i);

}

// 找到叶子节点中最小值的下标

int winner = 0;

for (int i = 1; i < size; i++) {

if (loser(i, winner)) {

winner = i;

}

}

// 非叶子节点全部初始化为最小值对应的叶子节点下标

Arrays.fill(nodes, winner);

// 从后向前依次调整非叶子节点

for (int i = size - 1; i >= 0; i--)

adjust(i);

}

/**

* 设置第index个叶子节点的下一个数据

* 如果数据源已结束,则设置为null

*

* @param index

*/

private void put(int index) {

Iterator branch = this.datas[index];

this.leafs[index] = branch.hasNext() ? branch.next() : null;

}

/**

* 获取第index个叶子节点的当前数据

*

* @param index

* @return

*/

@SuppressWarnings("unchecked")

private T get(int index) {

return (T) leafs[index];

}

/**

* 判断index1对应的节点是否小于index2对应的节点

*

* @param index1

* @param index2

* @return

*/

private boolean loser(int index1, int index2) {

T t1 = (T) get(index1);

T t2 = (T) get(index2);

if (t1 == null)

return false;

if (t2 == null)

return true;

// 这里, 当叶节点数据相等时比较分支索引是为了实现排序算法的稳定性

int n = comparator.compare(t1, t2);

return n != 0 ? n < 0 : index1 < index2;

}

/**

* 调整第index个叶子节点

* 具体调整过程为: 叶子节点和父节点比较, 败者(较大值)留在父节点位置, 胜者(较小值)继续和祖父节点比较,直至最终

*

* @param index

*/

private void adjust(int index) {

int size = this.datas.length;

int t = (size + index) / 2; // 父节点

while (t > 0) {

// 如果当前值比父节点要大,那么留在父节点位置;否则继续和祖父节点比较

if (loser(nodes[t], index)) {

int temp = nodes[t];

nodes[t] = index;

index = temp;

}

t /= 2;

}

nodes[0] = index; // 根节点始终为最小值

}

/**

* 依次读取数据源的数据进行归并排序, 返回排序后的数据列表

*

* @return 从小到大的顺序

*/

public List merge(int bufferSize) {

List list = new ArrayList();

T top = null;

while ((top = get(nodes[0])) != null) {

list.add(top); // 取得最小值

put(nodes[0]); // 索引对应的数据被取出,需要从数据源中再读入一个

adjust(nodes[0]); // 根据新插入的叶子节点重新调整树

if (list.size() == bufferSize)

break;

}

return list;

}

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值