Java数据结构与排序算法 (三)

优先队列

不仅仅是“先进先出”,而是可以插队。

优先级、关键码(Key)、全序关系与优先队列

将关键码定义为任意对象,必须建立一种统一的、相容的形式,以支持不同对象之间的比较,确定优先级。 实际上,作为优先队列的一个基本要求,在关键码之间必须能够定义某种全序关系(Total order relation)。 具体来说,任何两个关键码都必须能够比较“大小”。

举个例子:如果将这种全序关系用“≤”表示,则该关系还必须满足以下三条性质:

  • 自反性:对于任一关键码 k,都有 k ≤ k
  • 反对称性:若k1 ≤ k2且k2 ≤ k1,则k1 = k2
  • 传递性:若k1 ≤ k2且k2 ≤ k3,则k1 ≤ k3

所谓的优先队列也是对象的一种容器,只不过其中的每个对象都拥有一个关键码,在它们的关 键码之间存在满足上述性质的某种全序关系“≤”。关键码可以是在对象插入优先队列时被人为赋予 的,也可能就是对象本身具有的某一属性。

  • 关键码:对象

  • 全序关系:if(){}else(){}

关键码通过全序关系确认优先级排成优先队列。

条目(Entry)

所谓一个条目(Entry),就是由一个对象及 其关键码合成的一个对象,它反映和记录了二者之间的关联关系。

这样,通过将条目对象作为优先队列的元素,即可记录和维护原先对象与其关键码之间的关联关系。

/**
 * <b>Description:</b> 引入条目这一概念,旨在解决上面的前一个问题。
 * 所谓一个条目(Entry),就是由一个对象及其关键码合成的一个对象,它反映和记录了二者之间的关联关系。
 * 这样,通过将条目对象作为优先队列的元素,即可记录和维护原先对象与其关键码之间的关联关系。
 * @author tongson
 */
public interface Entry {
    /**
     * 取条目的关键码
     *
     * @return
     */
    public Object getKey();

    /**
     * 修改条目的关键码,返回此前存放的关键码
     *
     * @param k
     * @return
     */
    public Object setKey(Object k);

    /**
     * 取条目的数据对象
     *
     * @return
     */
    public Object getValue();

    /**
     * 修改条目的数据对象,返回此前存放的数据对象
     *
     * @param v
     * @return
     */
    public Object setValue(Object v);
} 
复制代码
public class EntryDefault implements Entry {
    protected Object key;
    protected Object value;

    /**************************** 构造函数 ****************************/
    public EntryDefault(Object k, Object v) {
        key = k;
        value = v;
    }

    /**************************** Entry接口方法 ****************************/
    /**
     * 取条目的关键码
     *
     * @return
     */
    @Override
    public Object getKey() {
        return key;
    }

    /**
     * 修改条目的关键码,返回此前存放的关键码
     *
     * @param k
     * @return
     */
    @Override
    public Object setKey(Object k) {
        Object oldK = key;
        key = k;
        return oldK;
    }

    /**
     * 取条目的数据对象
     *
     * @return
     */
    @Override
    public Object getValue() {
        return value;
    }

    /**
     * 修改条目的数据对象,返回此前存放的数据对象
     *
     * @param v
     * @return
     */
    @Override
    public Object setValue(Object v) {
        Object oldV = value;
        value = v;
        return oldV;
    }
} 
复制代码

比较器

基于某种 Comparable 接口实现一个关键码类,并将所有通常的比较方法封装起来,以支持关键码之间的比较。

/**
 * 基于某种 Comparable 接口实现一个关键码类,并将所有通常的比较方法封装起来,以支持关键码之间的比较。
 */
public interface Comparator {
    /**
     * 若a>(=或<)b,返回正数、零或负数
     *
     * @param a
     * @param b
     * @return
     */
    public int compare(Object a, Object b);
}

复制代码
/**
 * Comparable对象的默认比较器
 */
public class ComparatorDefault implements Comparator {
    public ComparatorDefault() {
    }

    @Override
    public int compare(Object a, Object b) throws ClassCastException {
        return ((Comparable) a).compareTo(b);
    }
}

复制代码

优先队列(interface)

public interface PQueue {
    /**统计优先队列的规模
     * 
     * @return
     */
    public int getSize();

    /**判断优先队列是否为空
     * 
     * @return
     */
    public boolean isEmpty();

    /**若Q非空,则返回其中的最小条目(并不删除);否则,报错
     * 
     * @return
     * @throws ExceptionPQueueEmpty
     */
    public Entry getMin() throws ExceptionPQueueEmpty;

    /**将对象obj与关键码k合成一个条目,将其插入Q中,并返回该条目
     * 
     * @param key
     * @param obj
     * @return
     * @throws ExceptionKeyInvalid
     */
    public Entry insert(Object key, Object obj) throws ExceptionKeyInvalid;

    /**若Q非空,则从其中摘除关键码最小的条目,并返回该条目;否则,报错
     * 
     * @return
     * @throws ExceptionPQueueEmpty
     */
    public Entry delMin() throws ExceptionPQueueEmpty;
} 
复制代码

排序器(interface)

/**
 * <b>Description:</b> 排序器接口 <br>
 */
public interface Sorter {
    void sort(Sequence S);
}
复制代码

基于优先队列的排序器

/**
 * <b>Description:</b> 基于优先队列的排序器 <br>
 */
public class SorterPQueue implements Sorter {
    private Comparator C;

    public SorterPQueue() {
        this(new ComparatorDefault());
    }

    public SorterPQueue(Comparator comp) {
        C = comp;
    }

    @Override
    public void sort(Sequence S) {
        //为批处理建立优先队列而准备的序列
        Sequence T = new SequenceDLNode();
        //构建序列T
        while (!S.isEmpty()) {
            //逐一取出S中各元素
            Object e = S.removeFirst();
            //用节点元素本身作为关键码
            T.insertLast(new EntryDefault(e, e));
        }
        // PQueue pq = new PQueueUnsortedList(C, T);
        // PQueue pq = new PQueueSortedList(C, T);
        PQueue pq = new PQueueHeap(C, T);

        //从优先队列中不断地
        while (!pq.isEmpty()) {
            //取出最小元素,插至序列末尾
            S.insertLast(pq.delMin().getValue());
            System.out.println("\t:\t" + S.last().getElem());
        }
    }
}
复制代码

用向量实现优先队列

用列表实现优先队列

基于无序列表的实现及分析

/**
 * <b>Description:</b> 基于无序列表的实现及分析 <br>
 */
public class PQueueUnsortedList implements PQueue {
    private List L;
    private Comparator C;

    /**
     * 构造方法(使用默认比较器)
     */
    public PQueueUnsortedList() {
        this(new ComparatorDefault(), null);
    }

    /**
     * 构造方法(使用指定比较器)
     *
     * @param c
     */
    public PQueueUnsortedList(Comparator c) {
        this(c, null);
    }

    /**
     * 构造方法(使用指定初始元素)
     *
     * @param s
     */
    public PQueueUnsortedList(Sequence s) {
        this(new ComparatorDefault(), s);
    }

    /**
     * 构造方法(使用指定比较器和初始元素)
     *
     * @param c
     * @param s
     */
    public PQueueUnsortedList(Comparator c, Sequence s) {
        L = new ListDLNode();
        C = c;
        if (null != s) {
            while (!s.isEmpty()) {
                Entry e = (Entry) s.removeFirst();
                insert(e.getKey(), e.getValue());
            }
        }
    }

    /**
     * 统计优先队列的规模
     *
     * @return
     */
    @Override
    public int getSize() {
        return L.getSize();
    }

    /**
     * 判断优先队列是否为空
     *
     * @return
     */
    @Override
    public boolean isEmpty() {
        return L.isEmpty();
    }

    /**
     * 若Q非空,则返回其中的最小条目(并不删除);否则,报错
     *
     * @return
     * @throws ExceptionPQueueEmpty
     */
    @Override
    public Entry getMin() throws ExceptionPQueueEmpty {
        if (L.isEmpty()) {
            throw new ExceptionPQueueEmpty("意外:优先队列空");
        }
        Position minPos = L.first();
        Position curPos = L.getNext(minPos);
        //依次检查所有位置,找出最小条目
        while (null != curPos) {
            if (0 < C.compare(minPos.getElem(), curPos.getElem())) {
                minPos = curPos;
            }
        }
        return (Entry) minPos.getElem();
    }

    /**
     * 将对象obj与关键码k合成一个条目,将其插入Q中,并返回该条目
     *
     * @param key
     * @param obj
     * @return
     * @throws ExceptionKeyInvalid
     */
    @Override
    public Entry insert(Object key, Object obj) throws ExceptionKeyInvalid {
        //创建一个新条目
        Entry entry = new EntryDefault(key, obj);
        //接至列表末尾
        L.insertLast(entry);
        return (entry);
    }

    /**
     * 若Q非空,则从其中摘除关键码最小的条目,并返回该条目;否则,报错
     *
     * @return
     * @throws ExceptionPQueueEmpty
     */
    @Override
    public Entry delMin() throws ExceptionPQueueEmpty {
        if (L.isEmpty()) {
            throw new ExceptionPQueueEmpty("意外:优先队列空");
        }
        Position minPos = L.first();
        Iterator it = L.positions();
        //依次检查所有位置,找出最小条目
        while (it.hasNext()) {
            Position curPos = (Position) (it.getNext());
            // System.out.println("\t" + ((Entry)(curPos.getElem())).getKey());
            if (0 < C.compare(((Entry) (minPos.getElem())).getKey(), ((Entry) (curPos.getElem())).getKey())) {
                minPos = curPos;
            }
        }
        return (Entry) L.remove(minPos);
    }
}
复制代码

基于有序列表的实现及分析

/**
 * <b>Description:</b> 基于有序列表的实现及分析 <br>
 */
public class PQueueSortedList implements PQueue {
    private List L;
    private Comparator C;

    /**
     * 构造方法(使用默认比较器)
     */
    public PQueueSortedList() {
        this(new ComparatorDefault(), null);
    }

    /**
     * 构造方法(使用指定比较器)
     *
     * @param c
     */
    public PQueueSortedList(Comparator c) {
        this(c, null);
    }

    /**
     * 构造方法(使用指定初始元素)
     *
     * @param s
     */
    public PQueueSortedList(Sequence s) {
        this(new ComparatorDefault(), s);
    }

    /**
     * 构造方法(使用指定比较器和初始元素)
     *
     * @param c
     * @param s
     */
    public PQueueSortedList(Comparator c, Sequence s) {
        L = new ListDLNode();
        C = c;
        if (null != s)
            while (!s.isEmpty()) {
                Entry e = (Entry) s.removeFirst();
                insert(e.getKey(), e.getValue());
            }
    }

    /**
     * 统计优先队列的规模
     *
     * @return
     */
    @Override
    public int getSize() {
        return L.getSize();
    }

    /**
     * 判断优先队列是否为空
     *
     * @return
     */
    @Override
    public boolean isEmpty() {
        return L.isEmpty();
    }

    /**
     * 若Q非空,则返回其中的最小条目(并不删除);否则,报错
     *
     * @return
     * @throws ExceptionPQueueEmpty
     */
    @Override
    public Entry getMin() throws ExceptionPQueueEmpty {
        if (L.isEmpty()) {
            throw new ExceptionPQueueEmpty("意外:优先队列空");
        }
        return (Entry) L.last();
    }

    /**
     * 将对象obj与关键码k合成一个条目,将其插入Q中,并返回该条目
     *
     * @param key
     * @param obj
     * @return
     * @throws ExceptionKeyInvalid
     */
    @Override
    public Entry insert(Object key, Object obj) throws ExceptionKeyInvalid {
        //创建一个新条目
        Entry entry = new EntryDefault(key, obj);

        //若优先队列为空 //或新条目是当前最大者
        if (L.isEmpty() || (0 > C.compare(((Entry) (L.first().getElem())).getKey(), entry.getKey()))) {
            //则直接插入至表头
            L.insertFirst(entry);
        } else {//否则
            //从尾条目开始
            Position curPos = L.last();
            while (0 > C.compare(((Entry) (curPos.getElem())).getKey(), entry.getKey())) {
                //不断前移,直到第一个不小于entry的条目
                curPos = L.getPrev(curPos);
            }
            //紧接该条目之后插入entry
            L.insertAfter(curPos, entry);
        }
        return (entry);
    }

    /**
     * 若Q非空,则从其中摘除关键码最小的条目,并返回该条目;否则,报错
     *
     * @return
     * @throws ExceptionPQueueEmpty
     */
    @Override
    public Entry delMin() throws ExceptionPQueueEmpty {
        if (L.isEmpty()) {
            throw new ExceptionPQueueEmpty("意外:优先队列空");
        }
        return (Entry) L.remove(L.last());
    }
}
复制代码

选择排序与插入排序

选择排序

插入排序

效率比较

用堆实现优先队列

堆的定义及性质
堆结构
完全性
用堆实现优先队列
/**
 * <b>Description:</b> 基于堆的优先队列及其实现 <br>
 */
public class PQueueHeap implements PQueue {
    /**
     * 完全二叉树形式的堆
     */
    private ComplBinTree H;
    /**
     * 比较器
     */
    private Comparator comp;

    /**
     * 构造方法
     */
    public PQueueHeap() {
        this(new ComparatorDefault(), null);
    }

    /**
     * 构造方法:默认的空优先队列
     *
     * @param c
     */
    public PQueueHeap(Comparator c) {
        this(c, null);
    }

    /**
     * 构造方法:根据某一序列直接批量式构造堆算法,S中元素都是形如(key, value)的条目
     *
     * @param S
     */
    public PQueueHeap(Sequence S) {
        this(new ComparatorDefault(), S);
    }

    /**
     * 构造方法:根据某一序列直接批量式构造堆算法,s中元素都是形如(key, value)的条目
     *
     * @param c
     * @param s
     */
    public PQueueHeap(Comparator c, Sequence s) {
        comp = c;
        H = new ComplBinTreeVector(s);
        if (!H.isEmpty()) {
            //自底而上
            for (int i = H.getSize() / 2 - 1; i >= 0; i--) {
                //逐节点进行下滤
                percolateDown(H.posOfNode(i));
            }
        }
    }

    /*-------- PQueue接口中定义的方法 --------*/

    /**
     * 统计优先队列的规模
     *
     * @return
     */
    @Override
    public int getSize() {
        return H.getSize();
    }

    /**
     * 判断优先队列是否为空
     *
     * @return
     */
    @Override
    public boolean isEmpty() {
        return H.isEmpty();
    }

    /**
     * 若Q非空,则返回其中的最小条目(并不删除);否则,报错
     *
     * @return
     * @throws ExceptionPQueueEmpty
     */
    @Override
    public Entry getMin() throws ExceptionPQueueEmpty {
        if (isEmpty()) {
            throw new ExceptionPQueueEmpty("意外:优先队列为空");
        }
        return (Entry) H.getRoot().getElem();
    }

    /**
     * 将对象obj与关键码k合成一个条目,将其插入Q中,并返回该条目
     *
     * @param key
     * @param obj
     * @return
     * @throws ExceptionKeyInvalid
     */
    @Override
    public Entry insert(Object key, Object obj) throws ExceptionKeyInvalid {
        checkKey(key);
        Entry entry = new EntryDefault(key, obj);
        percolateUp(H.addLast(entry));
        return entry;
    }

    /**
     * 若Q非空,则从其中摘除关键码最小的条目,并返回该条目;否则,报错
     *
     * @return
     * @throws ExceptionPQueueEmpty
     */
    @Override
    public Entry delMin() throws ExceptionPQueueEmpty {
        if (isEmpty()) {
            throw new ExceptionPQueueEmpty("意外:优先队列为空");
        }
        //保留堆顶
        Entry min = (Entry) H.getRoot().getElem();
        //若只剩下最后一个条目
        if (1 == getSize()) {
            //直接摘除之
            H.delLast();
        } else {//否则
            H.getRoot().setElem(((ComplBinTreeNodeRank) H.delLast()).getElem());
            //取出最后一个条目,植入堆顶
            percolateDown(H.getRoot());
        }
        //返回原堆顶
        return min;
    }

    /*-------- 辅助方法 --------*/

    /**
     * 检查关键码的可比较性
     *
     * @param key
     * @throws ExceptionKeyInvalid
     */
    protected void checkKey(Object key) throws ExceptionKeyInvalid {
        try {
            comp.compare(key, key);
        } catch (Exception e) {
            throw new ExceptionKeyInvalid("无法比较关键码");
        }
    }

    /**
     * 返回节点v(中所存条目)的关键码
     *
     * @param v
     * @return
     */
    protected Object key(BinTreePosition v) {
        return ((Entry) (v.getElem())).getKey();
    }

    /*-------- 算法方法 --------*/

    /**
     * 交换父子节点(中所存放的内容)
     *
     * @param u
     * @param v
     */
    protected void swapParentChild(BinTreePosition u, BinTreePosition v) {
        Object temp = u.getElem();
        u.setElem(v.getElem());
        v.setElem(temp);
    }

    /**
     * 上滤算法
     *
     * @param v
     */
    protected void percolateUp(BinTreePosition v) {
        //记录根节点
        BinTreePosition root = H.getRoot();
        //不断地
        while (v != H.getRoot()) {
            //取当前节点的父亲
            BinTreePosition p = v.getParent();
            if (0 >= comp.compare(key(p), key(v))) {
                //除非父亲比孩子小
                break;
            }
            //否则,交换父子次序
            swapParentChild(p, v);
            //继续考察新的父节点(即原先的孩子)
            v = p;
        }
    }

    /**
     * 下滤算法
     *
     * @param v
     */
    protected void percolateDown(BinTreePosition v) {
        //直到v成为叶子
        while (v.hasLChild()) {
            //首先假设左孩子的(关键码)更小
            BinTreePosition smallerChild = v.getLChild();
            if (v.hasRChild() && 0 < comp.compare(key(v.getLChild()), key(v.getRChild()))) {
                //若右孩子存在且更小,则将右孩子作为进一步比较的对象
                smallerChild = v.getRChild();
            }
            if (0 <= comp.compare(key(smallerChild), key(v))) {
                //若两个孩子都不比v更小,则下滤完成
                break;
            }

            //否则,将其与更小的孩子交换
            swapParentChild(v, smallerChild);
            //并继续考察这个孩子
            v = smallerChild;
        }
    }
}
复制代码
基于堆的优先队列及其实现
堆排序
直接堆排序
就地堆排序

Huffman 树

冒泡排序

/**
 * <b>Description:</b> 起泡排序算法 <br>
 */
public class SorterBubblesort implements Sorter {
    private Comparator C;

    public SorterBubblesort() {
        this(new ComparatorDefault());
    }

    public SorterBubblesort(Comparator comp) {
        C = comp;
    }

    @Override
    public void sort(Sequence S) {
        int n = S.getSize();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n - i - 1; j++) {
                if (0 < C.compare(S.getAtRank(j), S.getAtRank(j + 1))) {
                    Object temp = S.getAtRank(j);
                    S.replaceAtRank(j, S.getAtRank(j + 1));
                    S.replaceAtRank(j + 1, temp);
                }
            }
        }
    }
}
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值