阅读更多文章,请看学习笔记汇总
文章目录
树的基本术语
树是由若干结点(A,B,C都是结点),是由唯一的根(结点A)和若干棵互不相交的子树(如B、E、F、K、L这五个结点组成的树就是一棵子树)组成的
结点的度:结点拥有的子树个数或者分支的个数
树的度:树中各结点度的最大值,如图6-1结点度最大为3(A,D结点)
叶子结点:也叫终端结点,指度为0的结点,如K,L,F,G,M,I,J
分支结点:也叫非终端结点,指度不为0的结点,如A,B,C…
内部结点:除了根节点的非终端结点,如BCDEH结点
孩子:结点的子树的根,如A结点的孩子是B、C、D
双亲:与孩子的定义对应,如B、C、D结点的双亲都是A
兄弟:同一双亲的孩子之间互为兄弟,如B、C、D互为兄弟
祖先:从根到某结点的路径上的所有结点,如K的祖先是A、B、E
子孙:以某结点为根的子树中的所有结点,都是该结点的子孙。如D的子孙是H、I、J、M
层次:从根开始,根为第一层,根的孩子为第二层,根的孩子的孩子为第三层,以此类推
树的高度或深度:树中结点的最大层次。如图中树共有4层,所以高度为4
森林:若干棵互不相交的树的集合。如把根节点A去掉,剩下的3棵子树互不相交,它们组成一个森林
二叉搜索树
具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树
完全二叉树
定义:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边
堆
定义:堆是一个完全二叉树,每个节点与其子节点位置相对。父节点总是大于或等于子节点,这种情况下被叫作大顶堆,或者父节点总是小于或等于子节点,这种情况下叫作小顶堆。注意,给定父节点的子节点不一定按顺序排列
堆(堆像一棵倒过来的树):是一种经过排序的树形数据结构,每个结点都有一个值。通常所说的堆的数据结构,是指二叉堆。堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。由于堆的这个特性,常用来实现优先队列
树的结点定义
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
一般给的函数中参数是TreeNode* root,相当于告诉你一个指向树根结点的指针,root->val访问树根结点的值,root->left表示一个指向左子树根结点的指针,root->right表示一个指向右子树根结点的指针
树的遍历
递归写法
*递归理解方法
递归可以这样理解:就看一个分叉,把分支当做一个整体,然后在开头添加递归出口即可
出口必须先写,不然会出现段错误
递归是从顶部到底部再回到顶部
,动态规划是通过存储,直接从底部到顶部解决问题
递归经典例子
int Fibonacci(int n) {
if (n == 0) return 0;
else if (n == 1) return 1;
else return Fibonacci(n - 1) + FIbonacci(n - 2);
}
以先序遍历为例
如果二叉树为空树,则什么都不做,否则:
- 访问根结点
- 先序遍历左子树
- 先序遍历右子树
对应的算法描述如下:
void preorder(TreeNode *p) {
if (p) {
Visit(p); // Visit()可以是对结点p的任何访问操作
preorder(p->left); // 先序遍历左子树
preorder(p->right); // 先序遍历右子树
}
}
中序遍历 inorder
先左子树再根结点点最后右子树
后序遍历 postorder
先左子树再右子树最后根结点
经典迭代写法
中序遍历 迭代
将整棵树的最左边一条链压入栈中
每次取出栈顶元素,如果它有右子树,则将右子树压入栈中
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
// 中序遍历,迭代写法
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> stk;
auto cur = root;
while(cur || stk.size()) {
while(cur) { // 结点非空时,把左侧的点全加到栈,然后倒着遍历
stk.push(cur);
cur = cur->left;
}
// stk:1 2 4 8
cur = stk.top();
stk.pop();
res.push_back(cur->val);
cur = cur->right; // 遍历该结点的右子树,如果为空,则继续栈
}
return res;
}
};
先序遍历 迭代
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> stk;
auto cur = root;
while(cur || stk.size()) {
while(cur) { // 把左侧的点全加到栈,然后倒着遍历
res.push_back(cur->val); // 先访问根结点
stk.push(cur);
cur = cur->left;
}
// stk:1 2 4 8
cur = stk.top();
stk.pop();
cur = cur->right; // 类似地遍历右子树
}
return res;
}
};
后序遍历 迭代 lc145???
层次遍历(队列)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
// 2020年1月16日 星期四 16:26:37
// 用队列,每次记下当前层有len个结点,然后遍历这len个点
class Solution {
public:
static const int N = 1e4;
TreeNode* q[N];
int hh = 0, tt = 0;
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if (!root) return {};
q[tt ++] = root;
while(hh < tt) {
int len = tt - hh; // 当前层的结点个数
vector <int> tmp;
for(int i = 0; i < len; i ++) {
auto t = q[hh];
tmp.push_back(q[hh]->val);
hh ++;
if (t->left) q[tt ++] = t->left;
if (t->right) q[tt ++] = t->right;
}
res.push_back(tmp);
}
return res;
}
};
输出二叉搜索树
中序遍历即可
vector<int> ans;
void dfs(TreeNode* p) {
if (!p) return;
dfs(p->left);
ans.push_back(p->val);
dfs(p->right);
}
得到的ans是有序的
树的例题
102.树的最大深度(递归或层次遍历)
递归
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
int dfs(TreeNode* root) {
if (!root) return 0;
return max(dfs(root->left), dfs(root->right)) + 1;
}
class Solution {
public:
int maxDepth(TreeNode* root) {
return dfs(root);
}
};
层次遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int maxDepth(TreeNode* root) {
if (!root) return 0;
queue <TreeNode*> q;
q.push(root);
// 层次遍历
int level = 0;
while(q.size()) {
int len = q.size();
int flag = 0;
for (int i = 1; i <= len; i ++) {
auto t = q.front();
q.pop();
if (t->left) q.push(t->left);
if (t->right) q.push(t->right);
}
level ++;
}
return level;
}
};
111.树的最小深度(层次遍历)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
// 2020年1月16日 星期四 18:54:16
class Solution {
public:
int minDepth(TreeNode* root) {
if (!root) return 0;
queue <TreeNode*> q;
q.push(root);
// 层次遍历,记录level,如果当前层出现叶子结点,则退出while
int level = 0;
while(q.size()) {
int len = q.size();
int flag = 0;
for (int i = 1; i <= len; i ++) {
auto t = q.front();
q.pop();
if (t->left) q.push(t->left);
if (t->right) q.push(t->right);
if (t->left == NULL && t->right == NULL) flag = 1;
}
level ++;
if (flag == 1) {
break;
}
}
return level;
}
};
662.二叉树的最大宽度(层次遍历+编号)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
// 层次遍历,然后对每个结点编号(结构体),每一层记录最小的编号和最大的编号,算出深度
// 此题数据范围比较大,32位整数,故要用double来存
class Solution {
public:
// 构造指针+编号的结构体
typedef struct node {
TreeNode *a;
double b;
} node;
int widthOfBinaryTree(TreeNode *root) {
queue <node> q;
q.push({root, 1});
double res = 1;
while(q.size()) {
int len = q.size();
double mn = DBL_MAX, mx = 0; // DBL_MAX是double的最大值 大约10^308次
// 对于每一层找到左边界和右边界
for (int i = 1; i <= len; i ++) {
auto t = q.front();
q.pop();
if (t.a->left) {
q.push({t.a->left, 2 * t.b});
mn = min(mn, 2 * t.b);
mx = max(mx, 2 * t.b);
}
if (t.a->right) {
q.push({t.a->right, 2 * t.b + 1});
mn = min(mn, 2 * t.b + 1);
mx = max(mx, 2 * t.b + 1);
}
}
res = max(res, (double)((int)(mx - mn) + 1));
}
return (int)res;
}
};
98.验证二叉搜索树
法一:递归判断每个点是否在范围内
// 2019年12月23日 星期一 12:40:08
// 第一个点可以取的范围在INT_MIN 到 INT_MAX
// 然后递归判断每个点是否在范围内
// v可能是INT_MIN,再-1会爆int,所以用long long和1ll
bool dfs(TreeNode* root, long long Left, long long Right) {
if (!root) return true;
int v = root->val;
if (v < Left || v > Right) return false;
return dfs(root->left, Left, v - 1ll) && dfs(root->right, v + 1ll, Right);
}
class Solution {
public:
bool isValidBST(TreeNode* root) {
return dfs(root, INT_MIN, INT_MAX);
}
};
法二:中序遍历判断是否递增
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
vector<int> vec;
// 中序遍历
void inorder(TreeNode* p) {
if (p) {
inorder(p->left);
vec.push_back(p->val);
inorder(p->right);
}
}
class Solution {
public:
bool isValidBST(TreeNode* root) {
vec.clear(); // 清空vector
inorder(root);
for (int i = 1;i < vec.size(); i ++) {
if (vec[i] <= vec[i - 1]) return false;
}
return true;
}
};
94.二叉树的中序遍历(迭代写法)
/**
* @Author: Wilson79
* @Datetime: 2019年12月23日 星期一 13:34:05
* @Filename: 中序遍历迭代写法.cpp
*/
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
// 中序遍历,迭代写法
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> stk;
auto cur = root;
while(cur || stk.size()) {
while(cur) { // 结点非空时,把左侧的点全加到栈,然后倒着遍历
stk.push(cur);
cur = cur->left;
}
// stk:1 2 4 8
cur = stk.top();
stk.pop();
res.push_back(cur->val);
cur = cur->right; // 遍历该结点的右子树,如果为空,则继续栈
}
return res;
}
};
100.相同的树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if (!p || !q) return (p == q);
if (p->val != q->val) return false; // 如果当前点相同,则看他们的子树的情况
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
};
104.二叉树的最大高度
递归经典例子
// 2019年12月24日 星期二 13:31:54
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
int dfs(TreeNode* root) {
if (!root) return 0;
return max(dfs(root->left), dfs(root->right)) + 1;
}
class Solution {
public:
int maxDepth(TreeNode* root) {
return dfs(root);
}
};
105.从前序与中序遍历序列构造二叉树(递归)
/**
* @Author: Wilson79
* @Datetime: 2019年12月24日 星期二 13:34:41
* @Filename: 105.从前序与中序遍历序列构造二叉树.cpp
*/
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
// 前序遍历 preorder = [“3”,9,20,15,7]
// 中序遍历 inorder = [9,“3”,15,20,7]
// "3"根节点,分成了两部分
class Solution {
public:
unordered_map <int, int> hash;
TreeNode *dfs(vector<int> &preorder, vector<int> &inorder, int pre1, int pre2, int in1, int in2) {
if (pre1 > pre2) return NULL; // 出口必须先写
int val = preorder[pre1]; // 拿出根节点的val
auto root = new TreeNode(val); // 创建一个val,NULL,NULL的结点
int k = hash[val];
int len = k - in1; // 表示先序遍历的长度
root->left = dfs(preorder, inorder, pre1 + 1, pre1 + 1 + len - 1, in1, k - 1);
root->right = dfs(preorder, inorder, pre1 + 1 + len, pre2, k + 1, in2);
return root;
}
TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) {
int n = preorder.size();
for (int i = 0; i < inorder.size(); i++) hash[inorder[i]] = i;
return dfs(preorder, inorder, 0, n - 1, 0, n - 1);
}
};
106.从中序与后序遍历序列构造二叉树
/**
* @Author: Wilson79
* @Datetime: 2019年12月24日 星期二 18:29:11
* @Filename: 106.从中序与后序遍历序列构造二叉树.cpp
*/
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
// 后序遍历 postorder = [9,15,7,20,"3"]
// 中序遍历 inorder = [9,"3",15,20,7]
// 3
// 9 20
// 15 7
// 跟105的做法是相同的
class Solution {
public:
unordered_map <int, int> hash;
TreeNode* dfs(vector<int>& inorder, vector<int>& postorder, int p1, int p2, int in1, int in2) {
// p1后续遍历左端点,p2后续遍历右端点,in1中序遍历左端点,in2中序遍历右端点
if (p1 > p2) return NULL;
int val = postorder[p2];
int key = hash[val];
int len = key - in1; // 左子树长度
auto root = new TreeNode(val);
root->left = dfs(inorder, postorder, p1, p1 + len - 1, in1, key - 1);
root->right = dfs(inorder, postorder, p1 + len, p2 - 1, key + 1, in2);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
int n = inorder.size();
for (int i = 0; i < n; i ++) hash[inorder[i]] = i;
return dfs(inorder, postorder, 0, n - 1, 0, n - 1);
}
};
110.平衡二叉树
法一:O(n^2)
/**
* @Author: Wilson79
* @Datetime: 2019年12月24日 星期二 19:26:00
* @Filename: 110.平衡二叉树.cpp
*/
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
// 时间复杂度O(n^2)
class Solution {
public:
// 求子树的高度
int height(TreeNode *p) {
if (!p) return 0;
return max(height(p->left), height(p->right)) + 1;
}
bool isBalanced(TreeNode *root) {
if (!root) return true; // 如果它是空的,这个子树自然满足平衡二叉树
if (abs(height(root->left) - height(root->right)) > 1) return false;
return isBalanced(root->left) && isBalanced(root->right);
}
};
法二:O(n) 递归高度时直接判断
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
// 朴素算法中很多高度已经算过了,所以在算高度的时候直接可以判断
int height(TreeNode* p) {
if (!p) return 0;
int lh = height(p->left);
if (lh < 0) return -1; // 如果这棵子树已经是非平衡二叉树,就要一直返回到顶部
// 当发现lh返回-1的时候,我们需要提前返回-1
int rh = height(p->right);
if (rh < 0) return -1;
if (abs(lh - rh) > 1) return -1;
return max(lh, rh) + 1;
}
bool isBalanced(TreeNode* root) {
return height(root) != -1;
}
};
两个二叉搜索树中所有元素
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> ans;
void dfs(TreeNode* p) {
if (!p) return;
dfs(p->left);
ans.push_back(p->val);
dfs(p->right);
}
vector<int> getAllElements(TreeNode* root1, TreeNode* root2) {
dfs(root1);
dfs(root2);
sort(ans.begin(), ans.end());
return ans;
}
};