这是一种很优美也很简洁的算法。
还原:p->next->prev = p->prev->next = p;
实现非常的容易,其容易程度类似于树状数组。
利用在深度优先搜索的栈中所保存的指针,我们可以一句话O(1)的删除链表中的一个元素和还原链表中的一个元素。
删除:p->next->prev = p->prev; p->prev->next = p->next;还原:p->next->prev = p->prev->next = p;
这样我们就可以在搜索时将 for 的复杂度降低至少一倍,我个人认为是把指数的复杂度降低到了阶乘。
下面看一个简单例子:生成全排列。最快能想到的就是一个回溯的算法:每次 for 一遍,用一个标记数组记录元素是否有被选入排列中去,到最后一层时便是一个排列。
很容易写出代码:
void dfs(int dep, int u)
{
if (dep > maxdep)
count();
else
for (int i = 1; i <= n; ++ i)
{
if (vst[i]) continue;
vst[i] = 1;
dfs(dep + 1, i);
vst[i] = 0;
}
}
但是我们会发现,每一层都会 for 上一个 n, 无论你有没有做。等于说,复杂度相当于指数。
实际中,根据我写搜索题卡时的经验,这种无用功所消耗的时间是巨大的。
有没有办法可以优化呢?当然有,就是用 dancing links。
将代码改写成这样:void dfs(int dep, int u)
{
if (dep > maxdep)
count();
else
for (node * p = head->next; p; p = p->next)
{
p->next->prev = p->prev;
p->prev->next = p->next;
dfs(dep + 1, p->map);
p->next->prev = p->prev->next = p;
}
}
很容易看出,每次链表的长度都会缩减,回溯后将自动恢复。
等于说是,每递归深一层,链表的长度都会减少,复杂度为阶乘。
这将在搜索题中非常的实用,所带来的就不仅仅是常数优化了。