参考代码:https://blog.csdn.net/u012477435/article/details/83351659?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param
https://blog.csdn.net/m0_37950361/article/details/80668712
JZ-7 斐波那契数列
题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。
n<=39
方法一:循环(会超时)
int Fibonacci(int n) {
if (n==0 || n==1) return n;
return Fibonacci(n-1) + Fibonacci(n-2);
}
方法二:动态规划
选择的最优解为:动态规划, 就避免了递归带来的重复问题。
动态规划解析:
状态定义:设dp为一维数组,dp[ i ]代表斐波那契的第i个数字
转移方程:dp[ i + 2] = dp[ i + 1] + dp[ i ],
初始状态:dp[0] = 0 ,dp[1] = 1
返回值:dp[i]
空间复杂度的优化:由于本题,在状态转移的过程中,只需要使用到dp[ i + 2] = dp[ i + 1] + dp[ i ],三个数,因此无需使用数组进行存储,可以将空间复杂度从O(n) 降低到 O(1);
可能出现的数字越界问题:
class Solution {
public int fib(int n) {
int a = 0;
int b = 1;
int sum = 0;
if(n == 0){ //处理特殊情况
return a;
}
if( n == 1){
return b;
}
for(int i = 0;i < n - 1; i++){
sum = (a + b) % 1000000007 ; //动态方程
a = b;
b = sum;
}
return sum;
}
}
方法三:进一步优化
我们可以将递推式的求解从自顶向下改为自底向上(循环实现)。简而言之,我们已知前两项的值,然后我们就可以用前两项的值求出第3项的值,接着求第4、第5、…,直到求出第n项的值。
实现过程如下图所示,两个相同颜色的箭头可以确定一个新的数列项。
下面算法的时间复杂度为
O
(
n
)
O(n)
O(n),在面试中够用了,如果还是觉得简单可以继续往下看。
class Solution {
public:
int Fibonacci(int n) {
int a = 0,b = 1;
for(int i = 1;i <= n;i++){
a = a + b;
b = a - b;
}
return a;
}
};
方法三:矩阵幂乘(但是自己没有写出来代码)!
JZ-8 跳台阶
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
编程思路1(递归)
1级台阶:跳1级 1种跳法
2级台阶:跳1级;跳2级 2种跳法
3级台阶:1级+1级+1级;1级+2级;2级+1级 3种跳法
4级台阶:1级+1级+1级+1级;2级+2级;1级+2级+1级;2级+1级+1级;1级+1级+2级 5种跳法
5级台阶:1级+1级+1级+1级+1级;1级+1级+1级+2级;1级+1级+2级+1级;1级+2级+1级+1级;2级+1级+1级+1级;1级+2级+2 级;2级+1级+2级;2级+2级+1级 8种跳法
…
总结可发现以下规律:
当台阶数为1级时,有1种跳法;
当台阶数为2级时,有2种跳法;
当台阶数为3级时,有1+2种跳法;
当台阶数为4级时,有2+3种跳法;
当台阶数为5级时,有3+5种跳法;
…
当台阶数为n级时,有f(n-1)+f(n-2)种跳法。
故可用递归方法解决这一问题。
class Solution {
public:
int jumpFloor(int number) {
if(0 == number) return 0;
else if(1 == number) return 1;
else if(2 == number) return 2;
else{
return jumpFloor(number-1)+jumpFloor(number-2);
}
}
};
编程思路2(动态规划)
class Solution {
public:
int jumpFloor(int number) {
int a = 1;
int b = 1;
int sum = 1;
if(0 >= number){ //处理特殊情况
return 0;
}
for(int i = 1;i < number; i++){
sum = (a + b) % 1000000007; //动态方程
a = b;
b = sum;
}
return sum;
}
};
编程思路3(进一步优化)
class Solution {
public:
int jumpFloor(int number) {
int a = 1;
int b = 1;
if(0 >= number) return 0;
for(int i = 1;i <= number; i++){
a = a + b;
b = a -b;
}
return b;
}
};
JZ-10 矩阵覆盖
题目描述
我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2n的大矩形,总共有多少种方法?
比如n=3时,23的矩形块有3种覆盖方法:
思路1:被pass掉的递归法
class Solution {
public:
int rectCover(int number) {
if( 0 >= number) return 0;
else if(1 == number) return 1;
else if(2 == number) return 2;
else
return rectCover(number-1)+rectCover(number-1);
}
};
思路2:动态规划得人心
class Solution {
public:
int rectCover(int number) {
int a = 1,b = 1;
int sum = 1;
if(0 >= number) return 0;
if(1 == number) return 1;
for(int i=1;i < number;i++){
sum = (a + b)% 1000000007; //动态方程;
a = b;
b = sum;
}
return sum;
}
};
思路3:继续优化
class Solution {
public:
int rectCover(int number) {
if(0 >= number) return 0;
int a = 1,b = 1;
for(int i=1;i <=number;i++){
a = a+b;
b = a-b;
}
return b;
}
};
JZ-65 矩阵中的路径
题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如
矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
思路1 :一维数组表示矩阵
class Solution {
public:
bool hasPath(char* matrix, int rows, int cols, char* str)
{
//检查输入的合法性
if(matrix==nullptr || rows<1 || cols<1 || str==nullptr)
return false;
//定义一个布尔矩阵,其大小和matrix一样,用来存放元素有没有访问过的标识。
int length = rows*cols;//矩阵的长度
bool *Visited = new bool[length];
int d[4] = {-cols,cols,-1,1};//上下左右
//初始化矩阵Visited所有元素即为0,即为false
memset(Visited,0,length);
for(int i = 0;i < length;i++){
if(dfs(matrix,Visited,d,length,cols,i,str,0)){ //判断矩阵上的每一个元素是否匹配成功
delete[] Visited; //删除辅助数组
return true; //返回匹配成功
}
}
delete[] Visited; //释放辅助空间
return false; //运行至此,说明匹配失败
}
private:
bool dfs(char* matrix,bool *visited,int *d,int length,int cols,int index,char* str,int k){
if(str[k] == '\0') //如果已经匹配完整个字符序列,到结尾了
return true; //直接返回true,表示已经匹配完整个字符串
if(matrix[index] == str[k]){
visited[index] = true;
for(int i = 0;i<4;i++){
if(index+d[i] >= 0 && index+d[i] <= length && visited[index+d[i]]!=true){
if (dfs(matrix,visited,d,length,cols,index+d[i],str,k+1))
return true;
}
}
visited[index] = false;
}
return false;
}
};
思路2 :二维数组表示矩阵
class Solution {
public:
bool hasPath(char* matrix, int rows, int cols, char* str)
{
vector<char> visited(rows*cols,0);
for(int i=0;i<rows;i++)
for(int j=0;j<cols;j++){
if(dfs(matrix,visited,str,i,j,rows,cols)){ //判断矩阵上的每一个元素是否匹配成功
//delete[] Visited; //删除辅助数组
return true; //返回匹配成功
}
}
//delete[] Visited; //释放辅助空间
return false; //运行至此,说明匹配失败
}
private:
bool dfs(char* matrix,vector<char> visited,char* str,int x,int y,int rows,int cols){
if(x<0 || x>=rows || y<0 || y>=cols) //越界的点
return false;
if(matrix[x*cols+y]==*str && visited[x*cols+y]==0){
visited[x*cols+y]=1;
if(*(str+1)==0) //字符串结尾了,这是最后一个满足的字符
return true;
if(dfs(matrix,visited,(str+1),x,y-1,rows,cols) ||
dfs(matrix,visited,(str+1),x,y+1,rows,cols) ||
dfs(matrix,visited,(str+1),x+1,y,rows,cols) ||
dfs(matrix,visited,(str+1),x-1,y,rows,cols))
return true;
visited[x*cols+y] = 0;
}
return false;
}
};
JZ-66 机器人的运动范围
题目描述
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
方法1:DFS遍历
根据题目描述,我们可以模拟题目,我们假设一个5x5矩阵,阈值sho=3,如果我们用DFS的话,就相当于“不撞南墙不回头”,我在下面画了一个图,
最开始,我们在(0,0)的位置,我们假设按照{右,下,左,上}的方向去试探。所以我们走的顺序应该是按照图中的下标走的。
当走到4的时候,发现不能往继续往右边走,并且4个方向都走不通了,那就回溯到3,发现可以走到5,接着就站在5的视角,发现可以走6,就一直按照这个想法。
本题的递归函数就是:首先站在(0,0)的视角,先往右试探,发现可以走,就以下一个为视角,继续做相同的事情。
递归函数模板为:
时间复杂度:O(mn), m,n为矩阵大小,每个元素最多访问过一次
空间复杂度:O(mn)
dfs(){
// 第一步,检查下标
// 第二步:检查是否被访问过,或者是否满足当前匹配条件
// 第三步:检查是否满足返回结果条件
// 第四步:都没有返回,说明应该进行下一步递归
// 标记
dfs(下一次)
// 回溯
}
int main() {
dfs(0, 0);
}
class Solution {
public:
using V = vector<int>;
using VV = vector<V>;
int dir[5] = {-1,0,1,0,-1};
int check(int n){ //检测坐标的加和并返回
int sum = 0;
while(n){
sum += (n%10);
n /= 10;
}
return sum;
}
void dfs(int x, int y, int shot, int r, int c, int &ret, VV &mark) {
//检查下标 和 是否访问
if(x < 0 || x >= r || y < 0 || y >= c || mark[x][y] == 1)
return ;
//检查当前坐标是否满足条件
if(check(x) + check(y) > shot)
return ;
//代码走到这里,说明当前坐标符合条件
mark[x][y] = 1;
ret += 1;
for(int i = 0;i < 4; ++i)
//(-1,0),(0,1),(1,0),(0,-1) ===> 上,右,下,左
dfs(x+dir[i],y+dir[i+1],shot,r,c,ret,mark);
}
int movingCount(int threshold, int rows, int cols)
{
if(threshold <= 0)
return 0;
VV mark(rows,V(cols,-1));
int ret = 0;
dfs(0,0,threshold,rows,cols,ret,mark);
return ret;
}
};
方法2:BFS遍历
当前图的遍历算法还有bBFS,所以也可以用BFS做。方法一实例的图,用BFS就是如下这样:
class Solution {
public:
using pii = pair<int,int>;
int dir[5] = {-1,0,1,0,-1};
int check(int n){ //检测坐标的加和并返回
int sum = 0;
while(n){
sum += (n%10);
n /= 10;
}
return sum;
}
int movingCount(int threshold, int rows, int cols)
{
if(threshold <= 0) return 0;
int ret = 0;
int mark[rows][cols];
memset(mark,-1,sizeof(mark));
queue<pii> q;
q.push({0,0});
mark[0][0] = 1;
while(!q.empty()){
auto node = q.front(); //获取队列的头
q.pop(); //队列的头出队
//每次保证进队列的都是满足条件的坐标
//代码走到这里,说明当前坐标符合条件
++ret;
for(int i = 0;i < 4;++i){
int x = node.first + dir[i];
int y = node.second + dir[i+1];
//检查下标 和 是否访问
if(x >= 0 && x < rows && y >= 0 && y < cols && mark[x][y] == -1){
if(check(x) + check(y) <= threshold){ //检查当前坐标是否满足条件
q.push({x,y});
mark[x][y] = 1;
}
}
}
}
return ret;
}
};
JZ-27 字符串排列
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
方法一:回溯法|递归
回溯的本质:一种求解效益更高的穷举法。
基本思路:
1 针对所给问题,定义问题的解空间;
2 确定易于搜索的解空间结构;
3 从开始节点出发,以深度优先的方式搜索整个解空间,并在搜索过程中用剪枝函数避免无效搜索。
4 如果当前扩展结点不能再向纵深方向移动,当前节点为死节点。此时,应该往回移动至最近的一个活节点处,并使这个活节点成为当前节点的扩展结点。
思考过程
回溯过程
解决方案
class Solution {
public:
vector<string> res;
vector<string> Permutation(string str) {
//回溯。注:回溯是一种算法思想,可以用递归实现
if(str.size() == 0) return res;
permute(str,0);
sort(res.begin(),res.end());//按字典序输出
return res;
}
void permute(string s,int begin){ //begin指向当前执行排列操作的字符串的第一个字符
if(begin == s.size()) res.push_back(s);
else{
for(int i = begin;i < s.size();i++){
if(i != begin && s[i] == s[begin]) continue; //去重
swap(s[begin],s[i]);
permute(s,begin+1);
swap(s[begin],s[i]);
}
}
}
};