目录
翻转二叉树
解法:递归
(后序遍历,如果前序遍历要保留节点交换才行,也就是dfs写法)
最后最左子树的叶子节点left和最右子树的叶子结点right
当前节点的左右节点分别是right,left
返回当前节点
cpp
class Solution
{
public:
void dfs(TreeNode *root)
{
if (root == nullptr)
return;
TreeNode *left = root->left;
TreeNode *right = root->right;
root->left = right;
root->right = left;
dfs(root->left);
dfs(root->right);
}
TreeNode *invertTree(TreeNode *root)
{
// dfs(root);
// return root;
if (root == nullptr)
return root;
TreeNode *left = invertTree(root->left);
TreeNode *right = invertTree(root->right);
root->left = right;
root->right = left;
return root;
}
};
对称二叉树
解法:递归
传入左子树和右子树
如果当前节点都为空,返回 true;
有一个节点为空或者val值不相等,返回 false;
左子树的左节点与右子树的右节点比较,左子树的右节点与右子树的左节点比较
cpp
class Solution
{
public:
bool dfs(TreeNode *root1, TreeNode *root2)
{
if (root1 == nullptr && root2 == nullptr)
return true;
if ((root1 == nullptr || root2 == nullptr) ||
root1->val != root2->val)
return false;
return dfs(root1->left, root2->right) && dfs(root1->right, root2->left);
}
bool isSymmetric(TreeNode *root)
{
return dfs(root->left, root->right);
}
};
从前序和中序遍历序列构建二叉树
解法:递归
根据前序遍历第一个节点为根节点为出发点
在中序遍历中找到该节点,就可以把区间分为左子树区间和右子树区间
同时得到左子树节点个数,也可以把前序遍历区间分为左子树区间和右子树区间,这样就可以通过左右子树区间递归构建左右子树,从而构建出完整二叉树出来
cpp
class Solution
{
public:
TreeNode *dfs(vector<int> &root1, int begin1, int end1, vector<int> &root2, int begin2, int end2)
{
if (begin1 > end1)
return nullptr;
// if(begin2>end2) return nullptr;
// if(begin1 == end1) return new TreeNode(root1[begin1]);
// if(begin2 == end2) return new TreeNode(root2[begin2]);
int val = root1[begin1];
TreeNode *root = new TreeNode(val);
for (int i = begin2; i <= end2; i++)
{
if (root2[i] == val)
{
int leftNodeCnt = i - 1 - begin2 + 1;
root->left = dfs(root1, begin1 + 1, begin1 + 1 + leftNodeCnt - 1, root2, begin2, i - 1);
root->right = dfs(root1, begin1 + leftNodeCnt + 1, end1, root2, i + 1, end2);
}
}
return root;
}
TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder)
{
return dfs(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1);
}
};
从中序与后续遍历序列构建二叉树
上题思路一样的
cpp
class Solution
{
public:
TreeNode *dfs(vector<int> &root1, int begin1, int end1, vector<int> &root2, int begin2, int end2)
{
if (begin1 > end1)
return nullptr;
int rootVal = root2[end2];
TreeNode *root = new TreeNode(rootVal);
for (int i = begin1; i <= end1; i++)
{
if (root1[i] == rootVal)
{
int leftCnt = i - 1 - begin1 + 1;
root->left = dfs(root1, begin1, i - 1, root2, begin2, begin2 + leftCnt - 1);
root->right = dfs(root1, i + 1, end1, root2, begin2 + leftCnt, end2 - 1);
break;
}
}
return root;
}
TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder)
{
return dfs(inorder, 0, inorder.size() - 1, postorder, 0, postorder.size() - 1);
}
};
填充每个节点的下一个右侧节点指针
解法1:递归
使用数组prev来保存每一层的第一个节点 -> prev.size() == 当前深度depth
遇到其它节点node时,就把 prev[depth] 的next进行更新,prev[depth] 位置更新为 node
然后不断递归左右子树....
cpp
class Solution
{
public:
vector<Node *> depthOne;
void dfs(Node *root, int depth)
{
if (root == nullptr)
return;
if (depthOne.size() == depth)
{
depthOne.push_back(root);
}
else
{
depthOne[depth]->next = root;
depthOne[depth] = root;
}
dfs(root->left, depth + 1);
dfs(root->right, depth + 1);
}
Node *connect(Node *root)
{
dfs(root, 0);
return root;
}
};
解法2:bfs
使用二叉树的层序遍历,来把每层的next进行设置
cpp
class Solution
{
public:
Node *connect(Node *root)
{
if (root == nullptr)
return root;
queue<Node *> q;
q.push(root);
Node head;
while (!q.empty())
{
Node *tail = &head;
int sz = q.size();
while (sz--)
{
Node *node = q.front();
q.pop();
if (node->left)
{
tail->next = node->left;
tail = node->left;
q.push(node->left);
}
if (node->right)
{
tail->next = node->right;
tail = node->right;
q.push(node->right);
}
}
tail->next = nullptr;
}
return root;
}
};
解法3:模拟
使用链表模拟层序遍历
- 使用哨兵位头节点head的方法储存下一层链表节点
- 初始化 cur,指向 root
- 循环遍历每层节点:
- 循环遍历cur是否为空:把 cur 左节点和右节点(不为空)加入到head后面,更新 cur 为cur->next(这里可以直接更新,因为循环当层节点之前的next已经初始化好了)
- 为空代表当前层遍历完成,更新cur为下一层的头节点,也就是head->next,这里还要顺便把head->next 置空(因为循环遍历到倒数第二层节点时,最后一层节点是更新完了的,不置空会出现死循环)
cpp
class Solution
{
public:
Node *connect(Node *root)
{
if (root == nullptr)
return root;
Node head;
Node *cur = root;
while (cur)
{
// tail链接的是下一层节点的next
Node *tail = &head;
while (cur)
{
if (cur->left)
{
tail->next = cur->left;
tail = cur->left;
}
if (cur->right)
{
tail->next = cur->right;
tail = cur->right;
}
cur = cur->next; // 当前层的每个节点的next已经连接好了的
}
// 更新下一层的链表头节点
cur = head.next;
// 不更新最后一层会死循环
head.next = nullptr;
}
return root;
}
};
二叉树展开为链表
解法1:头插法
借助变量记录当前的尾节点,与当前节点进行头插,最后更新尾节点
cpp
class Solution
{
public:
TreeNode *dfs(TreeNode *root)
{
if (root == nullptr)
return root;
TreeNode *leftTail = dfs(root->left);
TreeNode *retTail = dfs(root->right);
if (leftTail)
leftTail->right = root->right;
if (root->left)
root->right = root->left;
root->left = nullptr;
if (retTail)
return retTail;
if (leftTail)
return leftTail;
return root;
}
void flatten(TreeNode *root)
{
dfs(root);
}
};
解法2:递归
比如例子1:
从root开始递归左子树,左子数按照 2->3->4 的顺序链接完成后,返回leftTail = 4;
递归右子树也是同理,返回retTail = 6(注意这里返回6是因为:可能root不是当前根节点,递归完成后要向上返回)
按照三步:节点4 链接 root 右节点,root右节点 链接 root 左节点,root 左节点为空
返回时:retTail 不为空返回;为空考虑 leftTail:leftTail 不为空返回;都为空返回 root
cpp
class Solution
{
public:
TreeNode *dfs(TreeNode *root)
{
if (root == nullptr)
return root;
TreeNode *leftTail = dfs(root->left);
TreeNode *retTail = dfs(root->right);
if (leftTail)
leftTail->right = root->right;
// 如果左节点不存在,root有节点保持不变即可
if (root->left)
root->right = root->left;
root->left = nullptr;
// 右子树最右节点不为空时返回
// 为空考虑左子树的最右节点
// 否则就返回根节点root
if (retTail)
return retTail;
if (leftTail)
return leftTail;
return root;
}
void flatten(TreeNode *root)
{
dfs(root);
}
};
路径总和
解法:递归
二叉树的前序遍历,在当前根节点上判断:
当前 targetSum 减去根节点的值是否等于0 且 当前节点是不是叶子节点,是就找到了答案,返回true
在左子树和右子树中找任意一种答案就可以返回true了
边界:当节点为空时返回 false(千万不要因为 targetSum 小于0时返回 false,因为 targetSum 可以是 负数啊)
cpp
class Solution
{
public:
bool hasPathSum(TreeNode *root, int targetSum)
{
if (root == nullptr)
return false;
if ((targetSum - root->val) == 0 && root->left == nullptr && root->right == nullptr)
return true;
return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
}
};
求根节点到叶子节点路径之和
解法:递归
依旧前序遍历,dfs 返回当前叶子节点的值,tmp 代表当前节点对应的值
当遍历到叶子节点时,tmp * 10 + 叶子节点的值 就是当前叶子节点的值,结果返回
否则就要递归左子树与右子树,将它们的叶子节点值之和进行返回
cpp
class Solution
{
public:
// int sum = 0;
int dfs(TreeNode *root, int tmp)
{
if (root == nullptr)
{
return 0;
}
if (root->left == nullptr && root->right == nullptr)
{
return (tmp * 10 + root->val);
}
return dfs(root->left, tmp * 10 + root->val) +
dfs(root->right, tmp * 10 + root->val);
}
int sumNumbers(TreeNode *root)
{
return dfs(root, 0);
}
};
二叉树中的最大路径和
解法:递归
dfs 返回的单链(没有拐弯的直径)最大路径和,与 0 取max(防止全是负数不选的情况)
使用变量 ret 枚举二叉树中的最大路径和,也就是左右子树单链加上当前节点的值
cpp
class Solution
{
public:
int ret = INT_MIN;
int dfs(TreeNode *root)
{
if (root == nullptr)
return 0;
// left 为 0 说明左子树不选
int left = max(dfs(root->left), 0);
int right = max(dfs(root->right), 0);
ret = max(ret, left + root->val + right);
return max(left, right) + root->val;
}
int maxPathSum(TreeNode *root)
{
dfs(root);
return ret;
}
};
完全二叉树的节点个数
解法:递归
二叉树的前序遍历枚举节点个数
cpp
class Solution {
public:
int countNodes(TreeNode* root) {
if(root == nullptr) return 0;
return countNodes(root->left) + countNodes(root->right) + 1;
}
};
二叉搜索树迭代器
解法:递归
中序遍历递归后的结果保存在栈/队列中,依次取出即可
cpp
class BSTIterator
{
public:
queue<TreeNode *> q;
void initQueue(TreeNode *root)
{
if (root == nullptr)
return;
initQueue(root->left);
q.push(root);
initQueue(root->right);
}
BSTIterator(TreeNode *root)
{
initQueue(root);
}
int next()
{
int tmp = q.front()->val;
q.pop();
return tmp;
}
bool hasNext()
{
if (q.size())
return true;
return false;
}
};
二叉树的最近公共祖先

cpp
class Solution
{
public:
TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q)
{
if (root == nullptr || root == p || root == q)
{
return root;
}
TreeNode *lefFind = lowestCommonAncestor(root->left, p, q);
TreeNode *rightFind = lowestCommonAncestor(root->right, p, q);
// p 和 q 各自分布在左右子树
if ((lefFind != nullptr) && (rightFind != nullptr))
{
return root;
}
// 右子树有结果返回,即使当前只有一个节点
if (lefFind == nullptr)
return rightFind;
if (rightFind == nullptr)
return lefFind;
return nullptr;
}
};
二叉树的右视图
解法:bfs
使用bfs:队列 + 两层循环的方式作一次层序遍历,每次外层循环中队列的最后一个节点就是答案,收集后进行返回
cpp
class Solution
{
public:
vector<int> rightSideView(TreeNode *root)
{
vector<int> ret;
if (root == nullptr)
return ret;
queue<TreeNode *> q;
q.push(root);
while (!q.empty())
{
int sz = q.size();
while (sz--)
{
TreeNode *top = q.front();
q.pop();
if (sz == 0)
{
ret.push_back(top->val);
}
if (top->left)
q.push(top->left);
if (top->right)
q.push(top->right);
}
}
return ret;
}
};
二叉树的层平均值
解法:bfs
层序遍历求每层(队列)平均值
cpp
class Solution
{
public:
vector<double> averageOfLevels(TreeNode *root)
{
vector<double> ret;
if (root == nullptr)
return ret;
queue<TreeNode *> q;
q.push(root);
while (!q.empty())
{
int sz = q.size();
double size = q.size(), sum = 0;
while (sz--)
{
TreeNode *top = q.front();
q.pop();
sum += top->val;
if (sz == 0)
{
ret.push_back(sum / size);
}
if (top->left)
q.push(top->left);
if (top->right)
q.push(top->right);
}
}
return ret;
}
};
二叉树的层序遍历
解法:dfs
演都不演了,直接告诉你要求层序遍历
cpp
class Solution
{
public:
vector<vector<int>> levelOrder(TreeNode *root)
{
vector<vector<int>> ret;
if (root == nullptr)
return ret;
queue<TreeNode *> q;
q.push(root);
while (!q.empty())
{
int sz = q.size();
vector<int> tmp;
while (sz--)
{
TreeNode *top = q.front();
q.pop();
tmp.push_back(top->val);
if (top->left)
q.push(top->left);
if (top->right)
q.push(top->right);
}
ret.push_back(tmp);
}
return ret;
}
};
二叉树的锯齿形层序遍历
解法:bfs
使用 tmp 来进行标记,如果当前需要从右往左收集节点,tmp为真,将收集的节点进行反转后保存
cpp
class Solution
{
public:
vector<vector<int>> zigzagLevelOrder(TreeNode *root)
{
vector<vector<int>> ret;
if (root == nullptr)
return ret;
queue<TreeNode *> q;
q.push(root);
bool tmp = false;
while (!q.empty())
{
int sz = q.size();
vector<int> ret1;
while (sz--)
{
TreeNode *top = q.front();
q.pop();
ret1.push_back(top->val);
if (top->left)
q.push(top->left);
if (top->right)
q.push(top->right);
}
if (tmp)
{
reverse(ret1.begin(), ret1.end());
}
ret.push_back(ret1);
tmp = !tmp;
}
return ret;
}
};
二叉搜索树的最小绝对差
解法:dfs
利用二叉搜索树中序遍历的有序性进行递归,收集每两个数之间的差值的最小值返回
cpp
class Solution
{
public:
int prev = -1, ret = INT_MAX;
void dfs(TreeNode *root)
{
if (root == nullptr)
return;
dfs(root->left);
if (prev != -1)
{
ret = min(ret, abs(root->val - prev));
}
prev = root->val;
dfs(root->right);
}
int getMinimumDifference(TreeNode *root)
{
dfs(root);
return ret;
}
};
二叉搜索树中第k小的元素
解法:递归
使用变量记录中序遍历的第几个,当 cnt == 0时使用 ret 记录答案,递归完成之后返回 ret;
但也可以不使用 ret,让 dfs 返回值作为答案返回:
当 root 为空时,返回 -1 (题目明确val为正数)
递归左子树和右子树保存结果,当其中一方返回结果不为-1时直接返回
当cnt为0时也是直接返回
cpp
class Solution
{
public:
int ret, cnt;
void dfs(TreeNode *root)
{
if (root == nullptr)
return;
dfs(root->left);
if ((--cnt) == 0)
{
ret = root->val;
}
dfs(root->right);
}
int kthSmallest(TreeNode *root, int k)
{
cnt = k;
dfs(root);
return ret;
}
};
class Solution
{
public:
int cnt;
int dfs(TreeNode *root)
{
if (root == nullptr)
{
return -1;
}
int left = dfs(root->left);
if (left != -1)
{
return left;
}
cnt -= 1;
if(cnt == 0)
{
return root->val;
}
// 不管有没有结果都是返回右子树的返回值
return dfs(root->right);
}
int kthSmallest(TreeNode *root, int k)
{
cnt = k;
return dfs(root);
}
};
验证二叉搜索树
解法:递归
利用中序遍历有序特性,使用变量标记上一个节点的值进行比较,中序遍历过程中都是 prev < root->val,则证明是一颗二叉搜索树
cpp
class Solution
{
public:
long long prevVal = LONG_MIN;
bool isValidBST(TreeNode *root)
{
if (root == nullptr)
{
return true;
}
bool leftBool = isValidBST(root->left);
if (!leftBool)
{
return false;
}
if (prevVal >= root->val)
{
return false;
}
prevVal = root->val;
return isValidBST(root->right);
}
};
岛屿数量
解法:递归
遍历岛屿,找到'1'且没有标记过,从该位置开始上下左右进行递归遍历,把遇到的所有'1'使用二维数组进行标记...递归的次数就是岛屿的数量
cpp
class Solution {
public:
bool vis[301][301] = {false};
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
int n,m,ret=0;
void dfs(vector<vector<char>>& grid, int i, int j)
{
vis[i][j] = true;
for(int k = 0; k < 4; k++)
{
int x = dx[k] + i,y = dy[k] + j;
if(x >=0 && x < n && y >=0 && y < m && !vis[x][y] && grid[x][y] == '1')
{
dfs(grid, x, y);
}
}
}
int numIslands(vector<vector<char>>& grid) {
n = grid.size(),m = grid[0].size();
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
if(grid[i][j] == '1' && !vis[i][j])
{
ret++;
dfs(grid, i, j);
}
}
}
return ret;
}
};
以上便是全部内容,有问题欢迎在评论区指正,感谢观看!