加载中...

leetcode刷题

二叉树.

226. 翻转二叉树

Q: 沿中轴线对称翻转一颗二叉树.

这种题还是尽量递归解决吧

TreeNode* invertTree(TreeNode* root) {
    if(!root) return root; 
    invertTree(root->left); invertTree(root->right);
    swap(root->left, root->right);
    return root;
}

101. 对称二叉树

Q: 判断一颗二叉树是否是对称的.

bool isSymmetric(TreeNode* root) {
    if (!root) return true;
    return dp(root->left,root->right);
}
bool dp(TreeNode* left, TreeNode* right){
    if(left==NULL && right ==NULL) return true;
    if(left==NULL || right ==NULL) return false;
    if(left->val!=right->val)return false;
    return dp(left->left,right->right) && dp(left->right,right->left);
}

104. 二叉树的最大深度

Q: 返回二叉树的最大深度 (int).

递归一句话.

int maxDepth(TreeNode* root) {
    if(root==NULL) return 0;
    return max(maxDepth(root->left),maxDepth(root->right)) + 1;
}

111. 二叉树的最小深度

Q: 返回给定二叉树的最小深度.

难点, 涉及到如果是NULL但是不是叶子的话, 就不是最小深度. 特化返回叶子的深度.

//这个递归挺难得...
int minDepth(TreeNode* root) {
    if(!root) return 0;
    if(!root->left && root->right) 
        return 1 + minDepth(root->right);
    if(!root->right && root->left)
        return 1 + minDepth(root->left); 
    return 1 + min(minDepth(root->left), minDepth(root->right));
}

257. 二叉树的所有路径

Q: 返回所有叶子到根节点的路径. vector<string>

这个递归感觉有点浪费空间哎....

跟前面一样 判断叶子节点 !left && !right

vector<string> res;
void dfs(TreeNode*node, string s){
    if(s.size()!=0)s+="->";
    s+=to_string(node->val);
    if(!node->left && !node->right) res.push_back(s);
    if(node->left)dfs(node->left,s);
    if(node->right)dfs(node->right,s);
}
vector<string> binaryTreePaths(TreeNode* root) {
    string now = "";
    dfs(root,now);
    return res;
}

572. 另一棵树的子树 ★★★★

等左开KMP再来看吧. leetcode

树上的KMP

树的哈希?

112. 路径总和

坑有点多...

直接递归, 必须判断当前点是不是叶子, 然后当前的值和tagetsum相同不

bool hasPathSum(TreeNode* root, int targetSum) {
    if(!root) return false; 
    if(!root->left && !root->right) return root->val == targetSum;
    return hasPathSum(root->left,targetSum-root->val) || 
        hasPathSum(root->right, targetSum-root->val);
}

113. 路径总和 II

上一题怎么把总数都求出来呢?

简单改一下就好了..

617. 合并二叉树

TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
    if (!t1) return t2; // 这里注意, 不用执着返回t1;
    if (!t2) return t1; 
    t1->val += t2->val;
    t1->left = mergeTrees(t1->left, t2->left);
    t1->right = mergeTrees(t1->right, t2->right);
    return t1;
}

700. BST の 搜索

非常简洁的迭代了吧, 以后可能经常用.

TreeNode* searchBST(TreeNode* root, int val) {
    while(root){
        if(root->val < val) root = root->right;
        else if(val < root->val) root = root->left;
        else return root;
    }
    return NULL;
}

98. 验证是否是BST

带一个判定值的中序遍历, v

long long valnow = LONG_MIN; //这样子有错...
bool isValidBST(TreeNode* root) {
    if(root==NULL) return true;

    bool left = isValidBST(root->left);
    if(valnow < root->val) valnow = root->val;
    else return false;
    bool right = isValidBST(root->right);

    return left&&right;
}
TreeNode * pre = NULL;  //改成存储一个先前节点就好了..
bool isValidBST(TreeNode* root) {
    if(root==NULL) return true;

    bool left = isValidBST(root->left);  
    if(pre!=NULL && pre->val >= root->val) return false; //这里判断
    pre = root;  
    bool right = isValidBST(root->right);

    return left&&right;
}

530. 二叉搜索树的最小绝对差

int res = INT_MAX;
TreeNode* pre = NULL;
void dp(TreeNode* t){
    if(!t) return;
    dp(t->left);
    if(pre!=NULL)
        res = min(res, t->val - pre->val); //直接取较小, 因为搜索树有序.
    pre = t;
    dp(t->right);
}   
int getMinimumDifference(TreeNode* root) {
    dp(root);
    return res;
}

501. BST の 众数

这个先序遍历那里条件不太好写..

vector<int> res;
int maxcnt = 0, cnt = 1, val = INT_MIN;
void dp(TreeNode* root){
    if(!root) return; 
    dp(root->left);
	// 把判断val和cnt的提前, 这样才不会出错 ...
    if(val == root->val) cnt++;
    else cnt = 1, val = root->val;
    if(cnt == maxcnt){res.push_back(val);}
    else if (cnt>maxcnt) {res = {val}, maxcnt=cnt; }

    dp(root->right);
}
vector<int> findMode(TreeNode* root) {
    dp(root); 
    return res;
}

236. 二叉树的最近公共祖先

这个递归真的好绕啊..

代码随录

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    if(root == q || root == p || root == NULL) return root;
    auto left = lowestCommonAncestor(root->left, p, q);
    auto right = lowestCommonAncestor(root->right, p, q);
    if(left != NULL && right != NULL) return root;
    if(left == NULL) return right;
    return left;
}

235. BST の 最近公共祖先

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    // 这种直接判断 是否全大于 是否全小于, 可以避免交换 p 和 q;
    // 因为可能有 p-val < q->val;
    if(root->val <p->val && root->val < q->val) {
        return lowestCommonAncestor(root->right, p, q);
    }else if(root->val > p->val && root->val > q->val) {
        return lowestCommonAncestor(root->left, p, q);
    }else return root;
}

701. BST の 插入

TreeNode* insertIntoBST(TreeNode* root, int val) {
    if(root == NULL) return new TreeNode(val);
    if(root->val < val) 
        root->right = insertIntoBST(root->right, val);
    else
        root->left = insertIntoBST(root->left, val);
    return root;
}

450. BST の 删除★★★★

TreeNode* deleteNode(TreeNode* root, int key) {
    if (root == NULL) return root;
    if (root->val == key){
        if( !root->left && !root->right ) return NULL;
        else if( !root->left) return root->right;
        else if( !root->right) return root->left;
        else{
            auto cur = root->right;
            while(cur->left!=NULL) cur = cur->left;
            cur->left = root->left;
            return root->right;
        }
    }
    if(root->val > key) root -> left = deleteNode(root->left, key);
    if(root->val < key) root -> right = deleteNode(root->right, key);

    return root;
}

669. 修剪BST min和max

已经是最优的代码了... 可是递归的正确性真是个老大难的题.....

TreeNode* trimBST(TreeNode* root, int low, int high) {
    if(root == NULL) return NULL;
    if(root->val<low) return trimBST(root->right, low, high);
    if(root->val>high) return trimBST(root->left, low, high);
    root->right = trimBST(root->right, low, high);
    root-> left = trimBST(root->left, low, high);
    return root;
}

108. 构造BST

这个递归代码..

TreeNode* sortedArrayToBST(vector<int>& nums) {
    return f(nums,0,nums.size()-1);
}
TreeNode* f(vector<int>& nums,int l,int r){
    if(l>r)return NULL;
    int mi = l +(r-l)/2;
    TreeNode* res= new TreeNode(nums[mi]);
    res->left = f(nums,l,mi-1), res->right = f(nums,mi+1, r);
    return res;
}

二叉树总结

这个写的不错.

回溯

  • 组合问题:
  • 切割问题: 字符串有多少种切割方式.
  • 子集问题:
  • 排列问题
  • 棋盘问题.

直接抽象成树的问题. 高度有限的N叉树.

void bt(...){
    if (终止条件) {存放结果;return;}
    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        bt(路径,选择列表); // 递归
        回溯,撤销处理结果
	}
              
}

动态规划

斐波那契:

f[n]=f[n-1]+f[n-2] f[0]=0 f[1]=1

746. 使用最小花费爬楼梯

dp数组表示到i个台阶需要多少话费dp(n+1)

//方法1
int dp[n+1]//: 用dp[i]表示到达第i个台阶花费,不包括第i个
dp[0]=0, dp[1]=0 //前两个可以随便到.
dp[i]=min(dp[i-2]+a[i-2], dp[i-1]+a[i-1]) //这个台阶可以跨1个或者2个上来.
return dp[n];
//方法2
int dp[n];//: 用dp[i]表示上了第i个台阶花费, 包括第i个.
dp[0]=cost[0], dp[1] = cost[1];
dp[i]=min(dp[i-1]+dp[i-2]) + cost[i];
return min(dp[n-1], dp[n-2]);

62. 不同路径

是一个组合问题 \(C^{m-1}_{m+n-2}\)

vector<vector<int>> dp(m,vector<int>(n));
dp[0][0] = 1;
for(int i = 0; i<m; i++){
    for(int j =0; j<n; j++){
        int&now =dp[i][j];
        if(i) now+=dp[i-1][j];
        if(j) now+=dp[i][j-1];
    }
}
return dp[m-1][n-1];

63. 不同路径 II

障碍只要越过去就行了.

int uniquePathsWithObstacles(vector<vector<int>>& a) {
    int n = a.size(), m = a[0].size();
    vector<int> dp(m); // 滚动数组需要注意, 选择 m 而不是 n
    dp[0] = !a[0][0];  // 需要判断初始点情况
    for(int i=0; i<n; i++){
        for(int j=0; j<m; j++){
            if(a[i][j]) {dp[j]=0; continue;} 
            if(j>0) dp[j] += dp[j-1];  // 这里的递推关系还在.
        }
    }
    return dp.back();
}

343. 整数拆分

拆分成至少两个整数.

其实只用考虑两种... j * (i - j)j * dp[i-j]

//答案: 
int integerBreak(int n) {
    vector<int> dp(n+1); //用dp来代表答案: 分开至少两个后.
    dp[2] = 1; // dp[0] dp[1] 都应该是 0;
    for(int i = 3;i<= n;i++){
        for(int j = 1;j<i-1;j++){ //这里j只循环到 i-2 因为 因为 i-j=1 没意义??
            dp[i] = max(dp[i], max((i-j)*j,dp[i-j]*j));
        }
    }
    return dp[n];
}
//原版: 
int integerBreak(int n) {
    vector<int> dp(n+1);
    dp[1] = 1;
    for(int i = 2;i<=n;i++){
        int& ma = dp[i];
        for(int j=1;j<i;j++){
            ma = max(dp[i-j] * max(dp[j],j), ma);
            if(j) ma=max((i-j)*j,ma);
        }
    }
    print(dp);
    return dp[n];
}

96. 不同的二叉搜索树

int numTrees(int n) {
    vector<int> f(n+1);
    f[1] = 1; f[0] = 1;
    for(int i = 2; i<=n; i++){
        for(int j = 0; j<i; j++){
            f[i]+= f[j]*f[i-j-1];
        }
    }
    // print(f);
    return f[n];
}

背包专题.

img

数量n 重量m w[i] v[i]

  1. dp[i][j]0-i的物品中随便取, 放进容量j的背包, 价值总和最大?
    1. 不放物品i dp[i-1][j]
    2. 放物品i dp[i-1][j-w[i]]+v[i]
    3. 两者直接取max就是了.
  2. 初始化 \(\forall i\) dp[i][0]=0 \FORALL j dp[0][j]=...
  3. 遍历方向, 先物品, 再weight, 先weight 再物品都是可以的.

416. 分割等和子集

为什么np问题可以用 动态规划? 时间复杂度和元素大小相关?

背包重量恰好等于整个数组元素的一半.

bool canPartition(vector<int>& nums) {
    int n = nums.size();
    int sum = accumulate(nums.begin(), nums.end(), 0);
    int maxNum = *max_element(nums.begin(), nums.end());
    int target = sum /2;
    if(n<2 || sum&1 || maxNum>target) 
        return false; //如果n<2或者sum是奇数或者max比target大, 都能直接返回false;
    vector<bool> dp(target+1);
    dp[0] = true;
    for(int i = 0; i < n; i++){
        int num = nums[i];
        for(int j = target; j >= num; --j){
            dp[j] = dp[j] || dp[j - num];
        }
    }
    return dp[target];
}

1049. 最后一块石头的重量 II

2-sum 基础上 减去集合可以达到的小于 target的最大值就好了...

int lastStoneWeightII(vector<int>& a) {
    int n = a.size();
    int sum = accumulate(a.begin(), a.end(), 0);
    int target = sum/2;
    vector<int> f(target+1);
    f[0] = true;
    for(int i = 0;i<n;i++){
        for(int j = target;j>=a[i];j--){
            f[j] |= f[j-a[i]];
        }
    }
    for(n = target;n>0;n--){
        if(f[n])break;
    }
    // print(f);
    return sum - 2 * n;
}
posted @ 2022-01-23 14:00  benenzhu  阅读(48)  评论(0)    收藏  举报