二叉树与经典问题
树的结构定义
链表定义:
typedef struct Node{
int data;
struct Node *next;
}Node,*LinkList;
(链表是一种特殊的树形结构)
将单一指针域的链表转变多指针域的树:
typedef struct Node{
int data;
struct Node *next[3];
}Node,*Tree;
二叉树注:
- 每个节点最多有2度
- 度为0的节点比度为2的节点多1个 => n个结点的树边有n-1条
证明2:n0+n1+n2 (左边为节点数) = 0n0+1n1+2*n2+1(右边为边数+1) =>n0=n2+1
二叉树的遍历
前序遍历 根 左 右
中序遍历 左 根 右
后序遍历 左 右 根
二叉树的种类
完全二叉树(complete binary tree)
定义:只在最后一层右边缺少1个结点
性质:
-
编号为i的子节点:
左孩子编号:2 * i
右孩子编号:2 * i + 1 -
可以用连续空间存储(数组)
-
子树的地址可以通过父节点计算得到
(好处:不需要记录左右子树的地址 可以节省大量存储空间)(记录式与计算式存储方式的区别)
满二叉树(full binary tree)
完美二叉树(perfect binary tree)
二叉树做题思路
关于树结构的深入理解:
结点:集合
边:关系
主要用与查找操作
练习递归技巧的最佳选择
- 数学递归法 - 》 结构递归法
- 赋予递归函数一个明确的意义
- 思考边界条件
- 实现递归过程
二叉树的前序遍历:(Leetcode-144)
1.函数意义:前序遍历以root为根结点的二叉树
2.边界条件:root为空时不需要遍历
3.递归过程:前序遍历左子树,前序遍历右子树
N叉树的前序遍历:(Leetcode-589)
1.函数意义:前序遍历以root为根结点的N叉树
2.边界条件:root为空时不需要遍历
3递归过程:前序遍历root下的每一棵子树
完全二叉树左孩子右兄弟表示法节省空间
C++前置基础:C语言,操作系统,底层原理,网络编程,数据结构上、下,面试算法上、下=》C++
练习题:
- leetcode-110(平衡二叉树)
class Solution {
public:
int getHeight(TreeNode *root){
if(root == NULL)return 0;
int l = getHeight(root->left);
int r = getHeight(root->right);
if(l<0 || r<0) return -2;
if(abs(l-r)>1)return -2;
return max(l,r) + 1;
}
bool isBalanced(TreeNode* root) {
return getHeight(root)>=0;
}
};
- leetcode-112(路径总和)
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if(root == NULL) return false;
if(root->left == NULL && root->right == NULL) return root->val = targetSum;
if(root->left && hasPathSum(root->left,targetSum - root->val)) return true;
if(root->right && hasPathSum(root->right,targetSum - root->val)) return true;
return false;
}
};
- leetcode-105(从前序和中序遍历构造二叉树)
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.size() == NULL) return NULL;
int pos = 0; //中序遍历时根结点的位置
while(inorder[pos]!=preorder[0]) ++pos;
vector<int> l_pre,l_in,r_pre,r_in;
for(int i = 0; i<pos;i++){
l_pre.push_back(preorder[i+1]);//左子树的前序遍历
l_in.push_back(inorder[i]);//左子树的中序遍历
}
for(int i = pos+1;i<preorder.size();i++){
r_pre.push_back(preorder[i]);//右子树的前序遍历
r_in.push_back(inorder[i]);//右子树的中序遍历
}
TreeNode *root = new TreeNode(preorder[0]);
root->left = buildTree(l_pre,l_in);
root->right = buildTree(r_pre,r_in);
return root;
}
};
- leetcode-222(完全二叉树的节点个数)
class Solution {
public:
int countNodes(TreeNode* root) {
if(root == NULL) return 0;
return countNodes(root->left)+countNodes(root->right)+1;
}
};
- leetcode-剑指Offer 54(二叉搜索树的第k大结点)
class Solution {
public:
void in_order(TreeNode *root,vector<int>&ans){
if(root == NULL) return ;
in_order(root->left,ans);
ans.push_back(root->val);
in_order(root->right,ans);
return ;
}
int kthLargest(TreeNode* root, int k) {
vector<int> ans;
in_order(root,ans);
return ans[ans.size()-k];
}
};
- leetcode-剑指Offer 26(树的子结构)
class Solution {
public:
bool isMatch(TreeNode *A,TreeNode *B){
if(B==NULL) return true;
if(A == NULL)return false;
if(A->val !=B->val)return false;
return isMatch(A->left,B->left) && isMatch(A->right,B->right);
}
bool isSubStructure(TreeNode* A, TreeNode* B) {
if(B == NULL) return false;
if(A == NULL) return false;
//此时A,B树都不是空树
if(A->val == B->val && isMatch(A,B)) return true;
return isSubStructure(A->left,B) || isSubStructure(A->right,B);
}
};
-
leetcode-662(二叉树的最大宽度)
-
leetcode-968 (监控的二叉树)(动态规划)
```cpp
class Solution {
public:
void getDP(TreeNode *root,int dp[2][2]){
if(root == NULL){
dp[0][0] = 0;
dp[0][1] = 10000;
dp[1][0] = 0;
dp[1][1] = 10000;
return ;
}
if(root->left == NULL && root->right == NULL ){
dp[0][0] = 10000;
dp[0][1] =1;
dp[1][0] = 0;
dp[1][1] = 1;
return ;
}
int l[2][2],r[2][2];
getDP(root->left,l);
getDP(root->right,r);
dp[0][0] = min(min(l[0][1]+r[0][0],l[0][0]+r[0][1]),l[0][1]+r[0][1]);
dp[0][1] = min(min(l[1][0]+r[1][0],l[1][1]+r[1][1]),min(l[1][0]+r[1][1],l[1][1]+r[1][0]))+1;
dp[1][0] = min(dp[0][0],l[0][0]+r[0][0]);
dp[1][1] = dp[0][1];
return ;
}
int minCameraCover(TreeNode* root) {
int dp[2][2];
getDP(root,dp);
return min(dp[0][1],dp[0][0]);
}
};