利用双向队列构造链接式优先队列

本文介绍基于二叉堆的优先队列实现方法,包括插入、删除等基本操作及维护队列有序性的上游和下沉操作。
  • 优先队列的定义

优先队列中的元素被赋予优先级。插入元素时保持优先队列部分有序,删除元素时具有最高优先级的元素最先删除。优先队列分为面向最高优先级的优先队列和面向最低优先级的优先队列。

  • 数据结构二叉堆

当一棵二叉树的每个结点都大于等于它的两个子结点时,这个二叉树称为堆有序,也被称作二叉堆。一般二叉堆都是一棵完全二叉树,如图所示:
一棵用完全二叉树表示的二叉堆

  • 基于二叉堆的优先队列

二叉堆可以很好的实现优先队列的基本操作:插入元素和删除优先级最高或最低的元素。二叉堆可以直接用数组实现,这里只讨论链接形式。首先需要保证二叉堆是一个完全二叉树,一棵完全二叉树可以通过队列来构造,可以参考我的另一篇博客利用队列构造链接式完全二叉树
而基于二叉堆的优先队列有插入和删除操作,因而要使用双向队列deque来实现:
新节点插入上层节点(deque队头)的左孩子或右孩子,同时用addLast将新节点加入deque队尾,上层节点的左右孩子插满后用removeFirst删除deque队头的上层节点,这里的规律是deque队尾始终是完全二叉树的末尾节点,而其队头始终是下一次插入节点的父节点
删除节点则相反,用removeLast删除deque队尾节点(完全二叉树的末尾节点),同时用addFirst将该节点的父节点插入deque队头(下一次插入节点的父节点),这么做仍然保持了上述规律。
优先队列中还有非常重要的两个操作:插入节点上游(swim)和删除节点下沉(sink),这两个操作可以保持优先队列二叉堆部分有序的特性。

  • 链接式优先队列的实现(面向最大元素)
public class ListMaxPQ<Key extends Comparable<Key>> {
    private class Node {
        Key item;
        Node parent;
        Node lchild, rchild;
    }
    private Node root;// 二叉堆根节点
    private Deque<Node> deque = new Deque<>();// 双向队列
    private int currentLength;

    public ListMaxPQ() {
        root = new Node();
        deque.addLast(root);// 首先让根节点入队
        currentLength = 0;
    }

    public void insert(Key v) {
        if (isEmpty()) {
            root.item = v;
            currentLength++;
            return;
        }
        Node newNode = new Node();// 新建节点
        newNode.item = v;
        while (!deque.isEmpty()) {
            Node t = deque.peek();// 取队头节点但不出队
            if (t.lchild == null) {
                t.lchild = newNode;// 建立双向链接
                newNode.parent = t;
                deque.addLast(t.lchild);// 左孩子插入deque队尾
                currentLength++;
                swim(newNode);// 新节点上游
                return;// 仅插入左孩子的节点不出队,之后插入其右孩子
            }
            if (t.rchild == null) {
                t.rchild = newNode;// 建立双向链接
                newNode.parent = t;
                deque.addLast(t.rchild);// 右孩子插入deque队尾
                deque.removeFirst();// 插入右孩子的节点处理完毕,让其出队
                currentLength++;
                swim(newNode);// 新节点上游
                return;
            }
        }
    }

    public Key delMax() {
        if (currentLength==0) {
            return null;
        }else if (currentLength == 1) {
            currentLength--;
            return root.item;
        }
        Key max = root.item;// 根节点即将删除的最大节点
        Node last = deque.removeLast();// 删除deque队尾(完全二叉树的末尾节点)
        if (last.parent.rchild == last) {// 如果是右孩子则将父节点恢复到deque中
            deque.addFirst(last.parent);
        }
        exch(last, root);// 交换末尾节点和根节点
        if (last.parent.lchild == last) {// 以下操作是删除末尾节点的任何链接,作为垃圾被回收
            last.parent.lchild = null;
        } else {
            last.parent.rchild = null;
        }
        last.parent = null;
        last = null;
        currentLength--;
        sink(root);// 根节点下沉
        return max;
    }

    private void swim(Node node) {
        while (node.parent != null && less(node.parent, node)) {
            exch(node.parent, node);
            node = node.parent;
        }
    }

    private void sink(Node node) {
        while (node.lchild != null) {
            Node t = node.lchild;
            if (node.rchild != null && less(t, node.rchild)) {
                t = node.rchild;
            }
            if (!less(node, t)) {
                break;
            }
            exch(node, t);
            node = t;
        }
    }

    private boolean less(Node v, Node w) {
        return v.item.compareTo(w.item) < 0;
    }

    private void exch(Node a, Node b) {
        Key t = a.item;
        a.item = b.item;
        b.item = t;
    }

    public boolean isEmpty() {
        return currentLength == 0;
    }

    public int size() {
        return currentLength;
    }

    public void show() {// 按照广度优先的顺序遍历
        ListQueue<Node> itQueue = new ListQueue<>();// 层次信息的队列
        itQueue.enqueue(root);
        while (!itQueue.isEmpty()) {
            Node t = itQueue.dequeue();// 根节点出队
            System.out.print(t.item + " ");
            // 分别将左右孩子入队,成为下一层节点
            if (t.lchild != null) {
                itQueue.enqueue(t.lchild);
            }
            if (t.rchild != null) {
                itQueue.enqueue(t.rchild);
            }
        }
        System.out.println();
    }

    public static void main(String[] args) {
        ListMaxPQ<Integer> pq = new ListMaxPQ<>();
        for (int i = 0; i < 20; i++) {
            pq.insert(i);
        }
        pq.show();
        while (!pq.isEmpty()) {
            System.out.print(pq.delMax() + " ");
        }
    }
}

最后展示的是一个简单的双向队列deque的实现:

public class Deque<T> { 
    private class DeNode{
        T item;
        DeNode front;
        DeNode next;
    }   
    private DeNode first;
    private DeNode last;
    private int currentLength;

    public void addFirst(T item) {
        DeNode newNode=new DeNode();
        newNode.item=item;
        newNode.next=first;
        if (isEmpty()) {
            first=newNode;
            last=first;
        }else {
            first.front=newNode;
            first=newNode;
        }
    }

    public void addLast(T item) {
        DeNode newNode=new DeNode();
        newNode.item=item;
        newNode.front=last;
        if (isEmpty()) {
            last=newNode;
            first=last;
        }else {
            last.next=newNode;
            last=newNode;
        }
    }

    public T removeFirst() {
        if (isEmpty()) {
            throw new NullPointerException();
        }
        T item=first.item;
        first.next.front=null;
        first=first.next;
        return item;
    }

    public T removeLast() {
        if (isEmpty()) {
            throw new NullPointerException();
        }
        T item=last.item;
        last.front.next=null;
        last=last.front;
        return item;
    }

    public T peek() {
        DeNode f=first;
        return (f==null)?null:f.item;
    }

    public boolean isEmpty() {
        return first==null&&last==null;
    }

    public int size() {
        return currentLength;
    }
}
源码,经典。 CARD *myinsert(LCARD *head, LCARD *insert) { LCARD *temp = NULL; if (head==NULL)//链表为空 { head = insert; insert->next = insert; insert->prior = insert; } else//链表非空 { temp = head; if (head->cardnum>insert->cardnum)//插入到头前边,并且把自己当作头 { head->prior->next = insert; insert->prior = head->prior; insert->next = head; head->prior = insert; head = insert; } if (insert->cardnum0<50)//小于50正向插入 { while ((temp->cardnum<insert->cardnum)&&(temp->next!=head))//循环 { temp = temp->next; } if (temp->cardnum>insert->cardnum)//第一个条件终止的 { temp->prior->next = insert; insert->prior = temp->prior; insert->next = temp; temp->prior = insert; } else//第二个条件终止的 { head->prior->next = insert; insert->prior = head->prior; insert->next = head; head->prior = insert; } } else//大于50反向插入 { while ((temp->cardnum>insert->cardnum)&&(temp->prior!=head))//循环,第二个条件禁止跑飞 { temp = temp->prior; } if (temp->cardnum<insert->cardnum)//只有第一个条件可以终止的 { temp->next->prior = insert; insert->next = temp->next; insert->prior = temp; temp->next = insert; } } } //printf("%d\t%d\n", insert->id, insert->cardnum); return head; } void swap_id(SWID *sw) { LCARD *temp = sw->head; if (sw->head->cardnum==sw->swapcardnum) { printf("out person cardnum=%d\n", sw->head->id); sw->head->id = sw->inID; return ; } if ((sw->swapcardnum0)<50) { while ((temp->cardnum!=sw->swapcardnum)&&(temp->next!=sw->head)) { temp = temp->next; } if (temp->cardnum==sw->swapcardnum) { printf("out person cardnum=%d\n", sw->head->id); temp->id = sw->inID; } } else { while ((temp->cardnum!=sw->swapcardnum)&&(temp->prior!=sw->head)) { temp = temp->prior; } if (temp->cardnum==sw->swapcardnum) { printf("out person cardnum=%d\n", sw->head->id); temp->id = sw->inID; } } } LCARD *mydel(LCARD *head, LCARD *del) { LCARD *temp = NULL; if (head==NULL)//没有链表 { printf("there is no card\n"); } else//有链表 { if(head->next==head)//链表里就有一个节点并且为头结点 { if (head->cardnum==del->cardnum) { free(head); head = NULL; } else { printf("in mydel error\n"); } } else//链表里有超过一个的节点 { temp = head; if (del->cardnum0<50)//成立则正向删除 { while ((temp->cardnum!=del->cardnum)&&(temp->next!=head)) { temp = temp->next; } if (temp->cardnum==del->cardnum) { temp->prior->next = temp->next; temp->next->prior = temp->prior; free(temp); } } else//反向删除 { while ((temp->cardnum!=del->cardnum)&&(temp->prior!=head)) { temp = temp->prior; } if (temp->cardnum==del->cardnum) { temp->prior->next = temp->next; temp->next->prior = temp->prior; free(temp); } } } } return head; }
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值