剑指offer!
秋招对我好点!
JZ 12. 矩阵中的路径
***分析:*** 使用回溯和递归 只要找到一个结果就可以返回
哎,还是对递归不太熟悉,他认识我,我不认识他!
继续学习吧!冲!
class Solution {
public:
bool dfs(vector<vector<char>>& board, string word,vector<vector<bool>>& visited,int m,int n,int i,int j,int k){
// 递归结束
if(i<0 || i>=m || j<0 || j>=n || visited[i][j] ==true || board[i][j]!=word[k]) return false;
if(k==word.size()-1) return true; // 返回值
visited[i][j] = true;
bool flag = dfs(board,word,visited,m,n,i-1,j,k+1)||
dfs(board,word,visited,m,n,i+1,j,k+1)||
dfs(board,word,visited,m,n,i,j-1,k+1)||
dfs(board,word,visited,m,n,i,j+1,k+1);
visited[i][j] = false;
return flag;
}
bool exist(vector<vector<char>>& board, string word) {
int m = board.size();
int n = board[0].size();
vector<vector<bool>> visited(m,vector<bool>(n,false));
for(int i=0;i<m;++i){
for(int j=0;j<n;++j){
if (dfs(board,word,visited,m,n,i,j,0)) return true;
}
}
return false;
}
};
JZ 13. 机器人的运动范围
题目链接
分析: 由于机器可以向右或者向下运动,并且题目求解的是能够到达的格子数,而不是分析运动到右下角会出现的一些问题,因此我们选择广度优先搜索,而不是深度优先搜索。
广度优先搜索类似于二叉树的层次遍历(每次分析上一个节点能达到的所有情况),选择队列queue进行辅助
深度优先搜索由于是递归问题,所以可以使用栈来辅助,递归问题从本质上来说就是利用栈来实现的,这也是为什么递归可以保留上一次递归的信息(先进后出的栈功不可没!)
class Solution {
public:
int getNum(int x){
int res = 0;
while(x!=0){
res += x%10;
x/=10;
}
return res;
}
int movingCount(int m, int n, int k) {
// 使用广度优先搜索
// 使用队列来辅助
if(k==0) return 1;
queue<pair<int,int>> q;
vector<vector<int>> visited(m,vector<int>(n,0));
int ans = 1;
q.push(make_pair(0,0));
visited[0][0] = 1;
int dx[2] = {0,1};
int dy[2] = {1,0};
while(!q.empty()){
auto [x,y] = q.front();
q.pop();
// 广度有限搜索
for(int i=0;i<2;++i){
int tx = x + dx[i];
int ty = y + dy[i];
if(tx<0 || tx>=m || ty<0 || ty>=n || visited[tx][ty] || getNum(tx) + getNum(ty)>k) continue;
q.push(make_pair(tx,ty));
ans++;
visited[tx][ty] = 1;
}
}
return ans;
}
};
JZ 14- I. 剪绳子
题目链接
分析: 从数学角度来分析,尽量将绳子剪成长度为3,结果的乘积会最大
可能是由于3是第一个质数前的最大素数?
class Solution {
public:
int cuttingRope(int n) {
if(n==2) return 1;
if(n==3) return 2;
if(n==4) return 4;
int number = 1;
while(n!=0){
if(n==4 || n==2){
number*=n;
n=0;
break;
}
number*=3;
n-=3;
}
return number;
}
};`
JZ 14- II. 剪绳子 II
题目链接
分析: 跟上面是一样的 ,尽量分成3, 不过由于数值较大, 每步计算都需要对1000000007取余
class Solution {
public:
int cuttingRope(int n) {
// 剪绳子 这里的绳子比较长
// 跟之前还是一样的 尽量分成多的34
if(n<=3) return n-1;
int mod = 1000000007;
int x = n/3;
int y = n%3;
long number =1;
if(y==0){
for(int i=0;i<x;++i){
number*=3;
number%=mod;
}
}else if(y==1){
for(int i=1;i<x;++i){
number*=3;
number%=mod;
}
number*=4;
}else{
for(int i=0;i<x;++i){
number*=3;
number%=mod;
}
number*=2;
}
number%=mod;
return (int)number;
}
};
JZ 26 树的子结构
题目链接
分析: 对于树的子结构问题,一般都采用递归。
主要步骤有两个:
- 在树 A 中找到和树 B 的根节点的值一样的节点 p
- 判断树 A 中以 p 为根节点的子树是不是包含和树 B 一样的结构
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSub(TreeNode* root1, TreeNode* root2){
if(!root2) return true;
if(!root1 || root1->val != root2->val) return false;
return isSub(root1->left,root2->left) && isSub(root1->right,root2->right);
}
bool isSubStructure(TreeNode* A, TreeNode* B) {
// 使用递归
if(!A || !B) return false; // 如果A B为nullptr的话 返回false
return isSub(A,B) || isSubStructure(A->left,B) || isSubStructure(A->right,B);
}
};
JZ 27 二叉树的镜像
题目链接
分析: 将root的两个子树先保存下来,一直递归到最深处,进行交换。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
if(!root) return NULL;
TreeNode* left = mirrorTree(root->left);
TreeNode* right = mirrorTree(root->right);
root->right = left;
root->left =right;
return root;
}
};
JZ 28 对称的二叉树
题目链接
分析: 如果一棵二叉树和它的镜像一样,那么它是对称的。
因此判断条件可视为 两个树在什么情况下是镜像对称的:
- 它们的两个根结点具有相同的值
- 每个树的右子树都与另一个树的左子树镜像对称
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool check(TreeNode* root1,TreeNode*root2){
if(!root1 && !root2) return true;
if(!root1 || !root2) return false;
return root1->val==root2->val && check(root1->right,root2->left) && check(root2->right,root1->left);
}
bool isSymmetric(TreeNode* root) {
// 根节点有相同的值 并且一个树的左子树和另一棵树的右子树镜像对称
return check(root,root);
}
};
JZ 34 二叉树中和为某一值的路径
题目链接
分析: 想都不要想,这题肯定采用深度优先搜索的方式,枚举每一条从根节点到叶子节点的路径。当我们遍历到叶子节点,且此时路径和恰为目标和时,我们就找到了一条满足条件的路径。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
public:
void dfs(TreeNode* root,int target,int num){
// 递归结束
if(!root) return;
path.push_back(root->val);
num+=root->val;
if(!root->left && !root->right && num==target){
result.push_back(path);
}
dfs(root->left, target,num);
dfs(root->right, target,num);
path.pop_back();
}
vector<vector<int>> pathSum(TreeNode* root, int target) {
// 使用递归 深度优先搜索
dfs(root,target,0);
return result;
}
};
JZ 66 构建乘积数组
题目链接
分析:
可以直接暴力求解,时间复杂度是O(N^2)
但是也可以像动态规划那里,利用上一步的计算结果直接得到当前的结果
所以将乘积分成左右两个部分计算,左边的乘积从左到右计算,右边的乘积从右到左计算,最后再合并到一起!
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
// 分成两部分来计算
int length = a.size();
if(length==0) return vector<int>{}; // 空输入
vector<int> res(length,0);
vector<int> left(length,0);
vector<int> right(length,0);
// 计算i左边的乘积
left[0] = 1;
for(int i=1;i<length;++i){
left[i] = left[i-1] * a[i-1];
}
// 计算i右边的乘积
right[length-1] =1;
for(int i=length-2;i>=0;--i){
right[i] = right[i+1] * a[i+1];
}
// 合并
for(int i=0;i<length;++i){
res[i] = left[i] * right[i];
}
return res;
}
};
JZ 67 把字符串转换成整数
题目链接
分析:
耐心去分析,把所有情况都分析到位了!
class Solution {
public:
int strToInt(string str) {
// 练习一下自己的逻辑能力
long long number = 0;
int size = str.size();
// 去除空格
int i=0;
while(i<size && str[i]==' ') i++;
cout<<"i:"<<i<<endl;
cout<<str[3]<<endl;
bool flag = true;
// 空格
if(i==size) return 0;
// 第一个数值是其他字母的
else if(str[i]!='+' && str[i]!='-' && (str[i]>'9' || str[i]<'0')) return 0;
else{
if(str[i]=='+'){
i++;
}else if(str[i]=='-'){
i++;
flag = false;
}
while(i<size && str[i]<='9' && str[i]>='0'){
number *=10;
number+=str[i]-'0';
if(number>INT_MAX) break;
i++;
}
}
if(!flag) number*=-1;
// else if(str[i]=='+'){
// i++;
// while(i<size && str[i]<='9' && str[i]>='0'){
// number *=10;
// number+=str[i]-'0';
// if(number>INT_MAX) break;
// i++;
// }
// }
// else if(str[i]=='-'){
// cout<<"1"<<endl;
// i++;
// while(i<size && str[i]<='9' && str[i]>='0'){
// number *=10;
// number+=str[i]-'0';
// if(number>INT_MAX) break;
// i++;
// }
// number*=-1;
// cout<<number<<endl;
// }else{
// while(i<size && str[i]<='9' && str[i]>='0'){
// number *=10;
// number+=str[i]-'0';
// if(number>INT_MAX) break;
// i++;
// }
// }
if(number>=INT_MAX) return INT_MAX;
if(number<=INT_MIN) return INT_MIN;
return (int)number;
}
};
JZ 68 - I. 二叉搜索树的最近公共祖先
题目链接
分析:
可以用二分查找,也可以使用递归求解!
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
// 二叉树是一样的
// 使用递归
// 返回值是最近公共祖先
if(root==nullptr) return nullptr;
if(root==p || root ==q) return root;
TreeNode* left = lowestCommonAncestor(root->left,p,q);
TreeNode* right = lowestCommonAncestor(root->right,p,q);
if(left==nullptr && right==nullptr) return nullptr;
if(left==nullptr) return right;
if(right==nullptr) return left;
return root;
}
};
JZ 68 II. 二叉树的最近公共祖先
题目链接
分析:
最近工作祖先存在三种情况:
- 左子树和右子树都存在一个
- 只存在与左子树中
- 只存在右子树中
先搜索到最深处,再递归回来,进行判断。
**需要时刻记住的是:**递归的返回值是什么!
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
// 使用dfs
if(root==nullptr || root == p || root == q) return root; // 递归结束
// 递归需要时刻记住的是 返回的是什么
TreeNode* left = lowestCommonAncestor(root->left,p,q);
TreeNode* right = lowestCommonAncestor(root->right,p,q);
if(left!=nullptr && right!=nullptr) return root; // 左子树和右子树都不为空
if(left==nullptr) return right; // 左子树为空 全在右子树
if(right==nullptr) return left; // 右子树为空 全在左子树
return nullptr;
}
};