树的遍历 根左右
借助栈实现,注意空节点不应该入栈 ,每次取出栈顶元素就是先序结果 ,注意每一次循环中应该让左孩子位于栈顶 ,于是呢就先压入右孩子,再压入左孩子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 vector<int > preorderTraversal (TreeNode* root) { stack<TreeNode*>st; vector<int >results; if (root == nullptr )return results; st.push (root); while (!st.empty ()){ TreeNode* cur = st.top (); st.pop (); results.push_back (cur->val); if (cur->right != nullptr )st.push (cur->right); if (cur->left != nullptr )st.push (cur->left); } return results; }
左右根,因为每次从栈中取得的元素是根,因此迭代顺序就是根左右或者根右左,而我们要求左右根,只需要求出根右左,然后将结果翻转reverse一下即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 vector<int > postorderTraversal (TreeNode* root) { stack<TreeNode*>st; vector<int >results; if (root == nullptr )return results; st.push (root); while (!st.empty ()){ TreeNode* cur = st.top (); st.pop (); results.push_back (cur->val); if (cur->left != nullptr )st.push (cur->left); if (cur->right != nullptr )st.push (cur->right); } reverse (results.begin (),results.end ()); return results; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 vector<int > inorderTraversal (TreeNode* root) { vector<int > result; stack<TreeNode*> st; TreeNode* cur = root; while (cur != NULL || !st.empty ()) { if (cur != NULL ) { st.push (cur); cur = cur->left; } else { cur = st.top (); st.pop (); result.push_back (cur->val); cur = cur->right; } } return result; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 vector<vector<int >> levelOrder (TreeNode* root) { vector<vector<int >>results; if (root == nullptr )return results; queue<TreeNode*>q; q.push (root); while (!q.empty ()){ int size = q.size (); vector<int >tmp; while (size--){ TreeNode* cur = q.front (); q.pop (); tmp.push_back (cur->val); if (cur->left != nullptr )q.push (cur->left); if (cur->right != nullptr )q.push (cur->right); } results.push_back (tmp); } return results; }
树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Solution {public : int result = INT_MIN; int maxPathSum (TreeNode* root) { maxUp (root); return result; } int maxUp (TreeNode* root) { if (root == nullptr )return 0 ; int left = maxUp (root->left); int right = maxUp (root->right); result = max (result,left+right+root->val); int maxu = max (left,right) + root->val; maxu = max (0 ,maxu); return maxu; } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : bool isSymmetric (TreeNode* root) { return isSymmetric (root->left,root->right); } bool isSymmetric (TreeNode* left,TreeNode* right) { if (left == nullptr && right == nullptr )return true ; else if (left == nullptr )return false ; else if (right == nullptr )return false ; else { return left->val == right->val && isSymmetric (left->left, right->right) &&isSymmetric (left->right, right->left); } } };
二叉树的深度应该先求它的左子树的深度,再求的右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。因此这是一个后序遍历。
1 2 3 4 5 6 7 int maxDepth (TreeNode* root) { if (root == nullptr )return 0 ; int l = maxDepth (root->left); int r = maxDepth (root->right); int depth = max (l,r) + 1 ; return depth; }
给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。说明: 叶子节点是指没有子节点的节点。
1 2 3 4 5 6 7 8 9 int minDepth (TreeNode* root) { if (root == nullptr )return 0 ; int minl; if (root->left == nullptr && root->right == nullptr )minl = 0 ; else if (root->left == nullptr )minl = minDepth (root->right); else if (root->right == nullptr )minl = minDepth (root->left); else minl = min (minDepth (root->left),minDepth (root->right)); return minl + 1 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : TreeNode* buildTree (vector<int >& preorder, vector<int >& inorder) { int len = preorder.size (); return buildTree (preorder,inorder,0 ,len-1 ,0 ,len-1 ); } TreeNode* buildTree (vector<int >& preorder, vector<int >& inorder,int l1, int r1,int l2, int r2) { if (r1 < l1)return nullptr ; if (r1 == l1)return new TreeNode (preorder[l1]); TreeNode* rt = new TreeNode (preorder[l1]); int len = 0 ; while (inorder[l2+len] != rt->val)len++; rt->left = buildTree (preorder,inorder,l1+1 ,l1+len,l2,l2+len-1 ); rt->right = buildTree (preorder, inorder,l1+len+1 ,r1,l2+len+1 ,r2); return rt; } };
1 2 3 4 5 6 7 8 9 10 11 12 13 string buildTree (string preorder, string inorder, int l1, int r1, int l2, int r2) { if (r1 < l1)return "" ; if (r1 == l1)return string (1 ,preorder[l1]); char rt = preorder[l1]; int len = 0 ; while (inorder[l2 + len] != rt)len++; string left = buildTree (preorder, inorder, l1 + 1 , l1 + len, l2, l2 + len - 1 ); string right = buildTree (preorder, inorder, l1 + len + 1 , r1, l2 + len + 1 , r2); return left + right + string (1 ,rt); }
给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:
二叉树的根是数组中的最大元素。
左子树是通过数组中最大值左边部分构造出的最大二叉树。
右子树是通过数组中最大值右边部分构造出的最大二叉树。
通过给定的数组构建最大二叉树,并且输出这个树的根节点。
示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 TreeNode* constructMaximumBinaryTree (vector<int >& nums) { return construct (nums,0 ,static_cast <int >(nums.size ()) - 1 ); } TreeNode* construct (vector<int >& nums,int l,int r) { if (l > r)return nullptr ; if (l == r)return new TreeNode (nums[l]); int maxi = l; for (int i = l;i <= r;i++){ if (nums[i] > nums[maxi])maxi = i; } TreeNode* root = new TreeNode (nums[maxi]); root->left = construct (nums,l,maxi-1 ); root->right = construct (nums,maxi+1 ,r); return root; }
如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。
所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。
1 2 3 4 5 6 7 8 9 10 int getHeight (TreeNode* root) { if (root == nullptr )return 0 ; int l = getHeight (root->left); int r = getHeight (root->right); if (l == -1 || r == -1 || abs (l-r) > 1 )return -1 ; else return max (l,r) + 1 ; } bool isBalanced (TreeNode* root) { return getHeight (root) != -1 ; }
完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。
对于情况一,可以直接用 2^树深度 - 1 来计算,注意这里根节点深度为1。
对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int countNodes (TreeNode* root) { if (root == nullptr )return 0 ; TreeNode* cur = root; int ldepth = 0 ,rdepth = 0 ; while (cur != nullptr ){ cur = cur->left; ldepth++; } cur = root; while (cur != nullptr ){ cur = cur->right; rdepth++; } if (ldepth == rdepth)return pow (2 ,ldepth) - 1 ; else return 1 + countNodes (root->left) + countNodes (root->right); }
1 2 3 4 5 6 7 TreeNode* invertTree (TreeNode* root) { if (root == nullptr )return root; swap (root->left,root->right); if (root->left!=nullptr )invertTree (root->left); if (root->right!=nullptr )invertTree (root->right); return root; }
二叉搜索树 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
解答:
根据二叉搜索树的特性,可知中序遍历的结果是有序 的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { long long pre = LONG_MIN; bool ret = true ; public : bool isValidBST (TreeNode* root) { inorder (root); return ret; } void inorder (TreeNode* root) { if (root == nullptr )return ; if (ret == false )return ; inorder (root->left); if (root->val <= pre)ret = false ; else pre = root->val; inorder (root->right); } };
如果这题说的是判断该数组是不是某二叉搜索树的中序遍历结果 ,那么这道题就非常简单了,因为二叉搜索树的中序遍历结果一定是有序的 ,我们只需要判断数组是否有序就行了。但这道题要判断的是不是某二叉搜索树的后序遍历结果,这样就有点难办了。
比如下面这棵二叉树,他的后续遍历是
[3,5,4,10,12,9]
我们知道后续遍历的最后一个数字一定是根节点,所以数组中最后一个数字9就是根节点,我们从前往后找到第一个比9大的数字10,那么10后面的[10,12](除了9)都是9的右子节点,10前面的[3,5,4]都是9的左子节点,后面的需要判断一下,如果有小于9的,说明不是二叉搜索树,直接返回false 。然后再以递归的方式判断左右子树。
[3,5,4,10,12, 9
]如此划分,和前面的题差不多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : bool verifyPostorder (vector<int >& postorder) { return verifyPostorder (postorder,0 ,postorder.size () - 1 ); } bool verifyPostorder (vector<int >& postorder,int left,int right) { if (right - left <= 1 )return true ; int rt = postorder[right]; int len = 0 ; while (postorder[left+len] < rt)len++; int cur = left + len; while (cur < right){ if (postorder[cur++] < rt)return false ; } return verifyPostorder (postorder,left,left+len-1 ) &&verifyPostorder (postorder,left+len,right-1 ); } };
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
这题注意双向链表顺序是二叉搜索树的中序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution { Node* pre; public : Node* treeToDoublyList (Node* root) { if (root == nullptr )return nullptr ; dfs (root); Node* head = root; while (head->left != nullptr )head = head->left; Node* tail = root; while (tail->right != nullptr )tail = tail->right; head->left = tail; tail->right = head; return head; } void dfs (Node* cur) { if (cur == nullptr )return ; dfs (cur->left); if (pre == nullptr ){ cur->left = pre; } else { pre->right = cur; cur->left = pre; } pre = cur; dfs (cur->right); } };
请务必记住二叉搜索树的特性,中序遍历就是从小到大 ,因此这题肯定需要中序遍历,但是这题是寻找第k大的节点,那么考虑右根左 这样的遍历顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : int result; int cnt; int kthLargest (TreeNode* root, int k) { cnt = k; dfs (root); return result; } void dfs (TreeNode* cur) { if (cur == nullptr )return ; dfs (cur->right); cnt--; if (cnt == 0 ){ result = cur->val; return ; } dfs (cur->left); } };
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
提醒一下,二叉搜索树满足下列约束条件:
节点的左子树仅包含键 小于 节点键的节点。
节点的右子树仅包含键 大于 节点键的节点。
左右子树也必须是二叉搜索树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution { int sum = 0 ; public : TreeNode* convertBST (TreeNode* root) { dfs (root); return root; } void dfs (TreeNode* root) { if (root == nullptr )return ; dfs (root->right); sum += root->val; root->val = sum; dfs (root->left); } };
树+回溯 回溯模板 树的回溯模板和图的回溯模板略有不同
1.确定递归终止条件
那么什么时候算是找到了叶子节点? 是当 cur不为空,其左右孩子都为空的时候,就找到叶子节点。
叶子节点终止条件是:
1 2 3 if (cur->left == nullptr && cur->right == nullptr ) { 终止处理逻辑 }
2.单层递归的逻辑
因为是前序遍历,需要先处理中间节点,中间节点就是我们要记录路径上的节点,先放进path中。
1 path.push_back (cur->val);
然后是递归和回溯的过程,上面说过没有判断cur是否为空,那么在这里递归的时候,如果为空就不进行下一层递归了。
所以递归前要加上判断语句,下面要递归的节点是否为空, 如下
1 2 3 4 5 6 if (cur->left) { traversal (cur->left, path, result); } if (cur->right) { traversal (cur->right, path, result); }
此时还没完,递归完,要做回溯啊,因为path 不能一直加入节点,它还要删节点,然后才能加入新的节点。
那么回溯要怎么回溯呢,一些同学会这么写,如下:
1 2 3 4 5 6 7 if (cur->left) { traversal (cur->left, path, result); } if (cur->right) { traversal (cur->right, path, result); } path.pop_back ();
这个回溯就要很大的问题,我们知道,回溯和递归是一一对应的,有一个递归,就要有一个回溯 ,这么写的话相当于把递归和回溯拆开了, 一个在花括号里,一个在花括号外。
所以回溯要和递归永远在一起,世界上最遥远的距离是你在花括号里,而我在花括号外!
那么代码应该这么写:
1 2 3 4 5 6 7 8 9 10 if (cur->left) { path.push_back (cur->val); traversal (cur->left, path, result); path.pop_back (); } if (cur->right) { path.push_back (cur->val); traversal (cur->right, path, result); path.pop_back (); }
什么时候递归需要返回值?
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例: 给定如下二叉树,以及目标和 sum = 22,
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : bool hasPathSum (TreeNode* root, int targetSum) { if (root == nullptr )return false ; return dfs (root,targetSum); } bool dfs (TreeNode* cur, int targetSum) { if (cur->left == nullptr && cur->right == nullptr ){ if (targetSum == cur->val)return true ; else return false ; } if (cur->left != nullptr ){ if (dfs (cur->left,targetSum - cur->val))return true ; } if (cur->right != nullptr ){ if (dfs (cur->right,targetSum - cur->val))return true ; } return false ; } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 vector<string>results; vector<string> binaryTreePaths (TreeNode* root) { if (root != nullptr )dfs (root,"" ); return results; } void dfs (TreeNode* cur,string path) { if (cur->left == nullptr && cur->right == nullptr ){ results.push_back (path+to_string (cur->val)); } if (cur->left != nullptr ){ dfs (cur->left,path + to_string (cur->val) + "->" ); } if (cur->right != nullptr ){ dfs (cur->right,path + to_string (cur->val) + "->" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution {public : vector<vector<int >>results; vector<int >path; vector<vector<int >> pathSum (TreeNode* root, int target) { if (root != nullptr )dfs (root,target); return results; } void dfs (TreeNode* cur,int target) { if (cur->left == nullptr && cur->right == nullptr ){ path.push_back (cur->val); if (target == cur->val)results.push_back (path); path.pop_back (); return ; } if (cur->left != nullptr ){ path.push_back (cur->val); dfs (cur->left, target-cur->val); path.pop_back (); } if (cur->right != nullptr ){ path.push_back (cur->val); dfs (cur->right, target-cur->val); path.pop_back (); } } };
求最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从低向上的遍历方式。
在回溯的过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断。
要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。
1 2 3 4 5 6 7 8 9 TreeNode* lowestCommonAncestor (TreeNode* root, TreeNode* p, TreeNode* q) { if (root == nullptr || root == p || root == q)return root; TreeNode* left = lowestCommonAncestor (root->left,p,q); TreeNode* right = lowestCommonAncestor (root->right,p,q); if (left != nullptr && right != nullptr )return root; else if (left == nullptr )return right; else if (right == nullptr )return left; else return nullptr ; }