文章目录
1. 二叉树
(1) 二叉树理论知识
(2)常见的二叉树
满二叉树
完全二叉树
二叉搜索树
平衡二叉搜索树
(C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树,所以map、set的增删操作时间时间复杂度是logn,注意我这里没有说unordered_map、unordered_set,unordered_map、unordered_map底层实现是哈希表。)
(2)二叉树的定义
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
2. 递归遍历
递归安装以下3要素来写:
a. 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
b. 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
c. 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
3. 二叉树的递归遍历(前序、中序、后序)
(1)解析
Leetcode144 二叉树的前序遍历
Leetcode145 二叉树的后序遍历
Leetcode94 二叉树的中序遍历
(2)思路
递归
(3)代码
/**
* 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) {
if(root == NULL)
return {};
vector<int> preordervec;
preordervec.push_back(root->val);
vector<int> l, r;
l = preorderTraversal(root->left);
r = preorderTraversal(root->right);
if(!l.empty())
preordervec.insert(preordervec.end(), l.begin(), l.end());
if(!r.empty())
preordervec.insert(preordervec.end(), r.begin(), r.end());
return preordervec;
}
};
//中序遍历
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
if(root == NULL)
return {};
vector<int> inordervec;
vector<int> l, r;
l = inorderTraversal(root->left);
r = inorderTraversal(root->right);
if(!l.empty())
inordervec.insert(inordervec.end(), l.begin(), l.end());
inordervec.push_back(root->val);
if(!r.empty())
inordervec.insert(inordervec.end(), r.begin(), r.end());
return inordervec;
}
};
//后序遍历
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
if(root == NULL)
return {};
vector<int> postordervec;
vector<int> l, r;
l = postorderTraversal(root->left);
r = postorderTraversal(root->right);
if(!l.empty())
postordervec.insert(postordervec.end(), l.begin(), l.end());
if(!r.empty())
postordervec.insert(postordervec.end(), r.begin(), r.end());
postordervec.push_back(root->val);
return postordervec;
}
};
(4)总结
a,vector返回空的时候采用{},而不是NULL
b.iterator insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据;由于[first,last)的区间需要有效,所以需要对l和r判断是否为空
4. 二叉树的迭代遍历(前序、中序、后序)
(1)解析
Leetcode144 二叉树的前序遍历
Leetcode145 二叉树的后序遍历
Leetcode94 二叉树的中序遍历
(2)思路
前序迭代法: 考虑用栈,根节点先入,右节点入,左节点入
中序迭代法:指针+栈,不同于先序遍历
中序迭代法:类似于前序遍历的方式
(3)代码
/**
* 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) {
stack<TreeNode*> preorderstack;
if(root == NULL)
return {};
vector<int> preordervec;
preorderstack.push(root);
while(!preorderstack.empty())
{
TreeNode* pnode = preorderstack.top();
preorderstack.pop();
preordervec.push_back(pnode->val);
if(pnode->right)
preorderstack.push(pnode->right);
if(pnode->left)
preorderstack.push(pnode->left);
}
return preordervec;
}
};
//中序遍历
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
if(root == NULL)
return {};
vector<int> inordervec;
stack<TreeNode*> inorderstack;
TreeNode* pcur = root;
while(!inorderstack.empty() || pcur != NULL)
{
if(pcur!=NULL)
{
inorderstack.push(pcur);
pcur = pcur->left;
}
else
{
TreeNode* ptmp = inorderstack.top();
inordervec.push_back(ptmp->val);
inorderstack.pop();
pcur = ptmp->right;
}
}
return inordervec;
}
};
//后序遍历
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
if(root == NULL)
return {};
vector<int> postordervec;
stack<TreeNode*> postorderstack;
postorderstack.push(root);
while(!postorderstack.empty())
{
TreeNode* pnode = postorderstack.top();
postordervec.push_back(pnode->val);
postorderstack.pop();
if(pnode->left)
postorderstack.push(pnode->left);
if(pnode->right)
postorderstack.push(pnode->right);
}
reverse(postordervec.begin(), postordervec.end());
return postordervec;
}
};
(4)总结
a.中序遍历采用指针+栈;循环条件while(!inorderstack.empty() || pcur != NULL),增加pcur != NULL的判断是针对无左指针或者所有左指针出栈结束后时inorderstack为empty时但pcur不为空时的情形;入栈采用inorderstack.push(pcur);而不得pcur->left;保证右节点的入栈
b.后续遍历,可采用先序遍历同样的方式,不同点在于先序遍历是中左右左,后序遍历先是中右左遍历,再反转之后成为左右中遍历
5. 二叉树的统一迭代遍历(前序、中序、后序)
(1)解析
Leetcode144 二叉树的前序遍历
Leetcode145 二叉树的后序遍历
Leetcode94 二叉树的中序遍历
(2)思路
使用栈的话,无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况。
那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 这种方法也可以叫做标记法。
(3)代码
/**
* 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) {}
* };
*/
//前序遍历
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> preorderstack;
if(root == NULL)
return {};
vector<int> preordervec;
preorderstack.push(root);
while(!preorderstack.empty())
{
TreeNode* pnode = preorderstack.top();
preorderstack.pop();
if(pnode != NULL)
{
if(pnode->right)
preorderstack.push(pnode->right);
if(pnode->left)
preorderstack.push(pnode->left);
preorderstack.push(pnode); // 访问过,但是还没有处理,加入空节点做为标记
preorderstack.push(NULL);
}
else
{
pnode = preorderstack.top();
preordervec.push_back(pnode->val);
preorderstack.pop();
}
}
return preordervec;
}
};
//中序遍历
public:
vector<int> inorderTraversal(TreeNode* root) {
if(root == NULL)
return {};
vector<int> inordervec;
stack<TreeNode*> inorderstack;
inorderstack.push(root);
while(!inorderstack.empty())
{
TreeNode* pnode = inorderstack.top();
inorderstack.pop();
if(pnode != NULL)
{
if(pnode->right != NULL)
inorderstack.push(pnode->right);
inorderstack.push(pnode);
inorderstack.push(NULL);
if(pnode->left != NULL)
inorderstack.push(pnode->left);
}
else
{
pnode = inorderstack.top();
inorderstack.pop();
inordervec.push_back(pnode->val);
}
}
return inordervec;
}
};
//后序遍历
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
if(root == NULL)
return {};
vector<int> postordervec;
stack<TreeNode*> postorderstack;
postorderstack.push(root);
while(!postorderstack.empty())
{
TreeNode* pnode = postorderstack.top();
postorderstack.pop();
if(pnode != NULL)
{
postorderstack.push(pnode);
postorderstack.push(NULL);
if(pnode->right != NULL)
postorderstack.push(pnode->right);
if(pnode->left != NULL)
postorderstack.push(pnode->left);
}
else
{
pnode = postorderstack.top();
postorderstack.pop();
postordervec.push_back(pnode->val);
}
}
return postordervec;
}
};
(4)总结
a.采用标志位指针,遇到标志位指针才入容器中
b.塞入左右子树时,要先判断是否为NULL