目录
0x00 先序,后序,中序遍历的相互推导:
已知先序和中序可以推导出后序,已知中序和后序可以推导出先序,但是已知先序和后序却推导不出中序。例如:有一颗树如下:
先序遍历:ABDEGCFH
中序遍历:DBGEAFHC
根据先序和中序遍历的结果,可以反推出整个树的样子,然后得出后序遍历
分析:因为先序根在前,所以A是根节点,然后结合中序遍历,得知DBGE是A的左子树,FHC是A的右子树。然后在先序中找左子树的根是谁,先序遍历中A之后是B,所以B是左子树的根。然后在先序结果中寻找FHC所对应的顺序CFH,说明C是右子树的根节点。同理,D是B的左子树,GE是B的右子树。GE在先序中E在前,说明E是根节点。这样就可以画出整个树了。
为什么知道先序和后序 却推导不出中序呢?
你可以自己尝试一番,这样你便会发现,先序根在前,后序根在后,两种遍历都只能确定谁是根,但是无法确定左子树和右子树的范围,所以无法根据先序和后序还原整个树,就无法推导出中序。
0x01满二叉树:
1.定义:
- 叶子节点在同一层
- 每个节点要么没有孩子,要么有两个孩子
2.特性:
- 每层的节点数量:2^(n-1) n为层数
- 一共有多少个节点:2^n - 1 假设每层都排满了
0x02完全二叉树:
1.定义:
满二叉树从最右边的叶子节点开始往左边删除若干个叶子节点所得到的树。满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树。即满二叉树是特殊的完全二叉树。
如图将一个完全二叉树的节点编号:
根节点编号 0 1 2 3
左孩子编号 1 3 5 7
右孩子编号 2 4 6 8
2.特性:
规律:如果完全二叉树根节点编号为n,则左孩子编号为2n+1 右孩子编号为2n+2;
如果已知左节点编号为M,则根节点编号为(M-1)/ 2.
如果已知右节点编号为M,则根节点编号为(M-2)/ 2, 由于C语言int类型除法自动取整的缘故,也可以写成(M-1)/ 2.
综上,如果完全二叉树孩子编号为M,则根节点的编号为(M-1)/2;
所以完全二叉树的结构可以用数组来描述!!!
这样一来,便节约了内存(但是内存必须连续),并提升查的效率(但是降低了增节点 和 删节点的效率!)。因为如果用类或者结构体来描述二叉树的节点类型,需要左右指针,而数组不需要左指针和右指针,所以节约了内存。而且树在查的时候,需要一个指针一个指针的寻址(O(n)),而数组查的时候可以跳跃寻址(O(1)),所以节约了时间。
0x03 完全二叉树的增删查改
1.构造函数:因为是用数组描述完全二叉树,所以只要有一个属性来记录数组首地址即可。
2.析构函数:析构函数主要是用来应对用动态内存方式创建的完全二叉树对象。
3.增:
这里暂时不实现往特定位置插入节点的功能,只实现往数组最后插入节点。这样问题就变的简单了,只要新开内存,然后把原来的数据和新插入的数据一起拷贝到新内存,然后更新pRoot,释放原有内存。
4.遍历:
先序遍历:仍然是分治思想,用递归来实现,即先找最简单的情形,然后想办法缩小问题规模。只不过这里递归函数的参数应该是数组的索引,而不是指针。
5.查:
已知某个节点地址,返回其父节点地址。
已知某个节点的地址,返回其左孩子的地址。
已知某个节点的地址,返回其右孩子的地址。
已知节点数据,返回节点地址。
以上函数实现套用公式即可
6.删:同数组的删除完全一样
思路1:
1.根据数据查地址,地址转换成下标
2.判断节点是否在树中,若不在树中,则直接返回false。如果在,则循环从要删除节点的下一个位置开始往前挪动
缺点:删除的内存没有释放。
思路2:新开内存
1.如果树中一个元素,直接释放内存,根节点地址置为空。
2.如果要删除的节点不在树中,直接返回false。如果在,则新开内存,拷贝要删除的节点之前的数据和要删除的节点之后的数据。
#include <iostream>
using namespace std;
#include "03-CompleteBinaryTree.h"
int main(){
completeBinaryTree<char> tree;
tree.pre_travel();
tree.insertNodeToTree('a');
tree.insertNodeToTree('b');
tree.insertNodeToTree('c');
tree.pre_travel();
char* p =tree.getParent(tree.getNodeAdd('a'));
cout<<*p<<endl;
tree.deleteNode('b');
tree.pre_travel();
return 0;
}
#pragma once
#include <cstring>
#include <iostream>
using namespace std;
template <class T>
class completeBinaryTree{
public:
T* pRoot;//根节点地址
size_t num;//节点数量
public:
//1.构造函数
completeBinaryTree(){ pRoot = NULL;num = 0;}
//2.析构函数
~completeBinaryTree(){if(pRoot != NULL) delete[] pRoot;}
//3.增
void insertNodeToTree(const T& data);
//4.遍历
void pre_travel();
//5.查:
T* getParent(const T* pRoot);
T* getLeftChild(const T* pRoot);
T* getRightChild(const T* pRoot);
T* getNodeAdd(const T& data);
//6.删除
bool deleteNode(const T& data);
private:
void _pre_travel(int);
//5.转换:
int _add2idx(const T* pRoot);
T* _id2add(int id){
if(id < 0 || id > num){
return NULL;
}
return pRoot+id;
}
};
//1.增
template <class T>
void completeBinaryTree<T>::insertNodeToTree(const T& data){
T* pNew = new T[num+1];
if(pRoot){
memcpy(pNew,pRoot,sizeof(T)*num);
delete[] pRoot;
}
pRoot = pNew;
pRoot[num++] = data;
return;
}
//2根据地址返回下标
template<class T>
int completeBinaryTree<T>::_add2idx(const T* pRoot){
int idx = 0;
T* pTemp = this->pRoot;
while(idx < num){
if(pTemp == pRoot) return idx;
pTemp++;
idx++;
}
return -1;
}
//根据下标
//3.先序遍历
template <class T>
void completeBinaryTree<T>::pre_travel(){
if(pRoot){
_pre_travel(_add2idx(pRoot));
}
return;
}
//4.查
template <class T>
void completeBinaryTree<T>::_pre_travel(int n){
if(n < 0 ||( n >= num)) return;
int left = 2*n+1;
int right = 2*n+2;
cout<<pRoot[n];
_pre_travel(left);
_pre_travel(right);
return;
}
template<class T>
T* completeBinaryTree<T>::getParent(const T* pRoot){
int idx = _add2idx(pRoot);
if(idx == -1) return NULL;
return _id2add((idx-1)/2);
}
template<class T>
T* completeBinaryTree<T>::getLeftChild(const T* pRoot){
int idx = _add2idx(pRoot);
if(idx == -1) return NULL;
return _id2add(2*idx + 1);
}
template<class T>
T* completeBinaryTree<T>::getRightChild(const T* pRoot){
int idx = _add2idx(pRoot);
if(idx == -1) return NULL;
return _id2add(2*idx+2);
}
template<class T>
T* completeBinaryTree<T>::getNodeAdd(const T& data){
for(size_t i = 0;i<num;i++){
if(data == pRoot[i]) return _id2add(i);
}
return NULL;
}
//删除
template<class T>
bool completeBinaryTree<T>::deleteNode(const T& data){
int idx = _add2idx(getNodeAdd(data));
if(-1 == idx) return false;
for(int i = idx + 1;i<num;i++){
pRoot[i-1] = pRoot[i];
}
num--;
return true;
}