递归与回溯算法归纳(一) - 知乎
递归
在函数中调用自己的方法
一个大问题可以分解为若干个规模较小,与原问题有相同形式的子问题,这些子问题可以用相同的解题思路来解决(自己里面套着自己,方法一样,传参不同),一直到某个临界点后原路返回。
递归算法有两种模型
模型一: 在递去的过程中解决问题
function recursion(大规模){
if (end_condition){ // 明确的递归终止条件
end; // 简单情景
}else{ // 在将问题转换为子问题的每一步,解决该步中剩余部分的问题
solve; // 递去
recursion(小规模); // 递到最深处后,不断地归来
}
}
模型二: 在归来的过程中解决问题
function recursion(大规模){
if (end_condition){ // 明确的递归终止条件
end; // 简单情景
}else{ // 先将问题全部描述展开,再由尽头“返回”依次解决每步中剩余部分的问题
recursion(小规模); // 递去
solve; // 归来
}
}
阶乘的实现
分析:用函数f(n)表示n的阶乘,则f(n-1)表示(n-1)!,则f(n)=n*f(n-1),如此建立起了大问题和小问题之间的联系,用小问题解决大问题。
public static long f(int n){
if(n == 1) // 递归终止条件
return 1; // 简单情景
return n*f(n-1); // 相同重复逻辑,缩小问题的规模
斐波那契数列
public static int fibonacci(int n) {
if (n == 1 || n == 2) { // 递归终止条件
return 1; // 简单情景
}
return fibonacci(n - 1) + fibonacci(n - 2); // 相同重复逻辑,缩小问题的规模
}
杨辉三角的取值
public static int getValue(int x, int y) {
if(y <= x && y >= 0){
if(y == 0 || x == y){ // 递归终止条件
return 1;
}else{
// 递归调用,缩小问题的规模
return getValue(x-1, y-1) + getValue(x-1, y);
}
}
return -1;
}
注:杨辉三角的列数要小于等于行数
代码是从第0行第0列开始计算
将第x行第y列的值得求解分解为两个可以调用该函数的子问题
回文字符串
分析:
判断一个字符串s是否是回文字符串可分为2步:
字符串s的的第一的字符和最后一个字符是否相等
去掉首尾的字符串s是否是回文字符串。
public static boolean isPalindromeString_recursive(String s){
int start = 0;
int end = s.length()-1;
if(end > start){ // 递归终止条件:两个指针相向移动,当start超过end时,完成判断
if(s.charAt(start) != s.charAt(end)){
return false;
}else{
// 递归调用,缩小问题的规模
return isPalindromeString_recursive(s.substring(start+1).substring(0, end-1));
}
}
return true;
}
二分查找
分析:
目标是查找target在low和high之间的位置
将问题分解成两个子问题:查找target在low和1/2(low+high)和target在1/2(low+high)和high之间的位置。
public static int binarySearch(int[] array, int low, int high, int target) {
//递归终止条件
if(low <= high){
int mid = (low + high) >> 1;
if(array[mid] == target){
return mid + 1; // 返回目标值的位置,从1开始
}else if(array[mid] > target){
// 由于array[mid]不是目标值,因此再次递归搜索时,可以将其排除
return binarySearch(array, low, mid-1, target);
}else{
// 由于array[mid]不是目标值,因此再次递归搜索时,可以将其排除
return binarySearch(array, mid+1, high, target);
}
}
return -1; //表示没有搜索到
}
回溯法:
试探法,当搜索到某一步时,发现选择达不到目标,就退回一步重新选择,称为回溯法。
递归是一种算法结构,回溯是一种算法思想,通过递归来实现。
1. 求子集
已知一组数(无重复元素),求这组数可以组成的所有子集。
#include
class Solution{
public:
std::vector>subsets(std::vector&nums){
std::vector>result;
std::vector item;
generate(0,nums,item,result);
return result;
}
private:
void generate(int i,std::vector&nums,
std::vector item,std::vector> &result)
{
if (i>=nums.size())
{
return;
}
else{
item.push_back(nums[i]);
result.push_back(item);
generate(i+1,nums,item,result);
item.pop_back();
generate(i+1,nums,item,result);
}
}
};
剪枝其实就是在递归中进行条件判断
#include
#include"string"
class Solution{
public:
std::vector generateParenthesis(int n){
std::vector result;
generate("",n,n,result);
return result;
}
private:
void generate(std::string item,int left,int right,std::vector & result){
if (left==0&&right==0)
{
result.push_back(item);
return;
}
if (left>0)
{
generate(item+'(',left-1,right,result);
}
if (left
分治:
将规模为N的问题分解为k个规模较小的子问题,这些问题相互独立且与原问题性质相同。求出问题的解后进行合并。
逆序数
已知数组nums,求新数组count,count[i]代表了在nums[i]右侧且比nums[i]小的元素个数