LeetCode 111. 二叉树的最小深度
分析
递归分析左右子树.
根据定义, 如果u是叶子结点, 那么深度为1.
如果不是叶子节点, 分成3种情况
1.左子树a, 右子树b 不空, 返回min(f(a), f(b)) + 1
2.a不空, b空, 返回f(a) + 1
3.a空, b不空 返回f(b) + 1
代码
/**
* 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 {
public:
int minDepth(TreeNode* root) {
if (!root) return 0;
if (!root->left && !root->right) return 1; // 如果是叶子节点 返回1
if (root->left && root->right) return min(minDepth(root->left), minDepth(root->right)) + 1;
if (root->left) return minDepth(root->left) + 1;
return minDepth(root->right) + 1;
}
};
LeetCode 112. 路径总和
分析
自上而下递归, 然后判断叶子结点的sum是否==0
代码
/**
* 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 {
public:
bool hasPathSum(TreeNode* root, int sum) {
if (!root) return false;
sum -= root->val;
if (!root->left && !root->right) return !sum;
return root->left && hasPathSum(root->left, sum) || root->right && hasPathSum(root->right, sum);
}
};
LeetCode 113. 路径总和 II
分析
新建一个dfs函数去递归, 不要在原函数中递归
还要新建全局变量res, path
代码
/**
* 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 {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> pathSum(TreeNode* root, int sum) {
if (root) dfs(root, sum);
return res;
}
void dfs(TreeNode* root, int sum){
path.push_back(root->val);
sum -= root->val;
if (!root->left && !root->right){
if (!sum) res.push_back(path);
}else{
if (root->left) dfs(root->left, sum);
if (root->right) dfs(root->right, sum);
}
path.pop_back();
}
};
LeetCode 114. 二叉树展开为链表
分析
代码
/**
* 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 {
public:
void flatten(TreeNode* root) {
while (root){
auto p = root->left;
if (p){
while (p->right) p = p->right;
p->right = root->right;
root->right = root->left;
root->left = NULL;
}
root = root->right;
}
}
};
LeetCode 115. 不同的子序列
分析
闫氏dp分析法.
f[i][j]
表示s[1~i]所有前i个字符和t前j个字符匹配的子序列
注意 右边是无条件可以选择的情况, 所以f[i][j] = f[i - 1][j]
中间结果会爆int, 用longlong存储
代码
class Solution {
public:
int numDistinct(string s, string t) {
typedef unsigned long long ULL;
int n = s.size(), m = t.size();
s = ' ' + s, t = ' ' + t;
vector<vector<ULL>> f(n + 1, vector<ULL>(m + 1));
for (int i = 0; i <= n; i ++ ) f[i][0] = 1;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ ) {
f[i][j] = f[i - 1][j];
if (s[i] == t[j]) f[i][j] += f[i - 1][j - 1];
}
return f[n][m];
}
};
116. 填充每个节点的下一个右侧节点指针
分析
可以发现指针, 从前指向后, 第1层就是全部是第一层的节点, 第2层全部都是第2层的节点…
所以按照宽搜的思路来做就可以了, 宽搜的过程中赋下Next指针就可以了
宽搜本来是需要队列的, 但是这题中有next指针, 所以宽搜的时候next指针也是可以省掉的
宽搜的时候怎么去初始化next的值呢, 分情况讨论
比如2这个点
左儿子的next = 右儿子, 但是右儿子的next怎么处理呢, 应该指向下一个点的左儿子, 2->right ->next = 2->next->left
当然还需特判下有没有下一个点, 每层最后一个点没有下一个点
code
右儿子处理的时候, 需要保证当前父亲节点右边还有节点
因为每个节点next初始化为NULL, 所以如果没有, 那就默认即可, 不用建立
简单来说, 就是在当前层, 需要对下一层的next指针进行赋值, 但是需要下一层得先存在, 所以刚开始得while(下一层存在root->left)
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() : val(0), left(NULL), right(NULL), next(NULL) {}
Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
Node* connect(Node* root) {
if (!root) return root;
auto source = root; // 存一下根, 返回用
// 当有左儿子时, 说明有下一层
while (root->left){
// 使用next遍历这一层的每个节点p, 来处理当前层的链接关系
for (auto p = root; p; p = p->next){
p->left->next = p->right; // 左儿子指向 右儿子
if (p->next) p->right->next = p->next->left; // 右儿子指向的时候, 需要先保证p的右边还有东西
}
root = root->left;// 走向下一层
}
return source;
}
};
117. 填充每个节点的下一个右侧节点指针 II
分析
扩展题, 如果按照上一题的做法, 让每个点的左儿子指向右儿子, 然后右儿子指向next点的左儿子,
然后这题, 比如下图, 7指向谁呢, 不知道诶, 因为对于4而言, 4可能没有右儿子, 4的next可能都没有儿子
所以不能按照上一题的思路做
需要在遍历前一层的时候, 将后一层的next建立出来, 遍历的时候, 由于不知道每个点是否存在左右儿子, 自己去维护下一层的单链表
需要记录下一层的头节点
假如新点的时候, 是让当前链表的末尾的next指向当前点, 因此还需要记录一个信息尾节点
每次迭代的时候, 只看两个变量, 因此也是O(1)
简单来说, 就是循环当前层, 然后对下一层建立单链表, 因为不知道下一层存不存在, 所以建立单链表的时候得判断 左儿子, 右儿子的情况
code
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() : val(0), left(NULL), right(NULL), next(NULL) {}
Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
Node* connect(Node* root) {
if (!root) return root;
auto cur = root;
while (cur){
auto head = new Node(-1);
auto tail = head;
for (auto p = cur; p; p = p->next){
// 当前层是p, 建立下一层的next指向
if (p->left) tail = tail->next = p->left;
if (p->right) tail = tail->next = p->right;
}
cur = head->next; // 跳到下一层
}
return root;
}
};
118. 杨辉三角
分析
递归下就可以了,
注意i从0开始, 所以每一层数组长度为i + 1
然后0, i位置已经定了
所以每一层从[1, i)循环
code
class Solution {
public:
vector<vector<int>> generate(int n) {
vector<vector<int>> res;
for (int i = 0; i < n; i ++ ){
vector<int> level(i + 1);
level[0] = level[i] = 1;
for (int j = 1; j < i; j ++ )
level[j] = res[i - 1][j - 1] + res[i - 1][j];
res.push_back(level);
}
return res;
}
};
LeetCode 119. 杨辉三角 II
分析
可以发现f[i][j] = f[i - 1][j - 1] + f[i - 1][j]
所以可以用一个滚动数组
所以第n行应该存到 n % 2行, n % 2 = n & 1
code
先不用滚动数组写
class Solution {
public:
vector<int> getRow(int n) {
vector<vector<int>> f(n + 1, vector<int>(n + 1));
for (int i = 0; i <= n; i ++ ){
f[i][0] = f[i][i] = 1;
for (int j = 1; j < i; j ++ )
f[i][j] = f[i - 1][j - 1] + f[i - 1][j];
}
return f[n];
}
};
&1转化为滚动数组
class Solution {
public:
vector<int> getRow(int n) {
vector<vector<int>> f(2, vector<int>(n + 1));
for (int i = 0; i <= n; i ++ ){
f[i & 1][0] = f[i & 1][i] = 1;
for (int j = 1; j < i; j ++ )
f[i & 1][j] = f[i - 1 & 1][j - 1] + f[i - 1 & 1][j];
}
return f[n & 1];
}
};
120. 三角形最小路径和
分析
自下向上考虑, 没有边界问题
联动题
AcWing寒假每日一题 数字三角形
code
注意从倒数第2行开始算; 如果从倒数第1行 (n - 1), 那么递推式i + 1会越界
并且注意第i行(i从0开始)有 i + 1个数,比如第0行, 1个数, 第1行, 2个数, 第2行, 3个数, 因此j循环范围[0, i]
class Solution {
public:
int minimumTotal(vector<vector<int>>& t) {
int n = t.size();
for (int i = n - 2; i >= 0; i -- )
for (int j = 0; j <= i; j ++ ){
t[i][j] = t[i][j] + min(t[i + 1][j], t[i + 1][j + 1]);
}
return t[0][0];
}
};