c++学习笔记(16) 递归
递归:能够解决那些难以用简单循环解决的问题
例如:八皇后问题,阶乘
递归函数: 调用自身的函数
1.阶乘:
0 !=1
1!=1
n!=n*(n-1)!
(n-1)!=(n-1)(n-2)!
假设函数factorial(n)为求n!;
如果调用到n=0的情况,函数直接返回结果,函数知道如何求解最简单的情形,称之为基本情况或者“停止条件”。如果参数大于0,函数将问题规约为求解(n-1)!的子问题。在递归中,子问题应该更简单或者更小,但是子问题和原问题具有相同的性质,可以用一个不同的参数调用本函数来求解子问题。这称之为递归调用。
#include <iostream>
using namespace std;
int factorial(int);
int main(int argc, char *argv[])
{
cout << "Enter a int number: ";
int num;
cin >> num;
cout << num << "! = " << factorial(num) << endl;
return 0;
}
int factorial(int n)
{
if(n==0)
return 1; // 停止条件
else
return n*factorial(n-1); // 递归调用
}
函数递归调用的执行过程:
例如: 斐波那契额数列:
定义函数fib(n),求数列前n项
fib(0)=0;
fib(1) = 1;
fib(2) = fib(1)+fib(0)
fib(3) = fib(2)+fib(1)
#include <iostream>
using namespace std;
int fib(int);
int main(int argc, char *argv[])
{
cout << "Enter a int number: n" << endl;
int num;
cin >> num;
cout << "The fib is " << fib(num) << endl;
return 0;
}
int fib(int n) // 求解斐波那契额数列
{
if(n==0)
return 0; // 停止条件
else if(n==1)
return 1;
else
return fib(n-1)+fib(n-2); // 递归调用
}
例如:fib(4)的调用过程。 调用栈的变化情况
利用递归判断回文字符串:
#include <iostream>
#include <string>
using namespace std;
bool isPalindrome(const string& s)
{
// 基本情况分为两种 1.首尾字符不相同,return false 2. 字符串长度为1或者0 return true
if(s[0]!=s[s.size()-1])
return false;
else if(s.size()<=1)
return true;
else
return isPalindrome(s.substr(1,s.size()-2)); // 获得子串,递归判断
}
int main(int argc, char *argv[])
{
cout << "Enter a string: " << endl;
string ss;
getline(cin, ss);
if(isPalindrome(ss))
cout << "The string is Palindrome" << endl;
else
cout << "The string is not Palindrome" << endl;
return 0;
}
递归辅助函数
给类似原问题的问题定义一个递归函数.(这句话有点绕),得到原问题的求解方案,这种新的函数叫做递归辅助函数
对isPalindrome()函数的改进。
#include <iostream>
#include <string>
using namespace std;
// 为了避免每次递归都要创建新的字符串,这里传入了参数low和high,指出上下界的范围
// 重载isPalindrome()函数作为辅助函数
// 定义一个辅助函数接受额外的参数
// 递归方案中有字符串和数组,递归辅助函数是非常有用的
bool isPalindrome(const string& s, int low, int high)
{
// 基本情况分为两种 1.首尾字符不相同,return false 2. 字符串长度为1或者0 return true
if(s[low]!=s[high])
return false;
else if(low>=high)
return true;
else
return isPalindrome(s, low+1, high-1); // 获得子串,递归判断
}
// 函数重载
bool isPalindrome(const string& s)
{
return isPalindrome(s, 0, s.size()-1);
}
int main(int argc, char *argv[])
{
cout << "Enter a string: " << endl;
string ss;
getline(cin, ss);
if(isPalindrome(ss))
cout << "The string is Palindrome" << endl;
else
cout << "The string is not Palindrome" << endl;
return 0;
}
递归的方法实现选择排序,对字符串进行排序:
#include <iostream>
#include <string>
using namespace std;
// 定义一个辅助函数接受额外的参数
// 递归方案中有字符串和数组,递归辅助函数是非常有用的
void sort(string& s, int high) //递归辅助函数
{
if(high>0)
{
int max_index = 0;
char max_char = s[0];
for(int i=0; i<=high; i++)
{
if(s[i]>max_char)
{
max_char = s[i];
max_index = i;
}
}
s[max_index] = s[high];
s[high] = max_char;
sort(s, high-1); // 递归调用
}
}
void sort(string& s)
{
sort(s, s.size()-1);
}
int main(int argc, char *argv[])
{
cout << "Enter a string: " << endl;
string ss;
getline(cin, ss);
sort(ss);
cout << "The String After sorted: " << ss << endl;
return 0;
}
递归实现二分搜索:
#include <iostream>
#include <string>
using namespace std;
// 定义一个辅助函数接受额外的参数
// 递归方案中有字符串和数组,递归辅助函数是非常有用的
int binary_search(const int list[], int key, int low, int high)
{
if(low>high) // 未找到
return -low-1;
int mid = (low+high)/2;
if(key<list[mid])
return binary_search(list, key, low, mid-1);
else if(key==list[mid])
return mid;
else
return binary_search(list, key, low+1, high);
}
int binary_search(const int list[], int key, int size)
{
return binary_search(list, key, 0, size-1);
}
int main(int argc, char *argv[])
{
int list[] = {2,3,4,5,6,7,8,9,12,14,16,18,19,20,34};
int i = binary_search(list, 4, 15);
int j = binary_search(list, 9, 15);
int k = binary_search(list, 20, 15);
int m = binary_search(list, 11, 15);
cout << "binary_search(list, 4, 15) return" << i << endl;
cout << "binary_search(list, 9, 15) return" << j << endl;
cout << "binary_search(list, 20, 15) return" << k << endl;
cout << "binary_search(list, 11, 15) return" << m << endl;
return 0;
}
运行结果:
汉诺塔问题:
汉诺塔问题的递归特点:
此问题的基本情况(停止条件)是:n==1时,将唯一一个盘子从A移动到B.
原问题分解为三个子问题:
1.将n-1个盘子从A->C, B作为辅助
2.将最下面的第n个盘子从A->B.
3.将n-1个盘子从C->B,A作为辅助
#include <iostream>
#include <string>
using namespace std;
void moveDisk(int n, char fromTower, char toTower, char auxTower)
{
if(n==1)
cout << "Move disk " << n << " from " << fromTower << " to " << toTower << endl;
else
{
moveDisk(n-1, fromTower, auxTower, toTower);
cout << "Move disk " << n << " from " << fromTower << " to " << toTower << endl;
moveDisk(n-1, auxTower, toTower, fromTower);
}
}
int main(int argc, char *argv[])
{
cout << "Enter number of disks: ";
int diskNumber;
cin >> diskNumber;
moveDisk(diskNumber, 'A', 'B', 'C');
return 0; //
}
运行结果:
八皇后问题:
#include <iostream>
#include <string>
using namespace std;
// 定义数组queen[8]
// queen[i]=j 代表i行j列的皇后
const int NUMBER_OF_QUEENS = 8;
int queen[NUMBER_OF_QUEENS];
// 判断解的合法性
bool isValid(int row, int column)
{
for(int i=1; i<=row; i++)
{
if(queen[row-i]==column || queen[row-i]==column-i || queen[row-i]==column+i)
return false;
}
return true;
}
//
bool search(int row)
{
if(row==NUMBER_OF_QUEENS) //停止条件
return true;
for(int column=0; column<NUMBER_OF_QUEENS; column++)
{
queen[row] = column;
if(isValid(row, column) && search(row+1))
return true;
}
return false;
}
// display the chessboard
void display()
{
cout << "\n-----------------\n";
for(int row=0; row<NUMBER_OF_QUEENS; row++)
{
for(int column=0; column<NUMBER_OF_QUEENS; column++)
{
cout << ((column==queen[row])?"|Q":"| ");
}
cout << "|\n-----------------\n";
}
}
int main(int argc, char *argv[])
{
search(0);
display();
return 0; //
}
递归有着严重的额外开销,每当程序进行函数调用时,系统必须为所有的局部变量和参数分配内存空间,需要消耗相当大的内存,可能造成栈溢出(stack overflow)的错误。
尾递归
尾递归可以有效地减少堆栈空间。
注:(书本)
尾递归是可取的,因为当最后一次递归调用结束时,函数就结束了,所以没有必要存储在堆栈中的中间调用,有些编译器可以优化尾递归来减少堆栈空间。
一个非为尾递归的函数可以通过使用辅助参数转换为一个尾递归函数。