堆(不是jvm中的对内存)是优先级队列的一种数据结构。它是一种树,插入和删除的时间复杂度都是O(logN)。

堆(降序的优先级队列)有以下特点:

  • 它是完全二叉树。除了树最后一层节点不需要是满的,其它每一层从左到右都是满的
  • 通常用数组实现
  • 堆中每一个节点都满足堆的条件,也就是说每一个节点的关键字都大于(或等于)这个节点的子节点的关键字。
    在这里插入图片描述

堆是弱序的,只要求父节点比子节点大就行,左右子节点是无序的。
所以不支持顺序遍历,也不支持便利的查找指定关键字。但是可以快速移除最大节点以及插入新节点的操作很快,所以适合优先级队列。

插入使用向上筛选,删除使用向下筛选。

插入时先把节点插入到最后一个位置,然后向上线性遍历,找个比父节点小的位置躺下就行了。

删除则是去除根节点,然后把最后一个节点移动到根节点了,再从根节点开始遍历,找到一个子节点(2个子节点中较大的一个子节点)比他小的位置躺好就行了。


如果是用数组实现的,数组索引有以下特点:

  • 它的父节点下标为(x-1)/2
  • 左子节点的下标是2*x+1
  • 右子节点下标是2*x+2

插入:

public boolean insert(int key){
	if(currentSize==maxSize)
		return false;
	Node newNode=new Node(key);
	heapArray[currentSize]=newNode;
	trickleUp(currentSize++);
	return true;
}
public void trickleUp(int index){
	int parent=(index-1)/2;
	Node bottom=heapArray[index];
	
	while(index>0 && heapArray[parent].key<bottom.key){
		heapArray[index]=heapArray[parent];
		index=parent;
		parent=(parent-1)/2;
	}
	heapArray[index]=bottom;
}

移除:

public Node remove(){
	if(currentSize==0)
		return null;
	Node root=heapArray[0];
	heapArray[0]=heapArray[--currentSize];
	trickleDown(0);
	return root;
}
public void trickleDown(int index){
	int largerChild;
	Node top=heapArray[index];
	whie(index<currentSize/2){
		int leftChild=2*index+1;
		int rightChild=leftChild+1;

		if(rightChild<currentSize &&  heapArray[leftChild].key<heapArray[rightChild].key){//存在右孩子,而且右孩子大,所以要走右边路线
			largerChild=rightChild;
		}else{
			lagerChild=rightChild;
		}

		if(top.key >=heapArray[largerChild].key){
			break;//找好位置了,可以躺好了
		}
		heapArray[index]=heapArray[largerChild];
		index=largerChild;
	}
	heapArray[index]=top;
}

使用二叉树实现堆,这种树叫树堆。

使用树实现堆难点是难以找到最后一个节点,虽然可以用一个标记记录,但是插入时,找到插入位置也不容易。

算法实现:

下面的方法是用来计算新插入元素的位置的。
count是当前堆中已经有的元素个数。

比如有7个元素,7的二进制是 111;
去除第一位后是 11,所以第7个元素的位置是 从根节点,向右,再向右。

public static byte[] getByteCode(int count){
        byte[] bytes=new byte[32];
        int i=0;
        for (;count>1;i++){
            bytes[i]= (byte) (count % 2);
            count=count>>1;
        }
        byte[] result=new byte[i];
        int size=i;
        for (;i>0;){
            result[size-i]=bytes[--i] ;
        }
        return result;
    }

插入操作:

public void insert(int key,Object value){
        Node node=new Node(key,value);
        if (root==null){//堆是空的,直接放进去就行了
            root=node;
            count++;
        }else {
            byte[] byteCode=getByteCode(++count);
            int len=byteCode.length;
            Node parent=root;
            for (int i=0;i<len;i++){
                if (i==len-1){
                    if (byteCode[i]==0){
                        parent.leftChild=node;
                    }else {
                        parent.rightChild=node;
                    }
                    node.parent=parent;
                    break;
                }
                if (byteCode[i]==0){
                    parent=parent.leftChild;
                }else {
                    parent=parent.rightChild;
                }
            }
            trickleUp(node);
        }
    }

    /**
     * 向上遍历,直到找到合适的位置(类冒泡插入排序)
     * @param node
     */
    private void trickleUp(Node node) {
        int key=node.key;
        Object value=node.value;//保存Node的值
        while (node.parent!=null && node.parent.key<key){
            node.key=node.parent.key;
            node.value=node.parent.value;//复制父节点的值到子节点
            node=node.parent;
        }
        node.key=key;
        node.value=value;
    }

删除操作

 public Node remove(){
        if (root==null){
            return null;
        }else {
            byte[] byteCode=getByteCode(count--);
            int len=byteCode.length;
            Node last=root;
            for (int i=0;i<len;i++){
                if (byteCode[i]==0){
                    last=last.leftChild;
                }else {
                    last=last.rightChild;
                }
            }
            //把last的值与根节点交换
            int key=root.key;
            Object value=root.value;
            
            root.key=last.key;
            root.value=last.value;
            
            last.key=key;
            last.value=value;

  	 //移除last节点
            if (byteCode[len-1]==0){
                last.parent.leftChild=null;
            }else {
                last.parent.rightChild=null;
            }
            last.parent=null;
            //向下遍历,重排
            trickleDown(root);
            
          
            return last;
        }
    }

 /**
     * 向下遍历,直到找到合适的位置(类冒泡插入排序)
     * @param node
     */
    private void trickleDown(Node node) {
        int key=node.key;
        Object value=node.value;//保存Node的值
	Node largeChild=getLargeChild(node);
         while (largeChild!=nulll && largeChild.key>key){
        	node.key=largeChild.key;
        	node.value=largeChild.value;

		node=largeChild;
		largeChild=getLargeChild(node);
        }
        node.key=key;
        node.value=value;
    }

Node节点

public class Node {
    int key;
    Object value;

    Node parent;
    Node leftChild;
    Node rightChild;


    public Node(int key, Object value) {
        this.key=key;
        this.value=value;
    }
}

优先级小的队列:

public class PriorityQ {

    Node root;
    int count=0;
    public void insert(Edge edge){
        Node node=new Node(edge);
        if (root==null){//堆是空的,直接放进去就行了
            root=node;
            count++;
        }else {
            byte[] byteCode=getByteCode(++count);
            int len=byteCode.length;
            Node parent=root;
            for (int i=0;i<len;i++){
                if (i==len-1){
                    if (byteCode[i]==0){
                        parent.leftChild=node;
                    }else {
                        parent.rightChild=node;
                    }
                    node.parent=parent;
                    break;
                }
                if (byteCode[i]==0){
                    parent=parent.leftChild;
                }else {
                    parent=parent.rightChild;
                }
            }
            trickleUp(node);
        }
    }
    public Node remove(){
        if (root==null){
            return null;
        }else {
            byte[] byteCode=getByteCode(count--);
            int len=byteCode.length;
            Node last=root;
            for (int i=0;i<len;i++){
                if (byteCode[i]==0){
                    last=last.leftChild;
                }else {
                    last=last.rightChild;
                }
            }
            //把last的值与根节点交换
            int key=root.key;
            Edge value=root.value;

            root.key=last.key;
            root.value=last.value;

            last.key=key;
            last.value=value;

            //移除last节点
            if (byteCode[len-1]==0){
                last.parent.leftChild=null;
            }else {
                last.parent.rightChild=null;
            }
            last.parent=null;
            //向下遍历,重排
            trickleDown(root);


            return last;
        }
    }
    private void trickleDown(Node node) {
        int key=node.key;
        Edge value=node.value;//保存Node的值
        Node smallChild=getSmallChild(node);
        while (smallChild!=null && smallChild.key<key){
            node.key=smallChild.key;
            node.value=smallChild.value;

            node=smallChild;
            smallChild=getSmallChild(node);
        }
        node.key=key;
        node.value=value;
    }

    private Node getSmallChild(Node node) {
        Node left=node.leftChild;
        Node right=node.rightChild;
        if (right!=null && left.key>right.key){
            return right;
        }
        return left;
    }

    private void trickleUp(Node node) {
        int key=node.key;
        Edge value=node.value;//保存Node的值
        while (node.parent!=null && node.parent.key>key){
            node.key=node.parent.key;
            node.value=node.parent.value;//复制父节点的值到子节点
            node=node.parent;
        }
        node.key=key;
        node.value=value;
    }
    public static byte[] getByteCode(int count){
        byte[] bytes=new byte[32];
        int i=0;
        for (;count>1;i++){
            bytes[i]= (byte) (count % 2);
            count=count>>1;
        }
        byte[] result=new byte[i];
        int size=i;
        for (;i>0;){
            result[size-i]=bytes[--i] ;
        }
        return result;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值