阅读本文大概需要5分钟

说到堆这种数据结构,很多人的第一反应是感觉很复杂,其实不然,堆就是个优先级队列而已,或者,堆其实就是一种树。本文先讲原理,后面给出堆的实现代码。

优先级队列可以用有序数组来实现,这种做法的问题是,尽管删除最大数据项的时间复杂度为O(1),但是插入还是需要较长的O(N)时间,这是因为必须移动数组中平均一半的数据项以插入新数据项,并在完成插入后,数组依然有序。

本文主要介绍实现优先级队列的另一种结构:堆。堆是一种树,并非java和C++等编译语言里的“堆”。由它实现的优先级队列的插入和删除的时间复杂度都是O(logN)。尽管这样删除的时间变慢了一些,但是插入的时间快的多了。当速度非常重要,且有很多插入操作是,可以选择堆来实现优先级队列。堆有如下特点:

  • 它是完全二叉树。即除了树的最后一层节点不需要是满的外,其他的每一层从左到右都完全是满的。
  • 它常常用一个数组实现。用数组实现的完全二叉树中,节点的索引有如下特点(设该节点的索引为x):
  • 它的父节点的索引为 (x-1) / 2; 它的左子节点索引为 2x + 1; 它的右子节点索引为 2x + 2。
  • 堆中每个节点的关键字都大于(或等于)这个节点的子节点的关键字。这也是堆中每个节点必须满足的条件。所以堆和二叉搜索树相比,是弱序的。

向堆中插入数据,首先将数据项存放到叶节点中(即存到数组的最后一项),然后从该节点开始,逐级向上调整,直到满足堆中节点关键字的条件为止。

从堆中删除数据与插入不同,删除时永远删除根节点的数据,因为根节点的数据最大,删除完后,将最后一个叶节点移到根的位置,然后从根开始,逐级向下调整,直到满足堆中节点关键字的条件为止。

原理就这么多,堆真的很简单。

下面给出堆的实现代码:

public
 
class
 
Heap
 
{
    
private
 
Node
[]
 heapArray
;
    
private
 
int
 maxSize
;
    
private
 
int
 currentSize
;
    
public
 
Heap
(
int
 mx
)
 
{
        maxSize 
=
 mx
;
        currentSize 
=
 
0
;
        heapArray 
=
 
new
 
Node
[
maxSize
];
    
}
    
public
 
boolean
 isEmpty
()
 
{
        
return
 
(
currentSize 
==
 
0
)?
 
true
 
:
 
false
;
    
}
    
public
 
boolean
 isFull
()
 
{
        
return
 
(
currentSize 
==
 maxSize
)?
 
true
 
:
 
false
;
    
}
    
public
 
boolean
 insert
(
int
 key
)
 
{
        
if
(
isFull
())
 
{
            
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
];
 
//将新加的尾节点存在bottom中
        
while
(
index 
>
 
0
 
&&
 heapArray
[
parent
].
getKey
()
 
<
 bottom
.
getKey
())
 
{
            heapArray
[
index
]
 
=
 heapArray
[
parent
];
            index 
=
 parent
;
            parent 
=
 
(
parent 
-
 
1
)
 
/
 
2
;
        
}
        heapArray
[
index
]
 
=
 bottom
;
    
}
    
public
 
Node
 remove
()
 
{
        
Node
 root 
=
 heapArray
[
0
];
        heapArray
[
0
]
 
=
 heapArray
[--
currentSize
];
        trickleDown
(
0
);
        
return
 root
;
    
}
    
//向下调整
    
public
 
void
 trickleDown
(
int
 index
)
 
{
        
Node
 top 
=
 heapArray
[
index
];
        
int
 largeChildIndex
;
        
while
(
index 
<
 currentSize
/
2
)
 
{
 
//while node has at least one child
            
int
 leftChildIndex 
=
 
2
 
*
 index 
+
 
1
;
            
int
 rightChildIndex 
=
 leftChildIndex 
+
 
1
;
            
//find larger child
            
if
(
rightChildIndex 
<
 currentSize 
&&
  
//rightChild exists?
                    heapArray
[
leftChildIndex
].
getKey
()
 
<
 heapArray
[
rightChildIndex
].
getKey
())
 
{
                largeChildIndex 
=
 rightChildIndex
;
            
}
            
else
 
{
                largeChildIndex 
=
 leftChildIndex
;
            
}
            
if
(
top
.
getKey
()
 
>=
 heapArray
[
largeChildIndex
].
getKey
())
 
{
                
break
;
            
}
            heapArray
[
index
]
 
=
 heapArray
[
largeChildIndex
];
            index 
=
 largeChildIndex
;
        
}
        heapArray
[
index
]
 
=
 top
;
    
}
    
//根据索引改变堆中某个数据
    
public
 
boolean
 change
(
int
 index
,
 
int
 newValue
)
 
{
        
if
(
index 
<
 
0
 
||
 index 
>=
 currentSize
)
 
{
            
return
 
false
;
        
}
        
int
 oldValue 
=
 heapArray
[
index
].
getKey
();
        heapArray
[
index
].
setKey
(
newValue
);
        
if
(
oldValue 
<
 newValue
)
 
{
            trickleUp
(
index
);
        
}
        
else
 
{
            trickleDown
(
index
);
        
}
        
return
 
true
;
    
}
    
public
 
void
 displayHeap
()
 
{
        
System
.
out
.
println
(
"heapArray(array format): "
);
        
for
(
int
 i 
=
 
0
;
 i 
<
 currentSize
;
 i
++)
 
{
            
if
(
heapArray
[
i
]
 
!=
 
null
)
 
{
                
System
.
out
.
print
(
heapArray
[
i
].
getKey
()
 
+
 
" "
);
            
}
            
else
 
{
                
System
.
out
.
print
(
"--"
);
            
}
        
}
    
}
}
class
 
Node
 
{
    
private
 
int
 iData
;
    
public
 
Node
(
int
 key
)
 
{
        iData 
=
 key
;
    
}
    
public
 
int
 getKey
()
 
{
        
return
 iData
;
    
}
    
public
 
void
 setKey
(
int
 key
)
 
{
        iData 
=
 key
;
    
}
}

这个实现的代码,可以在等公交的时候、吃饭排队的时候拿来看看,利用碎片化时间来学习。

END 这里不仅仅有技术

相关推荐阅读:

如果让你手写个栈和队列,你还会写吗? 开发了那么多项目,你能自己手写个健壮的链表出来吗? 下次面试若再被问到二叉树,希望你能对答如流! 面试还在被红-黑树虐?看完这篇轻松搞定面试官 2-3-4树是如何解决二叉树中非平衡问题的? 读完这篇,希望你能真正理解什么是哈希表 我敢说,这图绝对跟你想象中的不太一样!