在大一学习C语言的时候,举过一个用栈实现的括号匹配算法,当时以为很难,不过如今回顾起来,这个算法也算是比较简单的一个关于栈的应用了。而如今所常见的算法问题也都是什么中缀表达式转后缀表达式,双栈找最小值之类的。难度比之括号匹配稍有提高,不过倒也算是必需要掌握的算法。java
上述所说的表达式求值在程序设计语言中是一个最基本的问题,也是栈的实现的一个典型范例。c++
为何说是最基本? 咱们知道,中缀表达式对于人来讲是比较友好的,学过四则运算就能够对其求值,然而对于计算机来讲,虽然也能够想办法计算,可是却算不上友好了;相反,后缀表达式虽然对人不友好,可是倒是计算机所喜欢的。算法
(话说,后缀表达式在编译原理中的重要性也是能栖身前列的。)数据结构
在C语言入门的时候,咱们就会经过递归来求斐波那契数列,很简单:函数
int fibonacci(int n) {
if (n==0 || n==1) return 1;
return fibonacci(n-1) + fibonacci(n-2);
}
复制代码
可是那时候还不懂原理,仅仅知道,递归就是函数调用其自己,可是接触到数据结构的时候,再一次提出了递归的概念。性能
什么是递归?递归就是函数调用其自己。学习
reverse(know) { // 1. go on
if (you know) return you know; // 2. look 4
else back to see the 1; // 3. go back to 1.
} // 4. you know what is recurision now.
复制代码
这时候,咱们不只知道递归真正的用法,同时也知道了一个事实,即递归程序的开销一般很大,但与之相反的,其代码量又是很是少的。优化
一般状况下,咱们会选择将递归程序改写成非递归程序,即所谓消除递归,可是当改写后和改写前的程序并不会有太大的性能提高,咱们也没有必要去改写,好比:cout << fibonacci(5);
,为了这样的调用去消除递归,有必要吗?ui
可实际状况是,一个应用所要处理的数据并不算小,消除递归是不可避免的。spa
递归的精髓在于可否将原始的问题转换为属性相同,但问题规模较小的问题,学过算法就知道,这一样也是贪心策略和动态规划的本质。
对于递归程序的优化,咱们一般会选择栈作辅助,为何?咱们知道,在操做系统中,有一种叫作**“函数调用堆栈”**的名词,大概的解释就是:当在某一函数A中调用另外一函数B时,咱们将A中的内容保存后,压入系统堆栈(你能够说这是在建立还原点,也能够说这个是现场保护,开心就好。),而后执行函数B的内容,当函数B执行结束后,将A从系统堆栈中弹出,继续从断点处执行,同时销毁以前申请的栈空间。
同时,咱们要知道,操做系统的主存是由空间上限的,不多是无限的。系统堆栈的大小天然是受操做系统存储空间大小的约束的,并且绝对小于系统存储空间(不可能等于)。因此,当递归程序不断申请栈空间到达系统栈所能分配的上限时,就有了所谓的“系统堆栈溢出”,即咱们一般所说的“爆栈”。
n=3时,栈的申请状况
n=6时,栈的申请状况
java中,异常java.lang.StackOverflowError就是一种堆栈溢出错误,不过,能够经过修改JVM参数来增大虚拟机栈空间,如-vm args-Xss128k;但这也只是权宜之计,治标不治本呐。
可是呢,一个递归程序并不必定非要用栈辅助改写成非递归程序(即消除递归),有时候,一个循环就够了。
int main() {
int n, i=j=1, tmp=0;
cin >> n;
while (n--) {
tmp = i+j;
i = j;
j = tmp;
}
}
复制代码
暂时就说这么多,至于后面的,那就后面再说吧,毕竟这也只是(一)嘛。