0.Basic
- 0.1 树的节点的定义
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x): val(x), left(NULL), right(NULL){}
};
-
0.2 基本术语
-
结点的度: 结点拥有的子树数称为结点的度,
上图结点2度为3
-
叶子结点:度为0的结点,
上图1345
-
树的度:树内各结点的度的最大值
上图为3
-
树的深度:树中结点的最大层次称为树的深度或高度,
上图为3
-
满二叉树:深度为k且有 2 k − 1 2^k-1 2k−1个结点的二叉树
-
完全二叉树:深度为 k k k有 n n n个结点的二叉树,当且仅当其每个结点都与深度为 k k k的满二叉树中编号从1到 n n n的结点一一对应时,称为完全二叉树。除最后一层外,每一层上的结点数均达到最大值;在最后一层上只缺少右边的若干结点。
-
二叉排序树:又名二叉查找树,二叉树排序树或者是一棵空树,或者是具有如下性质的二叉树
- 1)若它的左子树非空,则左子树的值都小于根结点
- 2)若它的右子树非空,则右子树的值都大于根结点
- 3)它的左右子树也分别是二叉排序树
-
平衡二叉树:一种特殊的二叉排序树,也称为AVL树,其每个结点左右子树的高度差至多为1
-
红黑树:一种二叉查找树,但在每个结点增加一个存储位表示结点颜色,可以是红或黑,通过任何一条从根到叶子结点的路径上各个结点着色方式的限制,红黑树确保没有1条路径会比其他路径长出两倍,红黑树是一种弱平衡二叉树。相对于严格的AVL二叉树,其旋转少,对于搜索、插入、删除较多的场景,使用较多,C++STL中的set和map都是用红黑树实现的。性质:
- 1)每个结点非红即黑
- 2)根结点是黑色的
- 3)每个叶子结点都是黑色的
- 4)如果一个结点是红色的,则它的子结点必须是黑色的
-
1.0前中后序遍历
void treeTraverse(TreeNode* root) {
// 前序 DLR
treeTraverse(root->left);
// 中序 LDR
treeTraverse(root->right);
// 后序 LRD
}
1.1前中后序循环遍历
- 前序DLR
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> s;
TreeNode *p = root;
while(p!=nullptr||!s.empty())
{
while(p!=nullptr)
{
s.push(p);
res.push_back(p->val); // 访问结点
p = p->left;
}
if (!s.empty())
{
p = s.top();
s.pop();
p = p->right;
}
}
return res;
}
};
- 后序LRD
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> s;
TreeNode *p = root;
TreeNode *l = nullptr;
vector<int> res;
while (!s.empty() || p!=nullptr)
{
while (p!=nullptr)
{
s.push(p);
p = p->left;
}
if (!s.empty())
{
TreeNode *t = s.top();
if(t->right==nullptr || t->right==l)
{
res.push_back(t->val);
s.pop();
l = t;
}
else
{
p = t->right;
}
}
}
return res;
}
};
1.3二叉树的层序遍历的递归实现
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ans;
pre(root, 0, ans);
return ans;
}
void pre(TreeNode *root, int depth, vector<vector<int>> &ans) {
if (!root) return ;
if (depth >= ans.size())
ans.push_back(vector<int> {}); //新用法
ans[depth].push_back(root->val);
pre(root->left, depth + 1, ans);
pre(root->right, depth + 1, ans);
}
};
2.深度优先搜索
void treeDepthFirstTraverse(TreeNode* root) {
vector<TreeNode*> nodeVec; // 节点栈
nodeVec.push_back(root);
while(!nodeVec.empty()) {
TreeNode* curNode = nodeVec.back();
nodeVec.pop_back();
// 中间节点
if(curNode->right)
nodeVec.push_back(curNode->right);
if(curNode->left)
nodeVec.push_back(curNode->left);
// 叶子节点
if(!curNode->right&&!curNode->left)
cout<<"leaf node\n";
}
}
3.广度优先搜索
void treeBreathFirstTraverse(TreeNode* root) {
vector<TreeNode*> nodeVec; // 当前节点栈
nodeVec.push_back(root);
while(!nodeVec.empty()) {
vector<TreeNode*> nexNodeVec; // 下层节点栈
for(TreeNode* node : nodeVec) {
cout<<"cur node value" << node->val << endl;
if(node->left)
nexNodeVec.push_back(node->left);
if(node->right)
nexNodeVec.push_back(node->right);
}
nodeVec = nexNodeVec;
}
}
4.找到所有子结点的路径
// 递归
/**
* 返回的结果时二维数组,每个元素代表1条路径
* 要将递归的结果做与当前结点做拼接才能找到路径
**/
vector<vector<int>> getALLPath(TreeNode* root) {
<vector<vector<int>> res;
<vector<int>> path;
path.push_back(root->val);
// base case
if(!root->left&&!root->right) {
res.push_back(path);
return res;
}
<vector<vector<int>> leftRes,rightRes;
if(root->left)
leftRes = getALLPath(root->left);
if(root->right)
leftRes = getALLPath(root->right);
for(auto lpath: leftRes) {
// lpath.insert(lpath.begin(), root->val);
lpath.push_back(root->val);
res.push_back(lpath);
}
for(auto rpath: rightRes) {
// rpath.insert(rpath.begin(), root->val);
rpath.push_back(root->val);
res.push_back(rpath);
}
return res;
}
5.判断一棵树是否是二叉排序树
- tricks: static关键字,静态变量
bool isSearchTree(TreeNode* root) {
static TreeNode* pre = NULL;
if(root) {
if(!isSearchTree(root->left))
return false;
if(pre != NULL && pre->val >= root->val)
return false;
pre = root;
return isSearchTree(root->right);
}
return true;
}
5.1二叉排序树插入一个结点
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root==nullptr)
{
TreeNode *tmp = new TreeNode(val);
return tmp;
}
if(val < root->val)
{
if(root->left==nullptr)
{
TreeNode *tmp = new TreeNode(val);
root->left=tmp;
} else {
root->left = insertIntoBST(root->left, val);
}
} else {
if(root->right==nullptr)
{
TreeNode *tmp = new TreeNode(val);
root->right=tmp;
} else {
root->right = insertIntoBST(root->right, val);
}
}
return root;
}
};
6.判断一棵树是否是完全二叉树
tricks
- 1)vector erase的使用,erase(pos)或erase(begin, end)
- 2)完全二叉树只能是最后一层少结点,且只少右侧的若干结点
- 3)层级遍历,一旦出现一个空,后面就不能再有空结点,否则不是完全二叉树
bool isCompleteTree(TreeNode* root) {
if(root==NULL) return true;
vector<TreeNode*> nodes;
nodes.push_back(root);
while(!nodes.empty()) {
TreeNode *node = nodes.front();
nodes.erase(nodes.begin());
if(node!=NULL) {
nodes.push_back(node->left);
nodes.push_back(node->right);
} else {
while(!nodes.empty()) {
node = nodes.front();
nodes.erase(nodes.begin());
if(node!=NULL)
return false;
}
}
}
return true;
}
7.计算完全二叉树的结点个数 T [ O ( n ) ] = l o g 2 N T[O(n)]=log^2N T[O(n)]=log2N
trick
- 1)若遍历数结点个数,时间必为O(N)
- 2)二分查找,根据完全二叉树的性质和满二叉树结点计算公式 2 h − 1 2^h-1 2h−1
两种情况
- 1)左子树高度大于右子树,左子树是完全二叉树,右子树满二叉树
- 2)左子树高度等于右子树,左子树是满二叉树,右子树是完全二叉树
class Solution {
public:
int getTreeHeight(TreeNode* root);
int nodeNum(struct TreeNode* head) {
if(!head) return 0;
int lcnt = 0, rcnt = 0;
int lh = getTreeHeight(head->left);
int rh = getTreeHeight(head->right);
if(lh > rh) {
rcnt = pow(2, rh) - 1;
lcnt = nodeNum(head->left);
}
if(lh == rh) {
rcnt = nodeNum(head->right);
lcnt = pow(2, lh) - 1;
}
return rcnt + lcnt + 1;
}
};
8.序列化反序列化二叉树
Tips
strcmp/strdup/strcpy/strcat
操作char*
int
转string
,C++11
中使用to_string
char *
转string
,string ss = (char *) str
string,c_str()
转char *
,strdup
先序遍历,空结点使用#
填充
class Solution {
public:
char* Serialize(TreeNode *root) {
if(!root) {
return "#";
}
string res = to_string(root->val);
res += ",";
char *left = Serialize(root->left);
char *right = Serialize(root->right);
res += left;
res += right;
return strdup(res.c_str());
}
TreeNode* deserial(char *&str) {
if(*str=='#') {
++str;
return NULL;
}
int num = 0;
while(*str!=',') {
num = 10 * num + *str - '0';
++str;
}
++str;
TreeNode *head = new TreeNode(num);
head->left = deserial(str);
head->right = deserial(str);
return head;
}
TreeNode* Deserialize(char *str) {
return deserial(str);
}
};
9.树的直径
牛客NC99
更像是无向无环的最大路径长度,两次深度优先搜索,第二次的路径即为结果
- pair: pair是将2个数据组合成一组数据,当需要这样的需求时就可以使用pair,如stl中的map就是将key和value放在一起来保存。另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair。 pair的实现是一个结构体,主要的两个成员变量是first second 因为是使用struct不是class,所以可以直接使用pair的成员变量.
- vector:
resize vv.resize(int n,element)
表示调整容器vv的大小为n,扩容后的每个元素的值为element,默认为0
resize()会改变容器的容量和当前元素个数。emplace_back()
和push_back()
的区别
emplace_back() 和 push_back() 的区别,就在于底层实现的机制不同。push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。
/**
* struct Interval {
* int start;
* int end;
* };
*/
class Solution {
public:
// /**
// * 树的直径
// * @param n int整型 树的节点个数
// * @param Tree_edge Interval类vector 树的边
// * @param Edge_value int整型vector 边的权值
// * @return int整型
// */
vector<vector<pair<int, int>>> graph_matrix;
int max_node = 0;
int max_dis = 0;
void maxRoad(int cur, int curParent, int curDist) {
for(auto node : graph_matrix[cur]) {
// 确保仅朝1个方向遍历,避免1-2,2-1这样重复
if(node.first != curParent) {
maxRoad(node.first, cur, curDist + node.second);
}
}
if(curDist > max_dis) {
max_dis = curDist;
max_node = cur;
}
}
int solve(int n, vector<Interval>& Tree_edge, vector<int>& Edge_value) {
// write code here
// build graph
if(n<1) return 0;
graph_matrix.resize(n);
for(size_t i=0; i < n-1; i++) {
int n1 = Tree_edge[i].start;
int n2 = Tree_edge[i].end;
graph_matrix[n1].emplace_back(n2, Edge_value[i]);
graph_matrix[n2].emplace_back(n1, Edge_value[i]);
}
maxRoad(0, -1, 0);
maxRoad(max_node, -1, 0);
return max_dis;
}
};