二叉树理论基础
深度:层数
满二叉树:全满,有2^(k-1)个节点。
完全二叉树:只有最下面一层没填慢,而且那一层从左往右有节点的地方连续。
二叉搜索树:
左子树上节点均小于根节点,右子树均大于。
平衡二叉搜索树:
AVL,左右深度差小于等于1.
存储方式
链式存储:左右两指针指向下一节点。
顺序存储:
第i个元素的左孩子是2i+1,右孩子2i+2.
遍历方式
深度优先遍历(前中后序)和广度优先遍历
构造
class Node{
private:
int val;
Node* left;
Node* right;
public:
Node(int x):val(x),left(NULL),right(NULL){};
Node(int x,Node* l,Node* r):val(x),left(l),right(r){};
二叉树递归
把一个问题拆分为和它相似的子问题,就是递归的思想。
前序遍历:中左右
有一个函数可以实现这个树的遍历,先记录中节点,再记录它的左子树上的点,再记录它的右子树上的点。
而它的左子树也可以用这个函数来解决,只不过根节点变了而已。
而递归的边界(归)也就是当当前根节点为空的时候
void inorder(TreeNode* root,vector<int> &ans){
if(root==NULL)
return;
ans.push_back(root->val);
inorder(root->left,ans);
inorder(root->right,ans);
}
前中后都一样,只不过改写记录根节点的位置。
二叉树迭代
二叉树最后为空的节点先return,这和栈中的后进先出一样,所以可以用栈来实现前中后。
前序:
定义一个stack<TreeNode* > nd;
然后先将根节点放进去,再弹出,如果不为空的话就记录,再先后将它的右子树和左子树加入(因为要先处理左子树)。
后序:
左右中,和前序中的位置刚好反着,如果在前序中先入左,变成中右左,然后reverse一下,就能得到后序。
中序:
先访问的中间结点,但要先处理左边节点,两者不统一,使得它无法像前序一样。
那么在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ans;
stack<TreeNode*> nd;
TreeNode* cur=root;
nd.push(cur);
while(!nd.empty()){
cur=nd.top();
while(cur!=NULL){
nd.push(cur);
cur=cur->left;
}
if(cur==NULL){
nd.pop();
cur=nd.top();
ans.push_back(cur->val);
nd.pop();
nd.push(cur->right);
}
}
return ans;
一开始写的时候我想的是即使是空指针也放入,然后再弹出就可以了,但是这样会在一些地方不断地进入根节点,然后向左边循环。
所以应该在空指针时直接处理stack中的节点。
vector<int> ans;
stack<TreeNode*> nd;
TreeNode* cur=root;
while(!nd.empty()||cur!=NULL){
while(cur!=NULL){
nd.push(cur);
cur=cur->left;
}
if(cur==NULL){
cur=nd.top();
ans.push_back(cur->val);
nd.pop();
cur=cur->right;//这样在cur==null时就能直接处理stack中的元素。
}
}
return ans;
统一写法
不将空指针的结点加进入,但是用空指针将访问过但还没处理的节点标记出来,
这样到叶子结点时碰到NULL就先弹出NULL,然后cur=top(),处理根节点,再弹出,往回处理。
后序:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ans;
stack<TreeNode*> nd;
if(root!=NULL)
nd.push(root);
while(!nd.empty()){
TreeNode* cur=nd.top();
if(cur!=NULL){
nd.push(NULL);
if(cur->right!=NULL)//不加入空结点
nd.push(cur->right);
if(cur->left!=NULL)
nd.push(cur->left);
}
else{
nd.pop();
cur=nd.top();
ans.push_back(cur->val);
nd.pop();
}
}
return ans;
层序遍历
也叫广度优先搜索,用队列来解决。
queue<Node*> dl;
if(root!=NULL){
dl.push(root);
}
先将根结点加入队列中,
从队列中弹出队首(根节点),分别将其左右节点加入到队列末尾。
Node* cur=dl.front();
dl.pop();
if(cur->left!=NULL) dl.push(cur->left);
....
那可以在每一层开始前先用int size记录一下队列中的节点数,以防把下一层的节点也搞混。