giao目录
1.递归经典入门
1.1 经典入门例子:斐波那契序列
f(n) = f(n - 1) + f(n - 2)
int fibonacci(int n){
if(n == 0) return 0;
if(n == 1) return 1;
return fibonacci(n -1) + fibonacci(n - 2);
}
1.2 求阶乘
f(n) = n * f(n - 1)
int factorial(int n){
if(n == 0) return 1;
return n * factorial(n - 1);
}
1.3 gcd
result = gcd(b,a mod b)
int gcd(int a, int b) {//a为被除数,b为除数gcd(b,a mod b)
if (a % b == 0) return b;
return (b, a % b);
}
2.分治
2.1.1 跳台阶
n阶台阶,可以走一步,也可以走两步,求一共多少种可能性
Fibonacci : f(n) = f(n - 1) + f(n - 2)
int Fibonacci(int n){
if( n == 0 ) return 0;
if( n == 1 ) return 1;
if( n == 2 ) return 2;
return Fibonacci( n - 2 ) + Fibonacci(n - 1);
}
2.1.2 变态跳台阶
n阶台阶,可以走1 - n 步,求一共多少种可能性
Fibonacci : f(n) = 1 + f(n - 1) + f(n - 2) + … + f(1);
递归的深度比较长,使用迭代
int JumpEx(int n) {
vector<int> p(n+1,1);
p[0] = 0;
p[1] = 1;
p[2] = 2;
for (int i = 3; i <= n; ++i)
for (int j = 1; j < i; ++j)
p[i] += p[j];
return p[n];
}
2.2 归并排序
先归
后并
void mergeArray(int A[], int lo,int mid, int hi) {
int* temp = new int[hi - lo + 1];
int i = lo, j = mid + 1;
int k = 0;
while (i <= mid && j <= hi)
{
if (A[i] <= A[j])//注意要包括等号
temp[k++] = A[i++];
else temp[k++] = A[j++];
}
while (i <= mid)
temp[k++] = A[i++];
while (j <= hi)
temp[k++] = A[j++];
for (int i = lo, k = 0; i <= hi; i++, k++)
{
A[i] = temp[k];
}
delete[] temp;
}
void mergeSort(int A[], int lo, int hi) {
if (lo >= hi) return;
int mid = lo + (hi - lo) / 2;
mergeSort(A, lo, mid); //左半区间[lo, mid] 排好序
mergeSort(A, mid + 1, hi); //右半区间[mid + 1, hi] 排好序
mergeArray(A, lo, mid, hi); //进行合并
}
3.二叉树
3.1二叉树的遍历
1.1 先序:递归
void preOrder(TreeNode *root,void (*visit)(TreeNode *p)){
if( root == nullptr) return;
visit(root);
preOrder(root -> left,visit);
preOrder(root -> left,visit);
}
1.2 先序:迭代
利用栈的特点,永远先入栈右子树,然后入栈左子树,栈顶永远是左子树
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> s;
vector<int> res;
if(root)
s.push(root);
while(!s.empty()){
TreeNode* p = s.top();
s.pop();
res.push_back(p -> val);
if(p -> right) s.push(p -> right);
if(p -> left) s.push(p -> left);
}
return res;
}
};
2.1 中序:递归
void preOrder(TreeNode *root,void (*visit)(TreeNode *p)){
if( root == nullptr) return;
preOrder(root -> left,visit);
visit(root);
preOrder(root -> left,visit);
}
2.2 中序:迭代
基本思想:入栈根节点,从根节点开始,入栈所有左节点
发现:不能入栈了
此时栈顶为最左的节点,访问,出栈
加入其右节点(如果存在)
如果不存在右节点,继续出栈
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ret;
stack<TreeNode*> s;
TreeNode* p = root;
while(p || !s.empty()){//不存在右子树,且当前栈内节点全部遍历过
while(p) {
s.push(p);//入栈所有左节点和p
p = p -> left;
}
TreeNode* curr = s.top();
s.pop();
ret.push_back(curr -> val);
p = curr -> right;
}
return ret;
}
};
3.1 后序:递归
void preOrder(TreeNode *root,void (*visit)(TreeNode *p)){
if( root == nullptr) return;
preOrder(root -> left,visit);
preOrder(root -> left,visit);
visit(root);
}
3.2 后序:迭代
验证可以通过类似于先序迭代的方法:不同点在于,先入栈左子树,然后右子树,最后将访问的顺序倒置
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> s;
vector<int> ret;
if(root) s.push(root);
while(!s.empty()){
TreeNode *p = s.top();
s.pop();
if(p -> left) s.push(p->left);
if(p -> right) s.push(p->right);
ret.push_back(p ->val);
}
reverse(ret.begin(),ret.end());
return ret;
}
};
4.1 层序遍历
//广度优先搜索
void lvOrder(TreeNode *root,void (*visit)(TreeNode *p)){
if(p == nullptr) return;
queue<TreeNode*> node;
node.push(root);
while(!p.empty()){
TreeNode *tmp = node.front();
node.pop();
visit(tmp);
if(tmp -> left) node.push(tmp -> left);
if(tmp -> right) node.push(tmp -> right);
}
}
3.2 二叉树路径问题
3.2.1 二叉树高度
一目了然!
int getTreeHeight(TreeNode* root){
//递归结束条件root == nullptr
if(root == nullptr) return 0;
int left = getTreeHeight(root -> left);//获取左子树高度
int right = getTreeHeight(root -> right);//获取右子树高度
return max(left,right) + 1;
}
3.2.2 节点路径
bool searchPath(TreeNode* root,TreeNode *target,vector<TreeNode*> &path){
path.push_back(root);
if(root == target){
return true;
}
bool found = false;
if(root -> left)
found = searchPath(root -> left,target,path);//寻找左节点
if(!found && root -> right)//没找到
found = searchPath(root -> right,target,path);//寻找右节点
if(!found)//如果没找到,删除节点
path.pop_back();
return found;
}
3.2.3 二叉树和为某值的路径
class Solution {
public:
vector<vector<int>> pathSum(TreeNode* root, int sum) {
if(root == nullptr) return get;
vector<int> vec;
getPath(root,sum,0,vec);
return get;
}
void getPath(TreeNode *root,int sum,int curr, vector<int>& path){
curr += root -> val;
path.push_back(root -> val);
bool isLeaf = root -> left == nullptr && root -> right == nullptr;//判断是叶子节点
if(curr == sum && isLeaf) path.push_back(res);
if(root -> left) getPath(root -> left,sum,curr,res);
if(root -> right) getPath(root -> right,sum,curr,res);
path.pop_back();
}
vector<vector<int>>get;
};
3.3 公共祖先问题
3.3.1 二叉搜索树两节点的公共祖先
TreeNode Get(TreeNode *root,TreeNode *p,TreeNode *q){
if(root == nullptr) return nullptr;
if(p -> val < root -> val && q -> val > root -> val
|| p -> val > root -> val && q -> val < root -> val
|| q == root
|| p == root )
return root;
//否则
if(p -> val < root -> val && q -> val < root -> val)//都在左边
return Get(root -> left,p,q);
else//都在右边
return Get(root -> right,p,q);
}
3.3.2 平凡二叉树两节点公共祖先
基本思想:get两个节点路径,找到第最后一个相同的节点。如果
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == nullptr) return nullptr;
vector<TreeNode*> res1,res2;
searchPath(root,p,res1);
searchPath(root,q,res2);
int i;
for(i = 0;i < res1.size() && i < res2.size() && res1[i] == res2[i];++i);
return res1[i-1];
}
bool searchPath(TreeNode* root,TreeNode *target,vector<TreeNode*> &res){
res.push_back(root);
if(root == target) {
return true;
}
bool found = false;
if(!found && root -> left) found = searchPath(root-> left,target,res);
if(!found && root -> right) found = searchPath(root -> right,target,res);
if(!found)
res.pop_back();
return found;
}
};
3.4 二叉树恢复问题
3.4.1 中序 + 先序
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.empty()) return nullptr;
int base = preorder[0];
TreeNode* root = new TreeNode(base);
//划分左右子树,分治
vector<int> lp,li;//左,先序、中序
vector<int> rp,ri;//右,先序、中序
int i = 0;
for(;i < preorder.size() && inorder[i] != base;++i){
lp.push_back(preorder[i+1]);
li.push_back(inorder[i]);
}
++i;
for(;i < preorder.size();++i){
rp.push_back(preorder[i]);
ri.push_back(inorder[i]);
}
root -> left = buildTree(lp,li);
root -> right = buildTree(rp,ri);
return root;
}
};
3.4.2 中序 + 后序
比较花费空间,但是比较好理解
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(postorder.empty()) return nullptr;
int base = postorder.back();
TreeNode* root = new TreeNode(base);
//划分左右子树,分治
vector<int> lp,li;//左,后序、中序
vector<int> rp,ri;//右,后序、中序
int i = 0;
for(;i < inorder.size() && inorder[i] != base;++i){
lp.push_back(postorder[i]);
li.push_back(inorder[i]);
}
++i;
for(;i < inorder.size();++i){
rp.push_back(postorder[i-1]);
ri.push_back(inorder[i]);
}
root -> left = buildTree(li,lp);
root -> right = buildTree(ri,rp);
return root;
}
};