zkw神树
zkw神树不多说了,不懂的移步我的博客《zkw线段树》。这里优化dij用,不需要区间查询,只使用点修改一个,编程复杂度极低。
唯一不同的是的为了更快找到最小值的位置(dijkstra中点的编号),需要加一个mark标记,记录最小值逻辑上的位置,或者说图里面点的编号。
奉上一个做好的zkwheap模板,不光在这里能用,是个能修改元素值的堆。
template < typename T > struct ZkwHeapNode {
T value;
size_t mark;
// 编号
ZkwHeapNode() { }
ZkwHeapNode(const T & _value):value(_value) {}
};
template < typename T, typename Comp > class ZkwHeap {
private:
typedef ZkwHeapNode < T > Node;
typedef ZkwHeap < T, Comp > Heap;
Comp cmp;
Node *NodeList;
size_t n;
// 最多元素
int init_value;
// 初始值,大根堆(less<>)就用无穷小,小根堆(greater<>)就用无穷大
void fix(size_t _pos) {
if (cmp(NodeList[_pos << 1].value, NodeList[(_pos << 1) + 1].value))
NodeList[_pos] = NodeList[(_pos << 1) + 1];
else
NodeList[_pos] = NodeList[_pos << 1];
}
public:
ZkwHeap(size_t _MaxN, const T & _init_value):init_value(_init_value) {
n = 1 << (1 + (size_t) (log(_MaxN) / log(2.0)));
NodeList = new Node[n << 1];
for (size_t i = 1; i <= n + n - 1; i++)
NodeList[i].value = init_value;
for (size_t i = n; i <= n + n - 1; i++)
NodeList[i].mark = i - n + 1;
// 分配标记
}
~ZkwHeap() {
delete[]NodeList;
}
T top() {
return NodeList[1].value;
}
// 最大元素
T top_pos() {
return NodeList[1].mark;
}
// 最大元素位置
void modify(size_t _position, const T & _new_value) {
int _pos = _position + n - 1;
NodeList[_pos].value = _new_value;
while (_pos)
fix(_pos >>= 1);
}
T pop() {
T return_value = NodeList[1].value;
modify(NodeList[1].mark, init_value);
return return_value;
// 删除极值
}
};
理解了zkw线段树,也就很容易明白为什么用zkw线段树优化dijkstra了。
堆优化dijkstra分析
dijkstra算法效率是Θ(N^2)。事实上,运用邻接表+堆优化可是将时间复杂度降到O(NlgE)。我们将在最后用聚合分析得出这个界。
为什么要堆优化?因为dijkstra最大的时间复杂度消耗在寻找dis[]中的最小值上。而用小根堆就解决了这个问题。伪代码如下:
function dijkstra(G, s) {
H = a new heap
dis = a new array contain ∞
H中s的值设为0
for (int i=2; i<=G.n; i++) {
k = H中dis最小的点
dis[k] = k.dis
// 记录下来
for each p connected to k {
if (dis[