目录
一、二叉树三种遍历(深度优先搜索)
Ⅰ、前序遍历(中左右)
①、递归遍历
题目链接:二叉树前序遍历
我们只需要确定中左右的处理逻辑,然后中、左、右递归处理即可。
void backtracking(TreeNode* root, vector<int>& res)
{
if(root == NULL) return;
res.push_back(root -> val);
if(root ->left) backtracking(root ->left, res);
if(root ->right) backtracking(root ->right, res);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
if(root == NULL) return res;
backtracking(root, res);
return res;
}
②、迭代遍历
题目和上面的题目是一样的。
此处我们利用了栈的特点,实现了二叉树的迭代遍历,需要特别注意的是,当我们遍历的时候,入栈顺序应该是先右后左,栈有后进先出的特点。这样,我们就可以保证先存储左,后右,实现前序遍历。
ector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
if(root == NULL) return res;
stack<TreeNode*> st;
st.push(root);
while(!st.empty())
{
TreeNode* tem = st.top();
st.pop();
res.push_back(tem ->val);
if(tem ->right) st.push(tem->right);
if(tem ->left) st.push(tem ->left);
}
return res;
}
Ⅱ、后序遍历(左右中)
①、递归遍历
题目链接:二叉树后序遍历
遍历顺序左右中,切记要清楚每种遍历的顺序,这很重要。
void backtracking(TreeNode* root, vector<int>& res)
{
if(root == NULL) return;
if(root ->left) backtracking(root ->left, res);
if(root ->right) backtracking(root ->right, res);
res.push_back(root ->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
if(root == NULL) return res;
backtracking(root, res);
return res;
}
②、迭代遍历
题目与上面一样。
我们根据中序遍历的代码为中左右,后序遍历为左右中,那么我们只需要先写出中右左,然后reverse一下就得到左右中。
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
if(root == NULL) return res;
stack<TreeNode*> st;
st.push(root);
while(!st.empty())
{
TreeNode* tem = st.top();
st.pop();
res.push_back(tem ->val);
if(tem ->left) st.push(tem ->left);
if(tem ->right) st.push(tem ->right);
}
reverse(res.begin(), res.end());
return res;
}
Ⅲ、中序遍历(左中右)
①、递归遍历
题目链接:二叉树中序遍历
切记遍历顺序。左右中
void backtracking(TreeNode* root, vector<int>& res)
{
if(root == NULL) return;
if(root ->left) backtracking(root ->left, res);
res.push_back(root ->val);
if(root ->right) backtracking(root ->right, res);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
if(root == NULL) return res;
backtracking(root, res);
return res;
}
②、迭代遍历
题目与上面一样。
此处我们借用了一个指针,从而当指针不为空,一直向左储存,当为空时,说明触底了,然后处理左值并且向右储存。
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
if(root == NULL) return res;
stack<TreeNode*> st;
TreeNode* tem = root;
while(!st.empty() || tem != NULL)
{
if(tem != NULL)
{
st.push(tem);
tem = tem ->left;
}
else {
tem = st.top();
st.pop();
res.push_back(tem ->val);
tem = tem ->right;
}
}
return res;
}
二、二叉树层序遍历(广度优先搜索)
①、例题1
题目链接:二叉树层序遍历
该方法使用的是队列,然后套用一个for循环。这样可以从左到右的数据存储起来。
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if(root == NULL) return res;
queue<TreeNode*> que;
que.push(root);
while(!que.empty())
{
int sz = que.size();
vector<int> nums;
for(int i = 0 ;i < sz; i++){
TreeNode* tem = que.front();
que.pop();
nums.push_back(tem ->val);
if(tem ->left) que.push(tem ->left);
if(tem ->right) que.push(tem ->right);
}
res.push_back(nums);
}
return res;
}
②、例题2
链接:二叉树层序遍历Ⅱ
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> result;
queue<TreeNode*> que;
if(root!=NULL) que.push(root);
while(!que.empty())
{
int size = que.size();
vector<int> p;
for(int i = 0;i<size;i++)
{
TreeNode* tem = que.front();
que.pop();
p.push_back(tem->val);
if(tem->left) que.push(tem->left);
if(tem->right) que.push(tem->right);
}
result.push_back(p);
}
reverse(result.begin(),result.end());
return result;
}
③、例题3
题目链接:二叉树右视图
vector<int> rightSideView(TreeNode* root) {
vector<int> result;
queue<TreeNode*> que;
if(root == NULL )return result;
que.push(root);
while(!que.empty())
{
int sz = que.size();
result.push_back(que.back() ->val);
for(int i = 0; i<sz; i++){
TreeNode* tem = que.front();
que.pop();
if(tem ->left) que.push(tem ->left);
if(tem ->right) que.push(tem ->right);
}
}
return result;
}
④、例题4
题目链接:填充右侧节点
Node* connect(Node* root) {
queue<Node*> que;
if(root == NULL) return root;
que.push(root);
while(!que.empty())
{
int size = que.size();
for(int i = 0;i<size;i++){
Node* tem = que.front();
que.pop();
if(tem->left) que.push(tem->left);
if(tem->right) que.push(tem->right);
if(i == size-1){
tem->next = NULL;
}
else{
tem->next = que.front();
}
}
}
return root;
}
三、二叉树的属性
Ⅰ、对称二叉树
题目链接:对称二叉树
这里使用递归遍历,维持的原则上两边树的外侧和外侧比较,内侧和内侧比较,然后总体汇总true和false情况,最后返回。
bool backtracking(TreeNode* leftnode, TreeNode* rightnode)
{
if(leftnode == NULL && rightnode == NULL) return true;
else if(leftnode == NULL && rightnode != NULL) return false;
else if(leftnode != NULL && rightnode == NULL) return false;
else if(leftnode ->val != rightnode ->val) return false;
bool left = backtracking(leftnode ->left, rightnode ->right);
bool right = backtracking(leftnode ->right, rightnode ->left);
return left && right;
}
bool isSymmetric(TreeNode* root) {
if(root == NULL) return true;
return backtracking(root ->left, root ->right);
}
Ⅱ、二叉树的最大深度
题目链接:二叉树最大深度
①、层序遍历解法
该解法,就是利用了层序遍历每次遍历完一层,result加1,然后统计总层数,也就是最大深度。
int maxDepth(TreeNode* root) {
if(root==NULL) return 0;
queue<TreeNode*> que;
que.push(root);
int result = 0;
while(!que.empty())
{
int sz = que.size();
for(int i = 0; i<sz; i++){
TreeNode* tem = que.front();
que.pop();
if(tem ->left) que.push(tem ->left);
if(tem ->right) que.push(tem ->right);
}
result ++;
}
return result;
}
②、递归遍历解法
本题采用前序遍历(中左右)解法。res每次++表示的是没到一层,记录当前层数。tem定义为全局变量,tem用于记录历史最大层数。最后的res--是回溯的处理方式,因为左右都遍历完了,所以当前层数减一。
int tem = 0;
void backtracking(TreeNode* root, int& res)
{
if(root == NULL) return;
res ++;
tem = max(res, tem);
backtracking(root ->left, res);
backtracking(root ->right, res);
res --;
}
int maxDepth(TreeNode* root) {
int res = 0;
backtracking(root, res);
return tem;
}
Ⅲ、二叉树最小深度
该代码的重点是递归中判断NULL的条件有所不同。同时分别记录树左右两边的深度,然后选取两者中的最小值 + 1。
int backtracking(TreeNode* root)
{
if(root == NULL) return 0;
int leftdepth = backtracking(root ->left);
int rightdepth = backtracking(root ->right);
if(root ->left == NULL && root ->right != NULL) return 1 + rightdepth;
if(root ->left != NULL && root ->right == NULL) return 1 + leftdepth;
return 1 + min(leftdepth, rightdepth);
}
int minDepth(TreeNode* root) {
if(root == NULL) return 0;
return backtracking(root);
}
Ⅳ、完全二叉树的节点个数
题目链接:完全二叉树的节点个数
①、层序遍历
int countNodes(TreeNode* root) {
if(root == NULL) return 0;
stack<TreeNode*> st;
st.push(root);
int res = 0;
while(!st.empty())
{
int sz = st.size();
for(int i = 0; i<sz; i++){
TreeNode* tem = st.top();
res ++;
st.pop();
if(tem ->left) st.push(tem ->left);
if(tem ->right) st.push(tem ->right);
}
}
return res;
}
②、递归遍历
确定遍历顺序是后序遍历(左右中)。然后左右逻辑执行完之后就一个节点结束。res++。最后
res就表示最终结果。
void backtrackint(TreeNode* root, int& res)
{
if(root == NULL) return;
backtrackint(root ->left, res);
backtrackint(root ->right, res);
res ++;
}
int countNodes(TreeNode* root) {
if(root == NULL) return 0;
int res = 0;
backtrackint(root, res);
return res;
}
Ⅴ、平衡二叉树
题目链接:平衡二叉树
平衡二叉树定义:左右两个子树的高度差不高于1。
本题我们要知道遍历,为后续遍历,同时要清楚平衡二叉树要满足的所有地方的子树差都小于等于1,所以有一个不符合,我们就直接返回-1。
int backtracking(TreeNode* root)
{
if(root == NULL) return 0;
int leftdepth = backtracking(root ->left);
if(leftdepth == -1) return -1;
int rightdepth = backtracking(root ->right);
if(rightdepth == -1) return -1;
return abs(rightdepth - leftdepth) > 1 ? -1 : 1 + max(rightdepth, leftdepth);
}
bool isBalanced(TreeNode* root) {
if(root == NULL) return true;
return backtracking(root) != -1;
}
Ⅵ、二叉树所有路径
题目链接:二叉树所有路径
使用递归同时进行回溯的细节处理,就可以很好的完成本题。
void backtracking(TreeNode* root, vector<string>& res, string path)
{
path += to_string(root ->val);
if(root ->left == NULL && root ->right == NULL){
res.push_back(path);
return;
}
if(root ->left){
path += "->";
backtracking(root ->left, res, path);
path.pop_back();path.pop_back();
}
if(root ->right){
path += "->";
backtracking(root ->right, res, path);
path.pop_back();path.pop_back();
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
string path;
if(root == NULL) return res;
backtracking(root, res, path);
return res;
}
Ⅶ、左叶子之和
题目链接:左叶子之和
此处注意左叶子并非左侧节点。左叶子必须满足左侧节点不为空,同时左侧节点的左右两边均为空。
int backtracking(TreeNode* root)
{
if(root == NULL ) return 0;
if(root ->left == NULL && root ->right == NULL) return 0;
int leftnode = backtracking(root ->left);
if(root -> left != NULL && root ->left ->left == NULL && root ->left ->right == NULL){
leftnode = root ->left ->val;
}
int rightnode = backtracking(root ->right);
return leftnode + rightnode;
}
int sumOfLeftLeaves(TreeNode* root) {
if(root == NULL) return 0;
return backtracking(root);
}
Ⅷ、找树左下角的值
题目链接:找树左下角的值
①、层序遍历
此处思路为每次层序遍历时候,i == 0的时候永远是每一行最左边的值,这样,我们就不用担心最左边但不是最后一层的问题了。
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
if(root==NULL) return 0;
if(root->left==NULL&&root->right==NULL) return root->val;
que.push(root);
int result = 0;
while(!que.empty())
{
int size = que.size();
for(int i = 0;i<size;i++)
{
TreeNode* tem = que.front();
que.pop();
if(tem->left) que.push(tem->left);
if(tem->right) que.push(tem->right);
if(i==0) result = tem->val;
}
}
return result;
}
②、递归遍历
res全局变量,为最后结果,maxdepth记录最大深度,为每次判断时候的条件做准备。
int res, maxdepth = -1;
void backtracking(TreeNode* root, int depth)
{
if(root ->left == NULL && root ->right == NULL)
{
if(depth > maxdepth){
maxdepth = depth;
res = root ->val;
}
}
if(root ->left){
depth++;
backtracking(root ->left, depth);
depth --;
}
if(root ->right){
depth++;
backtracking(root ->right, depth);
depth--;
}
}
int findBottomLeftValue(TreeNode* root) {
if(root == NULL) return 0;
backtracking(root, 0);
return res;
}
Ⅸ、路径总和
题目链接:路径总和
①、目标值减法求解
bool backtracking(TreeNode* root, int count)
{
if(root ->left == NULL && root ->right == NULL && count == 0) return true;
if(root ->left == NULL && root ->right == NULL) return false;
if(root ->left){
count -= root ->left ->val;
if(backtracking(root ->left, count)) return true;
count += root ->left ->val;
}
if(root ->right){
count -= root ->right ->val;
if(backtracking(root ->right, count)) return true;
count += root ->right ->val;
}
return false;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if(root == NULL) return false;
return backtracking(root, targetSum - root ->val);
}
②、路径加法求解
此法就是通过左右两边的真值情况,如果有一个为true,直接全部返回true,同时每次遇到一个叶子节点就直接使用for循环求解,然后直接返回相等情况。
bool get_sum(TreeNode* cur,vector<int>& num,int targetSum)
{
num.push_back(cur->val);
if(cur->left==NULL&&cur->right==NULL) {
int tem = 0;
for(int i = 0;i<num.size();i++){
tem+=num[i];
}
return tem == targetSum;
}
if(cur->left){
bool l = get_sum(cur->left,num,targetSum);
if(l == true) return l;
num.pop_back();
}
if(cur->right){
bool r = get_sum(cur->right,num,targetSum);
if(r==true) return r;
num.pop_back();
}
return false;
}
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
vector<int> num;
if(root==NULL) return false;
if(root->left==NULL&&root->right==NULL) return root->val==targetSum;
return get_sum(root,num,targetSum);
}
};
四、二叉树的修改与构造
1、翻转二叉树
题目链接:翻转二叉树
切记使用swap()函数的时候,交换的是节点,而不是节点的值。
①、递归法
TreeNode* invertTree(TreeNode* root) {
if(root == NULL) return root;
swap(root ->left, root ->right);
invertTree(root ->left);
invertTree(root ->right);
return root;
}
②、迭代法
TreeNode* invertTree(TreeNode* root) {
if (root == NULL) return root;
stack<TreeNode*> st;
st.push(root);
while(!st.empty()) {
TreeNode* node = st.top(); // 中
st.pop();
swap(node->left, node->right);
if(node->right) st.push(node->right); // 右
if(node->left) st.push(node->left); // 左
}
return root;
}
2、通过两种遍历方式构造第三种二叉树的类型
①、中序与后序遍历构造二叉树
题目链接:中序与后序构造二叉树
本题目首先明确,后序为(左右中),中序为(左中右),所以,后序数组的最后一个值肯定是树中的高度最高的。因此将这个值记录下来,然后遍历中序数组,找到这个值的坐标。记录i用于分割左右数组。然后分割时,我们结合二分的思想,在每次分割的时候,要记住,区间开闭情况,我们采用左闭右开的区间(同时注意vector中的end操作取得是最后一个值的后一位)。
TreeNode* backtracking(vector<int>& inorder, vector<int>& postorder)
{
if(postorder.size() == 0) return NULL;
int idx = postorder[postorder.size() - 1];
TreeNode* tem = new TreeNode(idx);
if(postorder.size() == 1) return tem;
postorder.resize(postorder.size() - 1);
int i;
for( i = 0; i<inorder.size(); i++){
if(inorder[i] == idx) break;
}
vector<int> leftinorder(inorder.begin(), inorder.begin() + i);
vector<int> rightinorder(inorder.begin() + 1 + i, inorder.end());
vector<int> leftpostorder(postorder.begin(), postorder.begin() + leftinorder.size());
vector<int> rightpostorder(postorder.begin() + leftinorder.size() , postorder.end());
tem ->left = backtracking(leftinorder, leftpostorder);
tem ->right = backtracking(rightinorder, rightpostorder);
return tem;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(inorder.size() == 0 || postorder.size() == 0) return NULL;
return backtracking(inorder, postorder);
}
②、前序与中序遍历构造二叉树
题目链接:中序与前序构造二叉树
与上述思路一样,只需要分割好数组即可。
③、*前序列与后序构造(无法构造)
前序为(中左右),后序为(左右中),此时我们只能确定中间的位置,一个在头,一个在尾。但是无法知道中间的顺序。
五、最大二叉树
题目链接:最大二叉树
该解法的思路和中序那里很像,先找到最大值,也就是中间值,然后根据这个分割数组,从而实现
两边构造,思路与上面是相同的。
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
TreeNode* tem = new TreeNode(0);
if(nums.size() == 1){
tem ->val = nums[0];
return tem;
}
int maxvalue = 0;int maxvalueindex = 0;
for(int i = 0; i<nums.size(); i++){
if(nums[i] > maxvalue){
maxvalue = nums[i];
maxvalueindex = i;
}
}
tem ->val = maxvalue;
if(maxvalueindex > 0){
vector<int> N1(nums.begin(), nums.begin() + maxvalueindex);
tem ->left = constructMaximumBinaryTree(N1);
}
if(maxvalueindex < nums.size() - 1){
vector<int> N2(nums.begin() + maxvalueindex + 1, nums.end());
tem ->right = constructMaximumBinaryTree(N2);
}
return tem;
}
六、合并二叉树
题目链接:合并二叉树
合并非常简单,重叠就相加,有一个是NULL就返回另一个的值。
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if(root1==NULL) return root2;
if(root2==NULL) return root1;
root1->val+=root2->val;
root1->left = mergeTrees(root1->left,root2->left);
root1->right = mergeTrees(root1->right,root2->right);
return root1;
}