Last updated on June 3, 2024 am
二叉树的学习记录
一、二叉树相关概念
(1)二叉树的结构(照搬力扣网站)
1 2 3 4 5 6 7 8
| 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) {} };
|
满二叉树/完全二叉树(只有最后一层缺失,且只有右边缺失)
(2)二叉搜索树
1、简单介绍
一个节点左子树上的所有节点的值全部小于该节点,右子树上所有结点的值全部大于该节点。
作用:树如其名,二叉搜索树在搜索某个节点的值时速度更快。
如图,若想要找节点6,从根节点开始,小于根节点,找左子树;大于节点3,找右子树;等于节点6,找到节点。
除此之外,我们会发现这棵二叉搜索树的中序遍历:1.3.4.6.7.8.10.13.14
2、二叉搜索树的搭建
(1)笨蛋版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| void insert(int k) { TreeNode* temp=root; TreeNode* prev=nullptr; while(temp){ prev=temp; if(k<temp->val) temp=temp->next; else temp=temp->right; } if(k<prev->val){ TreeNode* curr=new TreeNode(k); prev->left=curr; curr->left=curr->right=nullptr; } if(k>prev->val){ TreeNode* curr=new TreeNode(k); prev->right=curr; curr->left=curr->right=nullptr; } }
|
(2)调用自身搭建
1 2 3 4 5 6 7 8 9 10
| TreeNode* add(TreeNode* root,int num) { if(root=nullptr) return new TreeNode(num); if(root->val>num) root->left=add(root->left,num); else root->right=add(root->right,num); return root; }
|
3、二叉搜索树的例题
1>由二叉搜索树的中序遍历搭建二叉搜索树(傻眼)
方法一:总是选取中序遍历中间位置左边的数字作为根节点
1 2 3 4 5 6 7 8 9 10 11 12
| TreeNode* sortedArrayToBST(vector<int>& nums) { return helper(nums, 0, nums.size() - 1); } TreeNode* helper(vector<int>& nums, int left, int right) { if(left>right) return nullptr; int mid=(right+left)/2; TreeNode* root=new TreeNode(num[mid]); root->left=helper(nums,left,mid-1); root->right=helper(nums,mid+1,right); return root; }
|
方法二:总是选取中序遍历中间位置左边的数字作为根节点(只需mid加一)
2>找到二叉搜索树中第k大的节点值(k=cnt)
方法一:(自写)(占用额外空间)
1 2 3 4 5 6 7 8 9 10 11 12
| void myinorder(TreeNode* root,vector<int>& nums){ if(root==nullptr) return; myinorder(root->right,nums); nums.emplace_back(root->val); myinorder(root->left,nums); } int findTargetNode(TreeNode* root, int cnt) { vector<int> nums; myinorder(root,nums); return nums[cnt-1]; }
|
方法二:优化(空间复杂度O(1))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| int k;int res; void myinorder(TreeNode* root){ if(root==nullptr) return; myinorder(root->right); k--; if(k==0){ res=root->val; return; } myinorder(root->left); } int findTargetNode(TreeNode* root, int cnt) { k=cnt; myinorder(root); return res; }
|
3>前序遍历构造二叉搜索树
方法一:通过构建二叉搜索树的函数来构造
1 2 3 4 5 6 7
| TreeNode* bstFromPreorder(vector<int>& preorder) { TreeNode* root =add(nullptr, preorder[0]); for (int i = 1; i < preorder.size(); i++) { root=add(root, preorder[i]); } return root; }
|
方法二:已知二叉搜索树的中序遍历是有序的,因此我们现在知道了二叉树的前序和中序遍历(中序遍历可由前序遍历排序得出),就可以搭建了。(见下面第八道例题)
方法三:递归(分析前序遍历的特点:我们会发现,由根节点开始,第一个大于根节点的节点及其后均为右子树,中间的是左子树,依此类推)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| TreeNode* bstFromPreorder(vector<int>& preorder) { return mybst(preorder,0,preorder.size()-1); } TreeNode* mybst(vector<int>& preorder,int left,int right) { if(left>right) return nullptr; int root=preorder[left]; int mid=left; for(;mid<=right+1;mid++) if(preorder[mid]>root) break; TreeNode* node=new TreeNode(root); node->left=mybst(preorder,left+1,mid-1); node->right=mybst(preorder,mid,right); return node; }
|
4>二叉搜索树的最近公共祖先
给定一个二叉树,找到该树中两个指定节点的最近公共祖先。(一个节点也可以是自己的祖先;最近:深度之差最小)
方法:递归(思路:找到分叉点。也就是两个节点都大于或者小于某个节点时,这个节点一定不是他们的最近公共祖先)
1 2 3 4 5 6
| TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if(p->val<root->val&&q->val<root->val) return lowestCommonAncestor(root->left,p,q); else return lowestCommonAncestor(root->right,p,q); return root; }
|
(3)二叉树的层次遍历(自顶向下)
力扣题目链接:102. 二叉树的层序遍历 - 力扣(LeetCode)
题目描述:给你二叉树的根节点 root
,返回其节点值的层序遍历。
输入:root = [ 3 , 9 , 20 , null , null , 15 , 7 ]
输出:[ [3] , [ 9 , 20 ] , [ 15 , 7 ] ]
广度优先搜索法:
(一直以为广度优先搜索都是固定的那种函数模板,函数体里总会调用自身函数,现在看来不是这样。)
(广度:一层一层全部搜索完再去下一层;深度:走到底再返回)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| vector <vector<int>> levelOrder(TreeNode* root) { vector<vector<int> > ret; if(!root) return ret; queue <TreeNode*> q; q.push(root); while(!q.empty()){ int curl=q.size(); ret.push_back(vector<int>()); for(int i=1;i<=curl;++i){ auto node=q.front();q.pop(); ret.back().push_back(node->val); if(node->left) q.push(node->left); if(node->right) q.push(node->right); } } return ret; }
|
思路亦可改为每次循环新创建一个向量,然后压入每层节点值,最后再把这个向量压入二级向量中。
二级向量数组:内层是向量,向量大小不固定;外层是存储这些向量的向量。
emplace_push:据说速度要大于push_back。
二、力扣刷题记录
(1)装饰树
装饰过程:在每个父节点与其子节点之间都插入一个值为-1的节点。返回完成装饰后树的根节点。
递归法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| TreeNode* expandBinaryTree(TreeNode* root) { if(root!=nullptr){ expandBinaryTree(root->left); if(root->left!=nullptr){ TreeNode* T=new TreeNode(-1); T->left=root->left; root->left=T; } expandBinaryTree(root->right); if(root->right!=nullptr){ TreeNode* T=new TreeNode(-1); T->right=root->right; root->right=T; } } return root; }
|
(2)求二叉树的最小深度
力扣题目链接:111. 二叉树的最小深度 - 力扣(LeetCode)
1 2 3 4 5 6 7 8
| int minDepth(TreeNode* root) { if(root==nullptr) return 0; if(root->left==nullptr) return minDepth(root->right)+1; if(root->right == nullptr) return minDepth(root->left) + 1; return min(minDepth(root->left), minDepth(root->right)) + 1; }
|
(3)N叉树的前序遍历
力扣题目链接:589. N 叉树的前序遍历 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Node { public: int val; vector<Node*> children; }; class Solution { public: vector<int> preorder(Node* root){ vector<int> res; helper(root,res); return res; } void helper(const Node* root, vector<int> & res) { if(root==nullptr) return; res.emplace_back(root->val); for(auto & ch:root->children) helper(ch,res); } };
|
(4)克隆二叉树(递归)(无连接)
1 2 3 4 5 6
| public TreeNode cloneTree(TreeNode root) { if(root==null) return null; return new TreeNode{root.val,cloneTree(root.left),cloneTree(root.right)}; }
|
(5)翻转二叉树(递归)
226. 翻转二叉树 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10
| public TreeNode* invertTree(TreeNode* root) { if(root==null) return null; TreeNode* left=invertTree(root->left); TreeNode* right=invertTree(root->right); root->left=right; root->right=left; return root; }
|
(6)相同的树(终于自己写会的递归,只是有细节不到位)
100. 相同的树 - 力扣(LeetCode)
错误版本:
1 2 3 4 5 6 7
| bool isSameTree(TreeNode* p, TreeNode* q) { if(p->val!=q->val) return 0; if(p==q==null) return 1; return isSameTree(p->left,q->left)+isSameTree(p->right,q->right)==2?1:0; }
|
正确版本:
1 2 3 4 5 6 7 8 9
| bool isSameTree(TreeNode* p, TreeNode* q) { if(p==nullptr&&q==nullptr) return true; else if(p==nullptr||q==nullptr) return false; if(p->val!=q->val) return false; return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right); }
|
(7)对称二叉树
101. 对称二叉树 - 力扣(LeetCode)
方法一:根据前几道题解来判断(反转后如果和原树相同说明是对称的)
1 2 3 4
| bool isSymmetric(TreeNode* root) { TreeNode root1=invertTree(root); return isSameTree(root1,root); }
|
方法二:递归(和判断是否是相同树很像)
1 2 3 4 5 6 7 8 9 10 11
| bool isSymmetric(TreeNode* root) { return issame(root,root); } bool issame(TreeNode* l,TreeNode* r) { if(l==nullptr&&r==nullptr) return true; if(l==nullptr||r==nullptr) return false; return l->val==r->val&&issame(l->left,r->right)&&issame(l->right,r->left); }
|
(8)由遍历构造二叉树
1>由前序遍历和中序遍历构造二叉树
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { if(pre.empty()) return nullptr; TreeNode* root=new TreeNode(pre[0]); int size=find(in.begin(),in.end(),pre[0]); vector<int> prel(pre.begin()+1,pre.begin()+size+1); vector<int> prer(pre.begin()+size+1,pre.end()); vector<int> inl(in.begin(),in.begin()+size); vector<int> inr(in.begin()+size+1,in.end()); root->left=mybuildTree(prel,inl); root->right=mybuildTree(prer,inr); return root; }
|