堆(不是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;
}
}