Skip to content

算法综合

递归思想

递归是一个比用 goto 稍微好点的下下策,不必要时最好不要使用!后者是因为破坏了结构化设计而遭到嫌弃,前者则是因为性能太低而遭到嫌弃。

为了验证递归的性能真的很低,我们可以设计一个实验。通过输出斐波那契数列(指定索引从 1 开始计算,第 1 位值为 1)的前 5 位,计算递归与迭代的性能差距。

迭代(假定参数合法):

C
int getFbncE_IT(int index)
{
    if (index <= 2)
        return 1;
    int a = 1, b = 1;
    for (int i = 3; i <= index; i++)
        if (i % 2)
            a += b;
        else
            b += a;
    return index % 2 ? a : b;
}

递归(假定参数合法):

C
int getFbncE_RE(int index)
{
    if (index <= 2)
        return 1;
    return getFbncE_RE(index - 1) + getFbncE_RE(index - 2);
}

我们一眼就看出来递归算法用的代码量很少,思路也很清晰简洁,但是在应用中,该函数会被调用非常多的次数,有股指数爆炸的味道了:

自变量12345n
函数调用次数11359n+(n1)+(n3)+(n4)+(n6)+(n7)+...

由于递归调用会建立函数的副本,所以在一些循环需求很大的场景下,使用递归会有可能出现硬件级错误。

那么什么时候用递归是比较合适的呢?下面来看一个例子:

C
// 用户输入不定长度的字符串,以 Sharp 符号为截止标识,要求逆序输出字符串。

void showReverse()
{
    char a;
    scanf("%c", &a);
    if (a != '#')
        showReverse();
    if (a != '#')
        printf("%c", a);
}

如果不用递归,我们可能要依靠链表来解决不定长度的问题,大大增加了编写的难度。

那如何得到使用它的规律呢?哈哈,我也不知道。毕竟常人用迭代,神人用递归

分治思想

当一个问题规模较大且不易求解的时候,就可以考虑将问题分成几个小的模块,逐一解决。

比如在查找中,用到分治思想的就有折半查找法:在一个有序序列中,指定最小值为 low、最大值为 high

KMP 算法

拓扑排序

线性顺序查找

在一组具有相同数据类型集合中找出关键字等于给定值K的数据元素,这个操作过程称为查找。通常情况下,查找算法的时间性能是以平均查找长度(ASL)来衡量的。

ASL=i=1nPiCi

其中,Pi 为查找表中第 i 个元素的概率;Ci 为找到关键字等于给定值 K 的数据元素时,已经和给定值 K 比较过的元素个数。

线性二分/多分查找

冒泡、快速排序

插入、希尔排序

选择排序

归并排序

基数排序

遵从 CC BY-NC-SA 4.0